Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #25144: Add a command to help splitting virtualhosts #2886

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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