Skip to content

Commit

Permalink
Fixes #25144: Add a command to help splitting virtualhosts
Browse files Browse the repository at this point in the history
  • Loading branch information
peckpeck committed Jul 15, 2024
1 parent 2590cf2 commit fb80725
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 0 deletions.
1 change: 1 addition & 0 deletions rudder-server/SOURCES/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
211 changes: 211 additions & 0 deletions rudder-server/SOURCES/split-vhost
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#!/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 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 <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

0 comments on commit fb80725

Please sign in to comment.