#!/usr/bin/bash

usage() {
  echo "Usage: split-vhost.sh [-b|-c <certificate_path> -k <key_path>] [-l] <agent_vhost> <user_vhost>"
  echo "  <xx_vhost>: virtualhost definition to use, must be one of:"
  echo "     <ip>, <ip>:<port>, <hostname>, <hostname>:<port>"
  echo "     - if <port> is not provided, it defaults to 443"
  echo "     - if <ip> is provided, the IP address must be assigned to this host"
  echo "     - if <ip> is an IPv6, it must be surrounded by square brackets (eg: [2001::1])"
  echo "     - if <hostname> is provided, dns entry must already point to this host"
  echo "  -b: use certbot to issue certificate (only available if <vhost2> is of the form <hostname>)"
  echo "  -c: provide an already issued certificate path (full certificate chain is accepted)"
  echo "  -k: provide an already issued certificate key path"
  echo "  -l: force apache to listen only on provided IP (only available for vhost of the form <ip> and <ip>:<port>)"
  echo "  The certificate will only be used for user (browser) communication, since Rudder provides"
  echo "  its own certificate for agent communication"
  #  -t <test_file> is undocumented, it uses test_file to test configuration replacement but does not run other commands
  # CERTBOT_OPTS is available as an environment variable ex
  # CERTBOT_OPTS="--register-unsafely-without-email --agree-tos"
}

CERT_BOT="no"
CERT_PATH=""
KEY_PATH=""
STRICT_LISTEN="no"
TEST=""
ORIGINAL_CONF="/opt/rudder/share/apache.conf"

# Detect one of the main Server base OS : RHEL, SUSE, Debian
if command -vp apt-get > /dev/null; then
  APACHE_CONF="/etc/apache2/sites-enabled/rudder.conf"
  SERVICE="apache2"
elif command -vp zypper > /dev/null; then
  APACHE_CONF="/etc/apache2/vhosts.d/rudder.conf"
  SERVICE="apache2"
elif command -vp dnf > /dev/null; then
  APACHE_CONF="/etc/httpd/conf.d/rudder.conf"
  SERVICE="httpd"
else
  echo "Unknown base OS, aborting"
  exit 1
fi

# Parameters
############
while getopts "bc:k:lt:" opt; do
  case ${opt} in
    b)
      CERT_BOT="yes"
      ;;
    c)
      CERT_PATH="${OPTARG}"
      ;;
    k)
      KEY_PATH="${OPTARG}"
      ;;
    l)
      STRICT_LISTEN="yes"
      ;;
    t)
      TEST="yes"
      APACHE_CONF="${OPTARG}"
      ;;
    h)
      usage
      exit 1
      ;;
  esac
done

shift $(($OPTIND-1))
if [ $# -ne 2 ]
then
  usage
  echo "ERROR: provide exactly 2 vhost"
  exit 1
fi

AGENT_HOST=${1%:[[:digit:]]*}
AGENT_PORT=$(echo "$1" | perl -pe 's/^(.*?)(?::(\d+))?$/$2/')
USER_HOST=${2%:[[:digit:]]*}
USER_PORT=$(echo "$2" | perl -pe 's/^(.*?)(?::(\d+))?$/$2/')

# Sanity checks
###############

is_ip() {
  RE='((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))'
  echo "${1//[][]}" | grep -qE "${RE}"
}

# -b -c -k compatibility
if [ "${CERT_BOT}" = "yes" ]
then
  if [ "${CERT_PATH}" != "" ] || [ "${KEY_PATH}" != "" ]; then
    echo "Option -b is not compatible with -c and -k"
    exit 1
  fi
else
  if [ "${CERT_PATH}" = "" ] || [ "${KEY_PATH}" = "" ]; then
    echo "You must provide both a Certificate path (-c) and a key path (-k)"
    exit 1
  fi
fi

# -l means IP only
if is_ip "${AGENT_HOST}"; then
  AGENT_HOST_IS_IP=yes
elif [ "${STRICT_LISTEN}" = "yes" ]; then
  echo "Option -l only works IP based vhost split"
  exit 1
fi

if is_ip "${USER_HOST}"; then
  USER_HOST_IS_IP=yes
elif [ "${STRICT_LISTEN}" = "yes" ]; then
  echo "Option -l only works IP based vhost split"
  exit 1
fi

echo "Creating a backup of ${APACHE_CONF} in ${APACHE_CONF}.backup"
cp "${APACHE_CONF}" "${APACHE_CONF}.backup"

if ! grep -q "^#SINGLE_VHOST_START" "${APACHE_CONF}"
then
  echo "Your apache configuration ${APACHE_CONF} cannot be split, either because it is too old or because it has already been customized"
  echo "Continuing will reset your current configuration to original one"
  echo -n "Please type 'yes' to start again with a default configuration: "
  read a
  if [ "${a}" != "yes" ]; then
    echo "Aborting"
    exit 1
  fi
  cp "${ORIGINAL_CONF}" "${APACHE_CONF}"
fi

# Preparation
#############

if [ "${TEST}" = "" ]
then
  rudder agent disable -s
  systemctl stop ${SERVICE}
fi

# run certbot if required
#########################

if [ "${CERT_BOT}" = "yes" ]
then
  if [ "${TEST}" = "" ]; then
    apt install -y certbot python3-certbot-apache
    certbot certonly --standalone ${CERTBOT_OPTS} -d "${USER_HOST}"
  fi
  CERT_PATH="/etc/letsencrypt/live/${USER_HOST}/fullchain.pem"
  KEY_PATH="/etc/letsencrypt/live/${USER_HOST}/privkey.pem"
fi

# Replacements
##############

# comment original vhost and uncomment split one
sed -i '/#SINGLE_VHOST_START/,/#SINGLE_VHOST_END/s/^/# /' "${APACHE_CONF}"
sed -i '/#MULTI_VHOST_START/,/#MULTI_VHOST_END/s/^#\( \|$\)//' "${APACHE_CONF}"

# Append Listen directives
AGENT_PORT=${AGENT_PORT:-443}
USER_PORT=${USER_PORT:-443}
if [ "${STRICT_LISTEN}" = "yes" ]
then
  sed -i '/# Listen <PORT>/aListen '${AGENT_HOST}:${AGENT_PORT} "${APACHE_CONF}"
  sed -i '/# Listen <PORT>/aListen '${USER_HOST}:${USER_PORT} "${APACHE_CONF}"
else
  if [ "${AGENT_PORT}" != "443" ]; then
    sed -i '/# Listen <PORT>/aListen '${AGENT_PORT} "${APACHE_CONF}"
  fi
  if [ "${USER_PORT}" != "443" ]; then
    sed -i '/# Listen <PORT>/aListen '${USER_PORT} "${APACHE_CONF}"
  fi
fi

# Edit Virtualhost directives
if [ "${AGENT_HOST_IS_IP}" = "yes" ]; then
  AGENT_IP="${AGENT_HOST}"
  sed -i 's/.*\(ServerName <AGENT_HOST>\)/#\1/' "${APACHE_CONF}"
else
  AGENT_IP="*"
fi
if [ "${USER_HOST_IS_IP}" = "yes" ]; then
  USER_IP="${USER_HOST}"
  sed -i 's/.*\(ServerName <USER_HOST>\)/#\1/' "${APACHE_CONF}"
else
  USER_IP="*"
fi
sed -i '/#AGENT_VHOST/,/#USER_VHOST/s/<VirtualHost .*>/<VirtualHost '${AGENT_IP}:${AGENT_PORT}'>/' "${APACHE_CONF}"
sed -i '/#USER_VHOST/,$s/<VirtualHost .*>/<VirtualHost '${USER_IP}:${USER_PORT}'>/' "${APACHE_CONF}"

# Generic variable replacement
sed -i 's/<AGENT_HOST>/'${AGENT_HOST}'/' "${APACHE_CONF}"
sed -i 's/<USER_HOST>/'${USER_HOST}'/' "${APACHE_CONF}"
sed -i 's|<CERT_PATH>|'${CERT_PATH}'|' "${APACHE_CONF}"
sed -i 's|<KEY_PATH>|'${KEY_PATH}'|' "${APACHE_CONF}"


# End
#####

if [ "${TEST}" = "" ]; then
  systemctl start ${SERVICE}
  rudder agent enable
fi
