Understanding how VPN traffic interacts with firewall rules is essential for maintaining both security and functionality. Whether you’re using iptables on Linux or the newer nftables framework, properly configuring firewall rules for VPN connections ensures your traffic is protected while allowing legitimate connections to pass through. This guide walks you through the interaction between VPN protocols and firewall rules, with practical examples for both iptables and nftables.
Understanding the VPN Traffic Flow Through Firewalls
When you establish a VPN connection, your traffic follows a specific path through the network stack, and firewall rules apply at different stages of this journey. The key concept to understand is that VPN traffic typically enters your system in two forms: the encrypted VPN tunnel traffic (which appears as a single connection to the firewall) and the decrypted traffic that emerges from the tunnel (which your applications actually use).
On Linux systems, iptables and nftables filter traffic at the kernel level using netfilter hooks. These hooks intercept packets at different points in their lifecycle: PREROUTING (before routing decisions), INPUT (for packets destined for local processes), FORWARD (for packets being routed through the system), OUTPUT (for locally generated packets), and POSTROUTING (before packets leave the system). Understanding which chain your VPN traffic traverses is crucial for writing correct firewall rules.
For a typical VPN client running on your machine, the decrypted traffic exiting the VPN tunnel will pass through the OUTPUT and INPUT chains if it’s destined for local applications. For VPN servers or routing configurations where traffic passes through to other machines, the FORWARD chain becomes critical. This distinction matters because it determines which rules actually apply to your VPN traffic and in what order.
Configuring iptables for VPN Traffic
Basic iptables Setup for VPN Clients
For a Linux system acting as a VPN client, you’ll need rules that allow the VPN connection itself (usually on port 1194 for OpenVPN or port 51820 for WireGuard) while protecting other services. Here’s a basic configuration:
# Allow outgoing VPN traffic
iptables -A OUTPUT -p udp --dport 1194 -j ACCEPT
iptables -A OUTPUT -p udp --dport 51820 -j ACCEPT
# Allow established VPN connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow traffic from the VPN interface (tun0 or wg0)
iptables -A INPUT -i tun0 -j ACCEPT
iptables -A OUTPUT -o tun0 -j ACCEPT
# Default drop for input (assuming this is a secure client)
iptables -P INPUT DROP
The connection tracking module (conntrack) is particularly important for VPN configurations because it maintains state about established connections, allowing return traffic for your VPN sessions without requiring explicit rules for each response packet.
iptables for VPN Servers
If you’re running a VPN server, the requirements are different. You need to accept incoming VPN connections and properly forward traffic between the VPN interface and your network interface:
# Allow incoming VPN connections
iptables -A INPUT -p udp --dport 1194 -j ACCEPT # OpenVPN
iptables -A INPUT -p udp --dport 51820 -j ACCEPT # WireGuard
# Allow forwarding between network interfaces
iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
For servers handling routed VPN configurations (rather than bridged), you may also need NAT rules to allow VPN clients to access the internet through your server’s public IP address.
NAT Configuration for VPN Routing
When your VPN server acts as a gateway for VPN clients to access the internet, you’ll need to configure Network Address Translation:
# Enable NAT for VPN clients
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
# Alternative with specific outgoing interface
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j SNAT --to-source YOUR_SERVER_IP
The MASQUERADE target automatically uses the IP address of the specified outgoing interface, which is useful for dynamic IPs. The SNAT target is more explicit and slightly more efficient for static IPs.
Configuring nftables for VPN Traffic
Modern nftables Approach
The nftables framework provides a more modern and efficient alternative to iptables, with an unified syntax for IPv4, IPv6, and bridge configurations. Here’s how to configure nftables for VPN:
# Create a table for filter rules
nft add table ip filter
# Add chains for input, forward, and output
nft add chain ip filter input { type filter hook input priority 0; policy drop; }
nft add chain ip filter forward { type filter hook forward priority 0; policy drop; }
nft add chain ip filter output { type filter hook output priority 0; policy accept; }
# Allow VPN traffic
nft add rule ip filter input udp dport 1194 accept # OpenVPN
nft add rule ip filter input udp dport 51820 accept # WireGuard
# Allow established connections
nft add rule ip filter input ct state established,related accept
# Allow traffic from VPN interfaces
nft add rule ip filter input iifname "tun0" accept
nft add rule ip filter input iifname "wg0" accept
# Allow forwarding for VPN
nft add rule ip filter forward iifname "tun0" oifname "eth0" accept
nft add rule ip filter forward iifname "wg0" oifname "eth0" accept
nft add rule ip filter forward ct state established,related accept
# Enable IP forwarding
sysctl -w net.ipv4.ip_forward=1
nftables NAT Configuration
Setting up NAT in nftables uses a separate nat table:
# Create NAT table
nft add table ip nat
# Add prerouting and postrouting chains
nft add chain ip nat prerouting { type nat hook prerouting priority -100; }
nft add chain ip nat postrouting { type nat hook postrouting priority 100; }
# Add NAT rule for VPN clients
nft add rule ip nat postrouting ip saddr 10.8.0.0/24 oifname "eth0" masquerade
One advantage of nftables is that rules persist more reliably across reboots when saved and restored using the nft list ruleset > /etc/nftables.conf command and loaded at startup.
Common Firewall VPN Issues and Solutions
Problem: VPN Connection Establishes But No Traffic Flows
This common issue typically occurs when firewall rules block the decrypted traffic exiting the VPN tunnel. Check that your INPUT and OUTPUT rules allow traffic on the VPN interface (tun0, tun1, wg0, etc.):
# Verify VPN interface exists
ip link show
# Check if traffic is reaching the interface
tcpdump -i tun0 -n
# Temporarily disable firewall to test
iptables -F # Warning: only for testing
If traffic flows after flushing rules, gradually add back your firewall rules to identify which one is blocking VPN traffic.
Problem: Split Tunneling Conflicts with Firewall
When using split tunneling (where only some traffic goes through the VPN), firewall rules can become complex. You need to ensure that both the VPN tunnel traffic and the non-VPN direct traffic are properly handled:
# Example: Allow direct traffic for specific subnets while VPN handles the rest
# Direct traffic to local network
iptables -A OUTPUT -d 192.168.1.0/24 -j ACCEPT
# VPN tunnel traffic
iptables -A OUTPUT -o tun0 -j ACCEPT
# Default drop
iptables -P OUTPUT DROP
Problem: DNS Leaks Through Firewall
DNS requests can leak past VPN tunnels if your firewall doesn’t properly route DNS traffic. Ensure DNS queries go through the VPN or are explicitly allowed only to trusted servers:
# Force DNS through VPN tunnel
iptables -A OUTPUT -p udp --dport 53 -o tun0 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -o tun0 -j ACCEPT
# Block DNS to other interfaces (prevents leaks)
iptables -A OUTPUT -p udp --dport 53 -j DROP
iptables -A OUTPUT -p tcp --dport 53 -j DROP
Security Considerations for VPN Firewall Rules
Principle of Least Privilege
When configuring firewall rules for VPN, apply the principle of least privilege: only allow the traffic that is explicitly required. This means specifying exact ports, protocols, and IP ranges rather than using broad rules:
# Prefer this specific rule
iptables -A INPUT -p udp --dport 51820 -s 203.0.113.0/24 -j ACCEPT
# Over this broad rule
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
IPv6 Considerations
Don’t forget about IPv6 when configuring VPN firewalls. If your VPN doesn’t handle IPv6, you may want to drop IPv6 traffic to prevent leaks:
# Drop all IPv6 traffic when using IPv4-only VPN
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
# Or specifically block tunnel traffic
ip6tables -A INPUT -i tun0 -j DROP
Kill Switch Implementation
A VPN kill switch prevents data leaks by blocking traffic if the VPN connection drops. This can be implemented with firewall rules:
# Block all non-VPN traffic when VPN is active (simplified kill switch)
# Only allow output to VPN server
iptables -A OUTPUT -p udp --dport 1194 -j ACCEPT
iptables -A OUTPUT -p udp --dport 51820 -j ACCEPT
# Allow only tunnel traffic
iptables -A OUTPUT -o tun0 -j ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT
# Drop everything else
iptables -P OUTPUT DROP
Managing Persistent Firewall Rules
Saving iptables Rules
On Debian-based systems:
# Save current rules
iptables-save > /etc/iptables/rules.v4
# Restore on boot (create /etc/network/if-pre-up.d/iptables)
#!/bin/sh
/sbin/iptables-restore < /etc/iptables/rules.v4
On RHEL-based systems:
# Save current rules
service iptables save
# This saves to /etc/sysconfig/iptables
Saving nftables Rules
# Save ruleset
nft list ruleset > /etc/nftables.conf
# Ensure it's loaded at boot (systemd service or init script)
Testing Your VPN Firewall Configuration
After configuring your firewall rules, thorough testing is essential:
- Verify VPN connection works: Connect to your VPN and confirm traffic flows
- Check for DNS leaks: Use tools like dnsleaktest.com to ensure DNS requests go through the VPN
- Test kill switch: Temporarily disconnect your VPN and verify that traffic is blocked
- Verify IP visibility: Check that your public IP shows as the VPN server’s IP, not your real IP
- Test both directions: Ensure inbound and outbound traffic are properly filtered
# Quick verification commands
curl ifconfig.me # Check public IP
dig +short myip.opendns.com @resolver1.opendns.com # Alternative IP check
tcpdump -i tun0 -n # Monitor VPN interface traffic
tcpdump -i eth0 -n port 1194 or port 51820 # Monitor VPN server communication
Related Reading
- Configure Firewall Rules on OPNsense to Block Known
- Vpn Port Selection Which Ports Bypass Most Firewall.
- CalyxOS Datura Firewall Setup: Controlling Per-App.
- How To Bypass China Great Firewall Using Shadowsocks With Ob
- MacOS Firewall Configuration for Privacy
Built by theluckystrike — More at zovo.one