From fb807250b1b652bacff45ad13bf77ea79fc07b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Mon, 15 Jul 2024 17:14:03 +0200 Subject: [PATCH] Fixes #25144: Add a command to help splitting virtualhosts --- rudder-server/SOURCES/Makefile | 1 + rudder-server/SOURCES/split-vhost | 211 ++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100755 rudder-server/SOURCES/split-vhost diff --git a/rudder-server/SOURCES/Makefile b/rudder-server/SOURCES/Makefile index 057ad5dda..02791566f 100644 --- a/rudder-server/SOURCES/Makefile +++ b/rudder-server/SOURCES/Makefile @@ -197,6 +197,7 @@ install: build rudder-server-version initial-promises initial-ncf install -m 755 rudder-metrics-reporting $(DESTDIR)/opt/rudder/bin/ install -m 755 rudder-reload-cf-serverd $(DESTDIR)/opt/rudder/bin/ install -m 755 rudder_synchronize.py ${DESTDIR}/opt/rudder/bin/ + install -m 755 split-vhost ${DESTDIR}/opt/rudder/bin/ install -m 644 $(WEB_RESOURCES)/demo-rudder-users.xml $(DESTDIR)/opt/rudder/etc/rudder-users.xml install -m 644 $(CORE_RESOURCES)/ldap/bootstrap.ldif $(DESTDIR)/opt/rudder/share/ diff --git a/rudder-server/SOURCES/split-vhost b/rudder-server/SOURCES/split-vhost new file mode 100755 index 000000000..f022074c3 --- /dev/null +++ b/rudder-server/SOURCES/split-vhost @@ -0,0 +1,211 @@ +#!/bin/bash + +usage() { + echo "Usage: split-vhost.sh [-b|-c -k ] [-l] " + echo " : virtualhost definition to use, must be one of:" + echo " , :, , :" + echo " - if is not provided, it defaults to 443" + echo " - if is provided, the IP address must be assigned to this host" + echo " - if is an IPv6, it must be surrounded by square brackets (eg: [2001::1])" + echo " - if is provided, dns entry must already point to this host" + echo " -b: use certbot to issue certificate (only available if is of the form )" + 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 and :)" + echo " The certificate will only be used for user (browser) communication, since Rudder provides" + echo " its own certificate for agent communication" + # -t 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 beacause 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 splitted 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 /aListen '${AGENT_HOST}:${AGENT_PORT} "${APACHE_CONF}" + sed -i '/# Listen /aListen '${USER_HOST}:${USER_PORT} "${APACHE_CONF}" +else + if [ "${AGENT_PORT}" != "443" ]; then + sed -i '/# Listen /aListen '${AGENT_PORT} "${APACHE_CONF}" + fi + if [ "${USER_PORT}" != "443" ]; then + sed -i '/# Listen /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 \)/#\1/' "${APACHE_CONF}" +else + AGENT_IP="*" +fi +if [ "${USER_HOST_IS_IP}" = "yes" ]; then + USER_IP="${USER_HOST}" + sed -i 's/.*\(ServerName \)/#\1/' "${APACHE_CONF}" +else + USER_IP="*" +fi +sed -i '/#AGENT_VHOST/,/#USER_VHOST/s///' "${APACHE_CONF}" +sed -i '/#USER_VHOST/,$s///' "${APACHE_CONF}" + +# Generic variable replacement +sed -i 's//'${AGENT_HOST}'/' "${APACHE_CONF}" +sed -i 's//'${USER_HOST}'/' "${APACHE_CONF}" +sed -i 's||'${CERT_PATH}'|' "${APACHE_CONF}" +sed -i 's||'${KEY_PATH}'|' "${APACHE_CONF}" + + +# End +##### + +if [ "${TEST}" = "" ]; then + systemctl start ${SERVICE} + rudder agent enable +fi