Skip to content

Firewall

The script automatically configures the firewall during installation. It detects the available firewall backend and applies rules that enforce the configured access policy — internet routing, client-to-client access, and server-side local networks.

Rules are scoped by destination so that only explicitly allowed destinations (the internet, when --route-internet is enabled; local networks passed via --local-network; other VPN clients, when --client-to-client is enabled) are reachable. This also blocks clients from implicitly reaching RFC1918/ULA-style networks that were not explicitly allowed.

The detected backend and the active access policy are recorded in /etc/openvpn/server/openvpn-install.conf (mode 600) so that uninstallation can remove the exact rules that were added.

Firewall Backends

The script supports three firewall backends, chosen automatically based on what is active on the system:

Backend Used On Priority
firewalld RHEL, Fedora, CentOS 1st (preferred)
nftables Modern Linux distributions 2nd
iptables Legacy systems / fallback 3rd

Protected Networks

When internet routing is enabled, the following networks are explicitly rejected/dropped before the general "allow internet" rule, so that enabling --route-internet does not implicitly expose them. A network can still be reached if it is also passed to --local-network.

Family Protected networks
IPv4 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16
IPv6 ::1/128, fc00::/7, fe80::/10

firewalld

Used primarily on RHEL-based distributions (Fedora, Rocky, Alma, CentOS).

The script creates a dedicated openvpn-install zone (source = the VPN subnet) and an openvpn-egress policy object (ingress zone openvpn-install, egress zone ANY, default target DROP). Zone rich rules govern traffic addressed to the server itself; the policy object governs traffic forwarded through the server, which is what client destination rules need.

# Port and zone/policy setup
firewall-cmd --permanent --add-port=1194/udp
firewall-cmd --permanent --new-zone=openvpn-install
firewall-cmd --permanent --new-policy=openvpn-egress
firewall-cmd --permanent --policy=openvpn-egress --add-ingress-zone=openvpn-install
firewall-cmd --permanent --policy=openvpn-egress --add-egress-zone=ANY
firewall-cmd --permanent --policy=openvpn-egress --set-target=DROP

# VPN subnet source + reachable gateway
firewall-cmd --permanent --zone=openvpn-install --add-source=10.8.0.0/24
firewall-cmd --permanent --zone=openvpn-install --add-rich-rule='rule priority="-400" family="ipv4" destination address="10.8.0.1/32" accept'

# Per local network (--local-network): allow + NAT
firewall-cmd --permanent --policy=openvpn-egress --add-rich-rule='rule priority="-300" family="ipv4" destination address="192.168.1.0/24" accept'
firewall-cmd --permanent --policy=openvpn-egress --add-rich-rule='rule family="ipv4" destination address="192.168.1.0/24" masquerade'

# When --route-internet is enabled: reject protected networks, then allow + NAT everything else
firewall-cmd --permanent --policy=openvpn-egress --add-rich-rule='rule priority="-200" family="ipv4" destination address="10.0.0.0/8" reject'
firewall-cmd --permanent --policy=openvpn-egress --add-rich-rule='rule priority="-100" family="ipv4" accept'
firewall-cmd --permanent --policy=openvpn-egress --add-rich-rule='rule family="ipv4" masquerade'

# When --client-to-client is enabled: allow forwarding within the zone
firewall-cmd --permanent --zone=openvpn-install --add-forward

Equivalent family="ipv6" rules are added when IPv6 clients are enabled. Zone-wide masquerade is not used; NAT is applied only via the scoped rich rules above.

SELinux

If SELinux is active and a non-standard port is used, the script adds a port rule:

semanage port -a -t openvpn_port_t -p udp 1194

nftables

Rules are stored in /etc/nftables/openvpn.nft and included in the main nftables configuration.

The forward chain (hook priority -10) evaluates, per address family:

  1. Established/related return traffic to the VPN subnet — accepted.
  2. Traffic to each --local-network — accepted.
  3. Traffic between VPN clients — accepted only if --client-to-client is enabled.
  4. If --route-internet is enabled: protected networks are dropped, then all remaining VPN-subnet traffic is accepted.
  5. If --route-internet is disabled: all remaining VPN-subnet traffic is dropped (split tunnel — only the explicitly allowed destinations above are reachable).

Example (IPv4, full tunnel, no local networks, client-to-client disabled):

table inet openvpn {
    chain input {
        type filter hook input priority 0; policy accept;
        iifname "tun*" ip saddr 10.8.0.0/24 accept
        iifname "eth0" udp dport 1194 accept
    }
    chain forward {
        type filter hook forward priority -10; policy accept;
        oifname "tun*" ip daddr 10.8.0.0/24 ct state established,related accept
        iifname "tun*" ip saddr 10.8.0.0/24 ip daddr 10.0.0.0/8 drop
        # ... one drop rule per protected network ...
        iifname "tun*" ip saddr 10.8.0.0/24 accept
    }
}

A separate openvpn-nat table adds masquerade rules — per --local-network destination, and for the default interface when --route-internet is enabled. The NAT table is only created when it would contain at least one rule (internet routing enabled, or at least one local network configured).

With IPv6 support, equivalent ip6/inet rules are added.

iptables

For systems without firewalld or nftables, the script uses iptables with helper scripts:

File Description
/etc/iptables/add-openvpn-rules.sh Script to add firewall rules
/etc/iptables/rm-openvpn-rules.sh Script to remove firewall rules
iptables-openvpn.service Systemd service for rule persistence

The systemd service ensures rules are applied at boot and removed on shutdown. add-openvpn-rules.sh refuses to run (and traps cleanup) if a OPENVPN_INSTALL_FORWARD chain already exists, to avoid duplicating rules from a previous run.

A dedicated OPENVPN_INSTALL_FORWARD chain (per protocol family) enforces the access policy for traffic forwarded from the VPN subnet, so the same policy applies to both userspace and DCO traffic:

# Base forwarding + NAT for return traffic
iptables -N OPENVPN_INSTALL_FORWARD
iptables -I FORWARD 1 -o tun+ -d 10.8.0.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -I FORWARD 1 -i tun+ -s 10.8.0.0/24 -j OPENVPN_INSTALL_FORWARD

# Per local network (--local-network): allow + NAT
iptables -A OPENVPN_INSTALL_FORWARD -d 192.168.1.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -d 192.168.1.0/24 -j MASQUERADE

# When --client-to-client is enabled
iptables -A OPENVPN_INSTALL_FORWARD -d 10.8.0.0/24 -j ACCEPT

# When --route-internet is enabled: reject protected networks, then accept + NAT the rest
iptables -A OPENVPN_INSTALL_FORWARD -d 10.0.0.0/8 -j REJECT
iptables -A OPENVPN_INSTALL_FORWARD -j ACCEPT
iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -o eth0 -j MASQUERADE

# When --route-internet is disabled: reject everything not matched above
iptables -A OPENVPN_INSTALL_FORWARD -j REJECT

Equivalent ip6tables rules are added when IPv6 clients are enabled.

Cleanup on Uninstall

Firewall rules are removed during uninstallation. If /etc/openvpn/server/openvpn-install.conf exists (installations created after this policy system was added), the recorded FIREWALL_BACKEND is used to remove exactly the backend that was configured:

  • firewalld: OpenVPN port removed; the openvpn-egress policy and openvpn-install zone are deleted
  • nftables: /etc/nftables/openvpn.nft deleted, include removed
  • iptables: scripts and systemd service removed

For installations created before the manifest file existed, the script falls back to detecting the backend from the currently active firewall service and the presence of the rule files (legacy firewalld port/masquerade/rich-rule removal, /etc/nftables/openvpn.nft, or the iptables systemd service).

Manual Adjustments

Client-to-client access, server-side network access, and split tunneling are configured at install time via --client-to-client, --local-network, and --no-route-internet — see Network & DNS — Access Policy. These flags are not applied retroactively to an already-installed server; see Customization for adjusting server.conf and firewall rules manually after installation.