diff --git a/openvpn-install.sh b/openvpn-install.sh index 42a7a10..0c75bb2 100755 --- a/openvpn-install.sh +++ b/openvpn-install.sh @@ -3071,6 +3071,15 @@ function installOpenVPN() { # Select the firewall backend before installing dependencies so native # firewalld and nftables systems do not need the iptables package. FIREWALL_BACKEND=$(detect_firewall_backend) + FIREWALLD_PORT_ZONE="" + if [[ $FIREWALL_BACKEND == 'firewalld' ]]; then + if [[ -n $NIC ]]; then + FIREWALLD_PORT_ZONE=$(firewall-cmd --get-zone-of-interface="$NIC" 2>/dev/null || true) + fi + if [[ -z $FIREWALLD_PORT_ZONE || $FIREWALLD_PORT_ZONE == 'no zone' ]]; then + FIREWALLD_PORT_ZONE=$(firewall-cmd --get-default-zone) + fi + fi # If OpenVPN isn't installed yet, install it. This script is more-or-less # idempotent on multiple runs, but will only install OpenVPN from upstream @@ -3525,6 +3534,7 @@ verb 3" # Record installer-owned policy so firewall rules can be removed exactly. { echo "FIREWALL_BACKEND=$FIREWALL_BACKEND" + echo "FIREWALLD_PORT_ZONE=$FIREWALLD_PORT_ZONE" echo "ROUTE_INTERNET=$ROUTE_INTERNET" echo "CLIENT_TO_CLIENT=$CLIENT_TO_CLIENT" echo "LOCAL_NETWORKS=$LOCAL_NETWORKS" @@ -3651,7 +3661,7 @@ verb 3" # destination rules to forwarded traffic; zone rich rules alone only # govern traffic addressed to the server. log_info "firewalld detected, using firewall-cmd..." - run_cmd_fatal "Adding OpenVPN port to firewalld" firewall-cmd --permanent --add-port="$PORT/$PROTOCOL" + run_cmd_fatal "Adding OpenVPN port to firewalld zone $FIREWALLD_PORT_ZONE" firewall-cmd --permanent --zone="$FIREWALLD_PORT_ZONE" --add-port="$PORT/$PROTOCOL" run_cmd_fatal "Creating OpenVPN firewalld zone" firewall-cmd --permanent --new-zone=openvpn-install run_cmd_fatal "Creating OpenVPN firewalld policy" firewall-cmd --permanent --new-policy=openvpn-egress run_cmd_fatal "Setting OpenVPN policy ingress" firewall-cmd --permanent --policy=openvpn-egress --add-ingress-zone=openvpn-install @@ -5045,6 +5055,7 @@ function removeOpenVPN() { if [[ -f $install_config ]]; then has_policy_manifest=y FIREWALL_BACKEND=$(grep '^FIREWALL_BACKEND=' "$install_config" | cut -d= -f2-) + FIREWALLD_PORT_ZONE=$(grep '^FIREWALLD_PORT_ZONE=' "$install_config" | cut -d= -f2- || true) ROUTE_INTERNET=$(grep '^ROUTE_INTERNET=' "$install_config" | cut -d= -f2-) CLIENT_TO_CLIENT=$(grep '^CLIENT_TO_CLIENT=' "$install_config" | cut -d= -f2-) LOCAL_NETWORKS=$(grep '^LOCAL_NETWORKS=' "$install_config" | cut -d= -f2-) @@ -5064,7 +5075,14 @@ function removeOpenVPN() { # Remove firewall rules log_info "Removing firewall rules..." if systemctl is-active --quiet firewalld && { [[ $has_policy_manifest == 'y' && $FIREWALL_BACKEND == 'firewalld' ]] || { [[ $has_policy_manifest == 'n' ]] && firewall-cmd --list-ports | grep -q "$PORT/$PROTOCOL_BASE"; }; }; then - run_cmd "Removing OpenVPN port from firewalld" firewall-cmd --permanent --remove-port="$PORT/$PROTOCOL_BASE" + if [[ $has_policy_manifest == 'y' ]]; then + if [[ -z $FIREWALLD_PORT_ZONE ]]; then + FIREWALLD_PORT_ZONE=$(firewall-cmd --get-default-zone) + fi + run_cmd "Removing OpenVPN port from firewalld zone $FIREWALLD_PORT_ZONE" firewall-cmd --permanent --zone="$FIREWALLD_PORT_ZONE" --remove-port="$PORT/$PROTOCOL_BASE" + else + run_cmd "Removing OpenVPN port from firewalld" firewall-cmd --permanent --remove-port="$PORT/$PROTOCOL_BASE" + fi if [[ $has_policy_manifest == 'y' ]]; then firewall-cmd --permanent --delete-policy=openvpn-egress 2>/dev/null || true firewall-cmd --permanent --delete-zone=openvpn-install 2>/dev/null || true diff --git a/test/server-entrypoint.sh b/test/server-entrypoint.sh index af0f893..ddcd6ee 100755 --- a/test/server-entrypoint.sh +++ b/test/server-entrypoint.sh @@ -6,6 +6,15 @@ echo "=== OpenVPN Server Container ===" /opt/test/local-network-detection.sh /opt/openvpn-install.sh /opt/test/interactive-install-flow.sh /opt/openvpn-install.sh +# Verify that the installer uses the zone bound to the public interface, not +# firewalld's default zone. +if systemctl is-active --quiet firewalld; then + FIREWALLD_TEST_INTERFACE=$(ip -4 route ls | awk '/^default / { for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }') + firewall-cmd --set-default-zone=public >/dev/null + firewall-cmd --permanent --zone=external --change-interface="$FIREWALLD_TEST_INTERFACE" >/dev/null + firewall-cmd --reload >/dev/null +fi + # Create TUN device if it doesn't exist if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net @@ -301,6 +310,10 @@ for setting in "ROUTE_INTERNET=$ROUTE_INTERNET" "CLIENT_TO_CLIENT=$CLIENT_TO_CLI exit 1 } done +if systemctl is-active --quiet firewalld && ! grep -Fxq 'FIREWALLD_PORT_ZONE=external' /etc/openvpn/server/openvpn-install.conf; then + echo "FAIL: Policy manifest is missing the public interface's firewalld zone" + exit 1 +fi echo "PASS: Access policy configuration is correct" @@ -831,11 +844,17 @@ if systemctl is-active --quiet firewalld; then echo "FAIL: firewalld zone-wide masquerade should not be enabled" exit 1 fi - if firewall-cmd --list-ports | grep -q "1194/udp"; then - echo "PASS: OpenVPN port is open in firewalld" + FIREWALLD_PORT_ZONE=$(grep '^FIREWALLD_PORT_ZONE=' /etc/openvpn/server/openvpn-install.conf | cut -d= -f2-) + if firewall-cmd --zone="$FIREWALLD_PORT_ZONE" --query-port="1194/udp"; then + echo "PASS: OpenVPN port is open in the public interface's firewalld zone" else - echo "FAIL: OpenVPN port not found in firewalld" - firewall-cmd --list-ports + echo "FAIL: OpenVPN port not found in firewalld zone $FIREWALLD_PORT_ZONE" + firewall-cmd --zone="$FIREWALLD_PORT_ZONE" --list-ports + exit 1 + fi + FIREWALLD_DEFAULT_ZONE=$(firewall-cmd --get-default-zone) + if [ "$FIREWALLD_DEFAULT_ZONE" != "$FIREWALLD_PORT_ZONE" ] && firewall-cmd --zone="$FIREWALLD_DEFAULT_ZONE" --query-port="1194/udp"; then + echo "FAIL: OpenVPN port was also added to firewalld's default zone" exit 1 fi elif systemctl is-active --quiet nftables; then