mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-14 00:02:42 +01:00
feat: add native firewalld support (#1388)
## Summary - Add native firewalld support for RHEL/Fedora/CentOS systems - When firewalld is active, use `firewall-cmd --permanent` instead of raw iptables - Rules persist across `firewall-cmd --reload` - Fall back to iptables when firewalld is not active - Add `After=firewalld.service` to iptables systemd unit for safety ## Changes **Install:** Detect firewalld, use `firewall-cmd` to add port, masquerade, and rich rules. Fall back to iptables if inactive. **Uninstall:** Detect which method was used and clean up accordingly. **Tests:** Add `fedora-42-firewalld` CI test with firewalld enabled. --- Closes https://github.com/angristan/openvpn-install/issues/356 Closes https://github.com/angristan/openvpn-install/pull/1200
This commit is contained in:
10
.github/workflows/docker-test.yml
vendored
10
.github/workflows/docker-test.yml
vendored
@@ -89,6 +89,15 @@ jobs:
|
||||
name: tls-auth
|
||||
sig: "3"
|
||||
key_file: tls-auth.key
|
||||
# Test firewalld support on Fedora
|
||||
- os:
|
||||
name: fedora-42-firewalld
|
||||
image: fedora:42
|
||||
enable_firewalld: true
|
||||
tls:
|
||||
name: tls-crypt-v2
|
||||
sig: "1"
|
||||
key_file: tls-crypt-v2.key
|
||||
|
||||
name: ${{ matrix.os.name }}
|
||||
steps:
|
||||
@@ -103,6 +112,7 @@ jobs:
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg BASE_IMAGE=${{ matrix.os.image }} \
|
||||
--build-arg ENABLE_FIREWALLD=${{ matrix.os.enable_firewalld && 'y' || 'n' }} \
|
||||
-t openvpn-server \
|
||||
-f test/Dockerfile.server .
|
||||
|
||||
|
||||
4
FAQ.md
4
FAQ.md
@@ -87,9 +87,9 @@ If your client is <2.3.3, remove `tls-version-min 1.2` from your `/etc/openvpn/s
|
||||
|
||||
---
|
||||
|
||||
**Q:** What syctl and iptables changes are made by the script?
|
||||
**Q:** What sysctl and firewall changes are made by the script?
|
||||
|
||||
**A:** Iptables rules are saved at `/etc/iptables/add-openvpn-rules.sh` and `/etc/iptables/rm-openvpn-rules.sh`. They are managed by the service `/etc/systemd/system/iptables-openvpn.service`
|
||||
**A:** If firewalld is active, the script uses `firewall-cmd --permanent` to configure port, masquerade, and rich rules. Otherwise, iptables rules are saved at `/etc/iptables/add-openvpn-rules.sh` and `/etc/iptables/rm-openvpn-rules.sh`, managed by `/etc/systemd/system/iptables-openvpn.service`.
|
||||
|
||||
Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
|
||||
|
||||
|
||||
@@ -149,8 +149,8 @@ export CLIENTNUMBER="1" # Revokes the first client in the list
|
||||
- Installs and configures a ready-to-use OpenVPN server
|
||||
- Certificate renewal for both client and server certificates
|
||||
- Uses [official OpenVPN repositories](https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos) when possible for the latest stable releases
|
||||
- Iptables rules and forwarding managed in a seamless way
|
||||
- If needed, the script can cleanly remove OpenVPN, including configuration and iptables rules
|
||||
- Firewall rules and forwarding managed seamlessly (native firewalld support, iptables fallback)
|
||||
- If needed, the script can cleanly remove OpenVPN, including configuration and firewall rules
|
||||
- Customisable encryption settings, enhanced default settings (see [Security and Encryption](#security-and-encryption) below)
|
||||
- OpenVPN 2.4 features, mainly encryption improvements (see [Security and Encryption](#security-and-encryption) below)
|
||||
- Variety of DNS resolvers to be pushed to the clients
|
||||
|
||||
@@ -1419,48 +1419,66 @@ verb 3" >>/etc/openvpn/server/server.conf
|
||||
installUnbound
|
||||
fi
|
||||
|
||||
# Add iptables rules in two scripts
|
||||
# Configure firewall rules
|
||||
log_info "Configuring firewall rules..."
|
||||
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
||||
|
||||
# Script to add rules
|
||||
echo "#!/bin/sh
|
||||
if systemctl is-active --quiet firewalld; then
|
||||
# Use firewalld native commands for systems with firewalld active
|
||||
log_info "firewalld detected, using firewall-cmd..."
|
||||
run_cmd "Adding OpenVPN port to firewalld" firewall-cmd --permanent --add-port="$PORT/$PROTOCOL"
|
||||
run_cmd "Adding masquerade to firewalld" firewall-cmd --permanent --add-masquerade
|
||||
|
||||
# Add rich rules for VPN traffic (source-based rules work reliably with dynamic tun0 interface)
|
||||
run_cmd "Adding VPN subnet rule" firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.8.0.0/24" accept'
|
||||
|
||||
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||
run_cmd "Adding IPv6 source rule" firewall-cmd --permanent --add-rich-rule='rule family="ipv6" source address="fd42:42:42:42::/112" accept'
|
||||
fi
|
||||
|
||||
run_cmd "Reloading firewalld" firewall-cmd --reload
|
||||
else
|
||||
# Use iptables for systems without firewalld
|
||||
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
||||
|
||||
# Script to add rules
|
||||
echo "#!/bin/sh
|
||||
iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -o $NIC -j MASQUERADE
|
||||
iptables -I INPUT 1 -i tun0 -j ACCEPT
|
||||
iptables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
|
||||
iptables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
|
||||
iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/add-openvpn-rules.sh
|
||||
|
||||
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||
echo "ip6tables -t nat -I POSTROUTING 1 -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
|
||||
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||
echo "ip6tables -t nat -I POSTROUTING 1 -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
|
||||
ip6tables -I INPUT 1 -i tun0 -j ACCEPT
|
||||
ip6tables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
|
||||
ip6tables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
|
||||
ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
|
||||
fi
|
||||
fi
|
||||
|
||||
# Script to remove rules
|
||||
echo "#!/bin/sh
|
||||
# Script to remove rules
|
||||
echo "#!/bin/sh
|
||||
iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o $NIC -j MASQUERADE
|
||||
iptables -D INPUT -i tun0 -j ACCEPT
|
||||
iptables -D FORWARD -i $NIC -o tun0 -j ACCEPT
|
||||
iptables -D FORWARD -i tun0 -o $NIC -j ACCEPT
|
||||
iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/rm-openvpn-rules.sh
|
||||
|
||||
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||
echo "ip6tables -t nat -D POSTROUTING -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
|
||||
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||
echo "ip6tables -t nat -D POSTROUTING -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
|
||||
ip6tables -D INPUT -i tun0 -j ACCEPT
|
||||
ip6tables -D FORWARD -i $NIC -o tun0 -j ACCEPT
|
||||
ip6tables -D FORWARD -i tun0 -o $NIC -j ACCEPT
|
||||
ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
|
||||
fi
|
||||
fi
|
||||
|
||||
run_cmd "Making add-openvpn-rules.sh executable" chmod +x /etc/iptables/add-openvpn-rules.sh
|
||||
run_cmd "Making rm-openvpn-rules.sh executable" chmod +x /etc/iptables/rm-openvpn-rules.sh
|
||||
run_cmd "Making add-openvpn-rules.sh executable" chmod +x /etc/iptables/add-openvpn-rules.sh
|
||||
run_cmd "Making rm-openvpn-rules.sh executable" chmod +x /etc/iptables/rm-openvpn-rules.sh
|
||||
|
||||
# Handle the rules via a systemd script
|
||||
echo "[Unit]
|
||||
# Handle the rules via a systemd script
|
||||
echo "[Unit]
|
||||
Description=iptables rules for OpenVPN
|
||||
After=firewalld.service
|
||||
Before=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
@@ -1473,10 +1491,11 @@ RemainAfterExit=yes
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
|
||||
|
||||
# Enable service and apply rules
|
||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||
run_cmd "Enabling iptables service" systemctl enable iptables-openvpn
|
||||
run_cmd "Starting iptables service" systemctl start iptables-openvpn
|
||||
# Enable service and apply rules
|
||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||
run_cmd "Enabling iptables service" systemctl enable iptables-openvpn
|
||||
run_cmd "Starting iptables service" systemctl start iptables-openvpn
|
||||
fi
|
||||
|
||||
# If the server is behind a NAT, use the correct IP address for the clients to connect to
|
||||
if [[ $ENDPOINT != "" ]]; then
|
||||
@@ -2057,15 +2076,24 @@ function removeOpenVPN() {
|
||||
# Remove customised service
|
||||
run_cmd "Removing service file" rm -f /etc/systemd/system/openvpn-server@.service
|
||||
|
||||
# Remove the iptables rules related to the script
|
||||
log_info "Removing iptables rules..."
|
||||
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
||||
# Cleanup
|
||||
run_cmd "Disabling iptables service" systemctl disable iptables-openvpn
|
||||
run_cmd "Removing iptables service file" rm /etc/systemd/system/iptables-openvpn.service
|
||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||
run_cmd "Removing iptables add script" rm /etc/iptables/add-openvpn-rules.sh
|
||||
run_cmd "Removing iptables rm script" rm /etc/iptables/rm-openvpn-rules.sh
|
||||
# Remove firewall rules
|
||||
log_info "Removing firewall rules..."
|
||||
if systemctl is-active --quiet firewalld && firewall-cmd --list-ports | grep -q "$PORT/$PROTOCOL"; then
|
||||
# firewalld was used
|
||||
run_cmd "Removing OpenVPN port from firewalld" firewall-cmd --permanent --remove-port="$PORT/$PROTOCOL"
|
||||
run_cmd "Removing masquerade from firewalld" firewall-cmd --permanent --remove-masquerade
|
||||
run_cmd "Removing VPN subnet rule" firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="10.8.0.0/24" accept' 2>/dev/null || true
|
||||
run_cmd "Removing IPv6 source rule" firewall-cmd --permanent --remove-rich-rule='rule family="ipv6" source address="fd42:42:42:42::/112" accept' 2>/dev/null || true
|
||||
run_cmd "Reloading firewalld" firewall-cmd --reload
|
||||
elif [[ -f /etc/systemd/system/iptables-openvpn.service ]]; then
|
||||
# iptables was used
|
||||
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
||||
run_cmd "Disabling iptables service" systemctl disable iptables-openvpn
|
||||
run_cmd "Removing iptables service file" rm /etc/systemd/system/iptables-openvpn.service
|
||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||
run_cmd "Removing iptables add script" rm -f /etc/iptables/add-openvpn-rules.sh
|
||||
run_cmd "Removing iptables rm script" rm -f /etc/iptables/rm-openvpn-rules.sh
|
||||
fi
|
||||
|
||||
# SELinux
|
||||
if hash sestatus 2>/dev/null; then
|
||||
|
||||
@@ -5,7 +5,10 @@ ARG BASE_IMAGE=ubuntu:24.04
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ARG BASE_IMAGE
|
||||
# Set to "y" to install and enable firewalld for testing
|
||||
ARG ENABLE_FIREWALLD=n
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV ENABLE_FIREWALLD=${ENABLE_FIREWALLD}
|
||||
|
||||
# Install basic dependencies based on the OS
|
||||
# dnsutils/bind-utils provides dig for DNS testing with Unbound
|
||||
@@ -16,10 +19,12 @@ RUN if command -v apt-get >/dev/null; then \
|
||||
elif command -v dnf >/dev/null; then \
|
||||
dnf install -y --allowerasing \
|
||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then dnf install -y firewalld; fi \
|
||||
&& dnf clean all; \
|
||||
elif command -v yum >/dev/null; then \
|
||||
yum install -y \
|
||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then yum install -y firewalld; fi \
|
||||
&& yum clean all; \
|
||||
elif command -v pacman >/dev/null; then \
|
||||
pacman -Syu --noconfirm \
|
||||
@@ -31,6 +36,11 @@ RUN if command -v apt-get >/dev/null; then \
|
||||
&& zypper clean -a; \
|
||||
fi
|
||||
|
||||
# Enable firewalld if requested (must be done after systemd is available)
|
||||
RUN if [ "$ENABLE_FIREWALLD" = "y" ] && command -v firewall-cmd >/dev/null; then \
|
||||
systemctl enable firewalld; \
|
||||
fi
|
||||
|
||||
# Create TUN device (will be mounted at runtime)
|
||||
RUN mkdir -p /dev/net
|
||||
|
||||
|
||||
@@ -77,15 +77,22 @@ fi
|
||||
# Verify all expected files were created
|
||||
echo "Verifying installation..."
|
||||
MISSING_FILES=0
|
||||
for f in \
|
||||
/etc/openvpn/server/server.conf \
|
||||
/etc/openvpn/server/ca.crt \
|
||||
/etc/openvpn/server/ca.key \
|
||||
"/etc/openvpn/server/$TLS_KEY_FILE" \
|
||||
/etc/openvpn/server/crl.pem \
|
||||
/etc/openvpn/server/easy-rsa/pki/ca.crt \
|
||||
/etc/iptables/add-openvpn-rules.sh \
|
||||
/root/testclient.ovpn; do
|
||||
# Build list of required files
|
||||
REQUIRED_FILES=(
|
||||
/etc/openvpn/server/server.conf
|
||||
/etc/openvpn/server/ca.crt
|
||||
/etc/openvpn/server/ca.key
|
||||
"/etc/openvpn/server/$TLS_KEY_FILE"
|
||||
/etc/openvpn/server/crl.pem
|
||||
/etc/openvpn/server/easy-rsa/pki/ca.crt
|
||||
/root/testclient.ovpn
|
||||
)
|
||||
# Only check for iptables script if firewalld is not active
|
||||
if ! systemctl is-active --quiet firewalld; then
|
||||
REQUIRED_FILES+=(/etc/iptables/add-openvpn-rules.sh)
|
||||
fi
|
||||
|
||||
for f in "${REQUIRED_FILES[@]}"; do
|
||||
if [ ! -f "$f" ]; then
|
||||
echo "ERROR: Missing file: $f"
|
||||
MISSING_FILES=$((MISSING_FILES + 1))
|
||||
@@ -380,21 +387,58 @@ echo ""
|
||||
# Verify OpenVPN server (started by systemd via install script)
|
||||
echo "Verifying OpenVPN server..."
|
||||
|
||||
# Verify iptables NAT rules exist (applied by iptables-openvpn service)
|
||||
echo "Verifying iptables NAT rules..."
|
||||
for _ in $(seq 1 10); do
|
||||
if iptables -t nat -L POSTROUTING -n | grep -q "10.8.0.0"; then
|
||||
echo "PASS: NAT POSTROUTING rule for 10.8.0.0/24 exists"
|
||||
break
|
||||
# Verify firewall rules exist
|
||||
echo "Verifying firewall rules..."
|
||||
if systemctl is-active --quiet firewalld; then
|
||||
# firewalld is active - verify masquerade is enabled
|
||||
echo "firewalld detected, checking masquerade..."
|
||||
for _ in $(seq 1 10); do
|
||||
if firewall-cmd --query-masquerade 2>/dev/null; then
|
||||
echo "PASS: firewalld masquerade is enabled"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if ! firewall-cmd --query-masquerade 2>/dev/null; then
|
||||
echo "FAIL: firewalld masquerade is not enabled"
|
||||
echo "Current firewalld config:"
|
||||
firewall-cmd --list-all 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
# Verify port is open
|
||||
if firewall-cmd --list-ports | grep -q "1194/udp"; then
|
||||
echo "PASS: OpenVPN port is open in firewalld"
|
||||
else
|
||||
echo "FAIL: OpenVPN port not found in firewalld"
|
||||
firewall-cmd --list-ports
|
||||
exit 1
|
||||
fi
|
||||
# Verify VPN subnet rich rule exists
|
||||
if firewall-cmd --list-rich-rules | grep -q 'source address="10.8.0.0/24"'; then
|
||||
echo "PASS: VPN subnet rich rule is configured"
|
||||
else
|
||||
echo "FAIL: VPN subnet rich rule not found in firewalld"
|
||||
echo "Current rich rules:"
|
||||
firewall-cmd --list-rich-rules
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# iptables mode - verify NAT rules
|
||||
echo "iptables mode, checking NAT rules..."
|
||||
for _ in $(seq 1 10); do
|
||||
if iptables -t nat -L POSTROUTING -n | grep -q "10.8.0.0"; then
|
||||
echo "PASS: NAT POSTROUTING rule for 10.8.0.0/24 exists"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if ! iptables -t nat -L POSTROUTING -n | grep -q "10.8.0.0"; then
|
||||
echo "FAIL: NAT POSTROUTING rule for 10.8.0.0/24 not found"
|
||||
echo "Current NAT rules:"
|
||||
iptables -t nat -L POSTROUTING -n -v
|
||||
systemctl status iptables-openvpn 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if ! iptables -t nat -L POSTROUTING -n | grep -q "10.8.0.0"; then
|
||||
echo "FAIL: NAT POSTROUTING rule for 10.8.0.0/24 not found"
|
||||
echo "Current NAT rules:"
|
||||
iptables -t nat -L POSTROUTING -n -v
|
||||
systemctl status iptables-openvpn 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify IP forwarding is enabled
|
||||
|
||||
Reference in New Issue
Block a user