mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-16 08:57:03 +01:00
feat: add native nftables support (#1389)
- Add nftables as a third firewall backend option alongside firewalld and iptables - Detection priority: firewalld → nftables → iptables (legacy fallback) - Uses dedicated `openvpn` and `openvpn-nat` tables for clean isolation - Integrates with native `nftables.service` via include in `/etc/nftables.conf` Closes https://github.com/angristan/openvpn-install/issues/530
This commit is contained in:
12
.github/workflows/docker-test.yml
vendored
12
.github/workflows/docker-test.yml
vendored
@@ -98,6 +98,15 @@ jobs:
|
|||||||
name: tls-crypt-v2
|
name: tls-crypt-v2
|
||||||
sig: "1"
|
sig: "1"
|
||||||
key_file: tls-crypt-v2.key
|
key_file: tls-crypt-v2.key
|
||||||
|
# Test nftables support on Debian
|
||||||
|
- os:
|
||||||
|
name: debian-12-nftables
|
||||||
|
image: debian:12
|
||||||
|
enable_nftables: true
|
||||||
|
tls:
|
||||||
|
name: tls-crypt-v2
|
||||||
|
sig: "1"
|
||||||
|
key_file: tls-crypt-v2.key
|
||||||
|
|
||||||
name: ${{ matrix.os.name }}
|
name: ${{ matrix.os.name }}
|
||||||
steps:
|
steps:
|
||||||
@@ -113,6 +122,7 @@ jobs:
|
|||||||
docker build \
|
docker build \
|
||||||
--build-arg BASE_IMAGE=${{ matrix.os.image }} \
|
--build-arg BASE_IMAGE=${{ matrix.os.image }} \
|
||||||
--build-arg ENABLE_FIREWALLD=${{ matrix.os.enable_firewalld && 'y' || 'n' }} \
|
--build-arg ENABLE_FIREWALLD=${{ matrix.os.enable_firewalld && 'y' || 'n' }} \
|
||||||
|
--build-arg ENABLE_NFTABLES=${{ matrix.os.enable_nftables && 'y' || 'n' }} \
|
||||||
-t openvpn-server \
|
-t openvpn-server \
|
||||||
-f test/Dockerfile.server .
|
-f test/Dockerfile.server .
|
||||||
|
|
||||||
@@ -269,7 +279,7 @@ jobs:
|
|||||||
- name: Show install script log
|
- name: Show install script log
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
docker cp openvpn-server:/opt/openvpn-install.log /tmp/openvpn-install.log 2>/dev/null && \
|
docker cp openvpn-server:/root/openvpn-install.log /tmp/openvpn-install.log 2>/dev/null && \
|
||||||
cat /tmp/openvpn-install.log || echo "No install log found"
|
cat /tmp/openvpn-install.log || echo "No install log found"
|
||||||
|
|
||||||
- name: Show client logs
|
- name: Show client logs
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export CLIENTNUMBER="1" # Revokes the first client in the list
|
|||||||
- Installs and configures a ready-to-use OpenVPN server
|
- Installs and configures a ready-to-use OpenVPN server
|
||||||
- Certificate renewal for both client and server certificates
|
- 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
|
- Uses [official OpenVPN repositories](https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos) when possible for the latest stable releases
|
||||||
- Firewall rules and forwarding managed seamlessly (native firewalld support, iptables fallback)
|
- Firewall rules and forwarding managed seamlessly (native firewalld and nftables support, iptables fallback)
|
||||||
- If needed, the script can cleanly remove OpenVPN, including configuration and firewall rules
|
- 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)
|
- 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)
|
- OpenVPN 2.4 features, mainly encryption improvements (see [Security and Encryption](#security-and-encryption) below)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ services:
|
|||||||
dockerfile: test/Dockerfile.server
|
dockerfile: test/Dockerfile.server
|
||||||
args:
|
args:
|
||||||
BASE_IMAGE: ${BASE_IMAGE:-ubuntu:24.04}
|
BASE_IMAGE: ${BASE_IMAGE:-ubuntu:24.04}
|
||||||
|
ENABLE_FIREWALLD: ${ENABLE_FIREWALLD:-n}
|
||||||
|
ENABLE_NFTABLES: ${ENABLE_NFTABLES:-n}
|
||||||
container_name: openvpn-server
|
container_name: openvpn-server
|
||||||
hostname: openvpn-server
|
hostname: openvpn-server
|
||||||
privileged: true
|
privileged: true
|
||||||
|
|||||||
@@ -1436,8 +1436,52 @@ verb 3" >>/etc/openvpn/server/server.conf
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
run_cmd "Reloading firewalld" firewall-cmd --reload
|
run_cmd "Reloading firewalld" firewall-cmd --reload
|
||||||
|
elif systemctl is-active --quiet nftables; then
|
||||||
|
# Use nftables native rules for systems with nftables active
|
||||||
|
log_info "nftables detected, configuring nftables rules..."
|
||||||
|
run_cmd_fatal "Creating nftables directory" mkdir -p /etc/nftables
|
||||||
|
|
||||||
|
# Create nftables rules file
|
||||||
|
echo "table inet openvpn {
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority 0; policy accept;
|
||||||
|
iifname \"tun0\" accept
|
||||||
|
iifname \"$NIC\" $PROTOCOL dport $PORT accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority 0; policy accept;
|
||||||
|
iifname \"$NIC\" oifname \"tun0\" accept
|
||||||
|
iifname \"tun0\" oifname \"$NIC\" accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table ip openvpn-nat {
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority 100; policy accept;
|
||||||
|
ip saddr 10.8.0.0/24 oifname \"$NIC\" masquerade
|
||||||
|
}
|
||||||
|
}" >/etc/nftables/openvpn.nft
|
||||||
|
|
||||||
|
if [[ $IPV6_SUPPORT == 'y' ]]; then
|
||||||
|
echo "
|
||||||
|
table ip6 openvpn-nat {
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority 100; policy accept;
|
||||||
|
ip6 saddr fd42:42:42:42::/112 oifname \"$NIC\" masquerade
|
||||||
|
}
|
||||||
|
}" >>/etc/nftables/openvpn.nft
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add include to nftables.conf if not already present
|
||||||
|
if ! grep -q 'include.*/etc/nftables/openvpn.nft' /etc/nftables.conf; then
|
||||||
|
run_cmd "Adding include to nftables.conf" sh -c 'echo "include \"/etc/nftables/openvpn.nft\"" >> /etc/nftables.conf'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload nftables to apply rules
|
||||||
|
run_cmd "Reloading nftables" systemctl reload nftables
|
||||||
else
|
else
|
||||||
# Use iptables for systems without firewalld
|
# Use iptables for systems without firewalld or nftables
|
||||||
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
||||||
|
|
||||||
# Script to add rules
|
# Script to add rules
|
||||||
@@ -2143,6 +2187,14 @@ function removeOpenVPN() {
|
|||||||
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 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 "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
|
run_cmd "Reloading firewalld" firewall-cmd --reload
|
||||||
|
elif [[ -f /etc/nftables/openvpn.nft ]]; then
|
||||||
|
# nftables was used
|
||||||
|
# Delete tables (suppress errors in case tables don't exist)
|
||||||
|
nft delete table inet openvpn 2>/dev/null || true
|
||||||
|
nft delete table ip openvpn-nat 2>/dev/null || true
|
||||||
|
nft delete table ip6 openvpn-nat 2>/dev/null || true
|
||||||
|
run_cmd "Removing include from nftables.conf" sed -i '/include.*openvpn\.nft/d' /etc/nftables.conf
|
||||||
|
run_cmd "Removing nftables rules file" rm -f /etc/nftables/openvpn.nft
|
||||||
elif [[ -f /etc/systemd/system/iptables-openvpn.service ]]; then
|
elif [[ -f /etc/systemd/system/iptables-openvpn.service ]]; then
|
||||||
# iptables was used
|
# iptables was used
|
||||||
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
||||||
|
|||||||
@@ -7,32 +7,40 @@ FROM ${BASE_IMAGE}
|
|||||||
ARG BASE_IMAGE
|
ARG BASE_IMAGE
|
||||||
# Set to "y" to install and enable firewalld for testing
|
# Set to "y" to install and enable firewalld for testing
|
||||||
ARG ENABLE_FIREWALLD=n
|
ARG ENABLE_FIREWALLD=n
|
||||||
|
# Set to "y" to install and enable nftables for testing
|
||||||
|
ARG ENABLE_NFTABLES=n
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV ENABLE_FIREWALLD=${ENABLE_FIREWALLD}
|
ENV ENABLE_FIREWALLD=${ENABLE_FIREWALLD}
|
||||||
|
ENV ENABLE_NFTABLES=${ENABLE_NFTABLES}
|
||||||
|
|
||||||
# Install basic dependencies based on the OS
|
# Install basic dependencies based on the OS
|
||||||
# dnsutils/bind-utils provides dig for DNS testing with Unbound
|
# dnsutils/bind-utils provides dig for DNS testing with Unbound
|
||||||
RUN if command -v apt-get >/dev/null; then \
|
RUN if command -v apt-get >/dev/null; then \
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
apt-get update && apt-get install -y --no-install-recommends \
|
||||||
iproute2 iptables curl procps systemd systemd-sysv dnsutils \
|
iproute2 iptables curl procps systemd systemd-sysv dnsutils \
|
||||||
|
&& if [ "$ENABLE_NFTABLES" = "y" ]; then apt-get install -y --no-install-recommends nftables; fi \
|
||||||
&& rm -rf /var/lib/apt/lists/*; \
|
&& rm -rf /var/lib/apt/lists/*; \
|
||||||
elif command -v dnf >/dev/null; then \
|
elif command -v dnf >/dev/null; then \
|
||||||
dnf install -y --allowerasing \
|
dnf install -y --allowerasing \
|
||||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||||
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then dnf install -y firewalld; fi \
|
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then dnf install -y firewalld; fi \
|
||||||
|
&& if [ "$ENABLE_NFTABLES" = "y" ]; then dnf install -y nftables; fi \
|
||||||
&& dnf clean all; \
|
&& dnf clean all; \
|
||||||
elif command -v yum >/dev/null; then \
|
elif command -v yum >/dev/null; then \
|
||||||
yum install -y \
|
yum install -y \
|
||||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||||
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then yum install -y firewalld; fi \
|
&& if [ "$ENABLE_FIREWALLD" = "y" ]; then yum install -y firewalld; fi \
|
||||||
|
&& if [ "$ENABLE_NFTABLES" = "y" ]; then yum install -y nftables; fi \
|
||||||
&& yum clean all; \
|
&& yum clean all; \
|
||||||
elif command -v pacman >/dev/null; then \
|
elif command -v pacman >/dev/null; then \
|
||||||
pacman -Syu --noconfirm \
|
pacman -Syu --noconfirm \
|
||||||
iproute2 iptables curl procps-ng bind \
|
iproute2 iptables curl procps-ng bind \
|
||||||
|
&& if [ "$ENABLE_NFTABLES" = "y" ]; then pacman -S --noconfirm nftables; fi \
|
||||||
&& pacman -Scc --noconfirm; \
|
&& pacman -Scc --noconfirm; \
|
||||||
elif command -v zypper >/dev/null; then \
|
elif command -v zypper >/dev/null; then \
|
||||||
zypper install -y \
|
zypper install -y \
|
||||||
iproute2 iptables curl procps systemd tar gzip bind-utils gawk \
|
iproute2 iptables curl procps systemd tar gzip bind-utils gawk \
|
||||||
|
&& if [ "$ENABLE_NFTABLES" = "y" ]; then zypper install -y nftables; fi \
|
||||||
&& zypper clean -a; \
|
&& zypper clean -a; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -41,6 +49,14 @@ RUN if [ "$ENABLE_FIREWALLD" = "y" ] && command -v firewall-cmd >/dev/null; then
|
|||||||
systemctl enable firewalld; \
|
systemctl enable firewalld; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Enable nftables if requested (must be done after systemd is available)
|
||||||
|
# Use empty nftables.conf - do NOT flush ruleset as it removes Docker's networking rules
|
||||||
|
RUN if [ "$ENABLE_NFTABLES" = "y" ] && command -v nft >/dev/null; then \
|
||||||
|
systemctl enable nftables; \
|
||||||
|
mkdir -p /etc/nftables; \
|
||||||
|
echo '#!/usr/sbin/nft -f' > /etc/nftables.conf; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Create TUN device (will be mounted at runtime)
|
# Create TUN device (will be mounted at runtime)
|
||||||
RUN mkdir -p /dev/net
|
RUN mkdir -p /dev/net
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,11 @@ REQUIRED_FILES=(
|
|||||||
/etc/openvpn/server/easy-rsa/pki/ca.crt
|
/etc/openvpn/server/easy-rsa/pki/ca.crt
|
||||||
/root/testclient.ovpn
|
/root/testclient.ovpn
|
||||||
)
|
)
|
||||||
# Only check for iptables script if firewalld is not active
|
# Only check for iptables script if firewalld and nftables are not active
|
||||||
if ! systemctl is-active --quiet firewalld; then
|
if ! systemctl is-active --quiet firewalld && ! systemctl is-active --quiet nftables; then
|
||||||
REQUIRED_FILES+=(/etc/iptables/add-openvpn-rules.sh)
|
REQUIRED_FILES+=(/etc/iptables/add-openvpn-rules.sh)
|
||||||
|
elif systemctl is-active --quiet nftables; then
|
||||||
|
REQUIRED_FILES+=(/etc/nftables/openvpn.nft)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in "${REQUIRED_FILES[@]}"; do
|
for f in "${REQUIRED_FILES[@]}"; do
|
||||||
@@ -422,6 +424,46 @@ if systemctl is-active --quiet firewalld; then
|
|||||||
firewall-cmd --list-rich-rules
|
firewall-cmd --list-rich-rules
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
elif systemctl is-active --quiet nftables; then
|
||||||
|
# nftables mode - verify OpenVPN tables exist
|
||||||
|
echo "nftables detected, checking OpenVPN tables..."
|
||||||
|
for _ in $(seq 1 10); do
|
||||||
|
if nft list table inet openvpn >/dev/null 2>&1; then
|
||||||
|
echo "PASS: nftables 'inet openvpn' table exists"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if ! nft list table inet openvpn >/dev/null 2>&1; then
|
||||||
|
echo "FAIL: nftables 'inet openvpn' table not found"
|
||||||
|
echo "Current nftables ruleset:"
|
||||||
|
nft list ruleset 2>&1 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Verify NAT table exists
|
||||||
|
if nft list table ip openvpn-nat >/dev/null 2>&1; then
|
||||||
|
echo "PASS: nftables 'ip openvpn-nat' table exists"
|
||||||
|
else
|
||||||
|
echo "FAIL: nftables 'ip openvpn-nat' table not found"
|
||||||
|
nft list ruleset 2>&1 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Verify masquerade rule exists
|
||||||
|
if nft list table ip openvpn-nat | grep -q "masquerade"; then
|
||||||
|
echo "PASS: nftables masquerade rule exists"
|
||||||
|
else
|
||||||
|
echo "FAIL: nftables masquerade rule not found"
|
||||||
|
nft list table ip openvpn-nat 2>&1 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Verify include in nftables.conf
|
||||||
|
if grep -q 'include.*/etc/nftables/openvpn.nft' /etc/nftables.conf; then
|
||||||
|
echo "PASS: OpenVPN rules included in nftables.conf"
|
||||||
|
else
|
||||||
|
echo "FAIL: OpenVPN rules not included in nftables.conf"
|
||||||
|
cat /etc/nftables.conf 2>&1 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
# iptables mode - verify NAT rules
|
# iptables mode - verify NAT rules
|
||||||
echo "iptables mode, checking NAT rules..."
|
echo "iptables mode, checking NAT rules..."
|
||||||
|
|||||||
Reference in New Issue
Block a user