I'm trying to implement a set of iptables rules for OpenVPN:
- Everything (without exception) goes through OpenVPN
- If OpenVPN is down or inaccessible everything (without exception) cannot access the internet
- Local access is always possible, irrespective of the status of OpenVPN
The best (out of six examples) I've seen so far is this, however it still allows access to the internet when the VPN is down through ipv6:
# Clean down existing rules
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -F
iptables -t mangle -F
iptables -F
iptables -X
ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -t nat -F
ip6tables -t mangle -F
ip6tables -F
ip6tables -X
# Allow loopback device (internal communication)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Allow all local traffic.
iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT
# Allow VPN establishment
iptables -A OUTPUT -p udp --dport 1194 -j ACCEPT
iptables -A INPUT -p udp --sport 1194 -j ACCEPT
# Accept all TUN connections (tun = VPN tunnel)
iptables -A OUTPUT -o tun+ -j ACCEPT
iptables -A INPUT -i tun+ -j ACCEPT
# Set default policies to drop all communication unless specifically allowed
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROPIf I try to blanket block all ipv6 traffic (which is what a number of examples suggest) then I cannot connect to anything:
# Set default policies to drop all communication unless specifically allowed
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROPIf I try to mirror what is in the ipv4 section then I have this:
# Allow loopback device (internal communication)
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
# Allow all local traffic.
# (Skipped as I don't know how to do this)
# Allow VPN establishment
ip6tables -A OUTPUT -p udp --dport 1194 -j ACCEPT
ip6tables -A INPUT -p udp --sport 1194 -j ACCEPT
# Accept all TUN connections (tun = VPN tunnel)
ip6tables -A OUTPUT -o tun+ -j ACCEPT
ip6tables -A INPUT -i tun+ -j ACCEPTNow I'm unable to connect to anywhere and curl just hangs.
How can I fix this?
4 Answers
Future readers, be aware that the rules presented here "will allow deanonymization because it allows any connection over port 1194, not just traffic originating from OpenVPN". This answer presents a much simpler set of rules which do not require hardcoding any IPs or ports.
It turns out that I'd not whitelisted the DNS servers of the VPN service. Once I did that, I was able to blacklist ipv6 without an issue.
Please note: As per Rock Storm's comment, this is probably not the best way of doing this as it allows any connection over port 1194. See this answer for a better approach which doesn't require hardcoding any IPs or ports - however it needs a further modification to allow local traffic (which, in my case, is allowing input and output for
192.168.0.0/16)
For the sake of completeness, this is what I finally got to work. Three things to bear in mind:
- You should replace
[Primary DNS IP here]and[Secondary DNS IP here]with the IP addresses of the DNS servers you're planning to use - Don't forget to edit
/etc/resolvconf.confto add the IP addresses in #1 as nameservers and blacklist your own to avoid DNS leakages. - This assumes your local traffic has an IP range starting
192.168. If not, you'll need to change this.
Code as follows:
# Remove any existing rules
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -F
sudo iptables -X
sudo ip6tables -P INPUT ACCEPT
sudo ip6tables -P FORWARD ACCEPT
sudo ip6tables -P OUTPUT ACCEPT
sudo ip6tables -t nat -F
sudo ip6tables -t mangle -F
sudo ip6tables -F
sudo ip6tables -X
# Allow loopback device (internal communication)
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
# Allow all local traffic
sudo iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
sudo iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT
# Allow DNS (could be VPN provider or someone like Cloudflare's 1.1.1.1)
sudo iptables -A OUTPUT -d [Primary DNS IP here] -j ACCEPT
sudo iptables -A OUTPUT -d [Secondary DNS IP here] -j ACCEPT
# Allow related and established connections
sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Allow VPN establishment
sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 53 -j ACCEPT
sudo iptables -A OUTPUT -p udp --dport 1198 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 1198 -j ACCEPT
# Accept all tun0 (VPN tunnel) connections
sudo iptables -A OUTPUT -o tun0 -j ACCEPT
sudo iptables -A INPUT -i tun0 -j ACCEPT
# Allow for nslookup to not throw an error
sudo ip6tables -I OUTPUT 1 -p udp -s 0000:0000:0000:0000:0000:0000:0000:0001 -d 0000:0000:0000:0000:0000:0000:0000:0001 -j ACCEPT
# Drop everything else (ipv4)
sudo iptables -P INPUT DROP
sudo iptables -P OUTPUT DROP
sudo iptables -P FORWARD DROP
# Drop everything (ipv6)
sudo ip6tables -P INPUT DROP
sudo ip6tables -P OUTPUT DROP
sudo ip6tables -P FORWARD DROP Your example script cleans all rules for IPv4 and IPv6 rulesets... but doesn't add any rules for IPv6.
Notice how the "Clean down existing rules" section calls both iptables and ip6tables, resetting both firewalls to their default "wide open" state. The remaining sections, however, do nothing with ip6tables at all.
I just fell over this topic, and have a comment and some additional information.
My objective was to have a VPN connection and killswitch (running on tun0) with a network gateway to share the VPN service on the local lan..on the server ip (used as gateway on various PCs and TVs. etc)
But I would also like to access the server over SSH on the WAN IP!
I also what to run a second private VPN on tun1, so I can access my local LAN from anywhere.
So. I run a VPN client with killswitch and gateway and a VPN server. And I have full VPN and SSH access on the global WAN IP
This complicate things a bit.
Also the line
#Allow related and established connections
sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPTWill disable the killswitch sever side!!!. The gateway is ok, but programs link a torrent-client etc, will still have access to the net if the main vpn is down.
Here is my solution
IT works pretty well.
Can any iptables gurus find any loopholes etc in the setup then please comment.
I have made it as a script to active the setup. It makes it a bit more easy when debugging and setting to no firewall. Its not really persistent, so either commit it properly or run on startup with systemd.
#!/bin/sh
#A link to this script runs as a systemd runscript
#Here:
#nano /usr/local/bin/fwmark.sh
echo running NEW set_iptabels
# Clean down existing rules
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -F
iptables -X
ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -t nat -F
ip6tables -t nat -X
ip6tables -t mangle -F
ip6tables -F
ip6tables -X
#Start Killswitch configuration
# Allow loopback device (internal communication)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Allow all local traffic
iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT
# Allow DNS
iptables -A OUTPUT -d 4.2.2.2 -j ACCEPT
iptables -A OUTPUT -d 4.2.2.3 -j ACCEPT
# Allow related and established connections
# DO NOT RUN THIS---LOTS OF GUIDES SAY TO..IT DISABLES THE KILLSWITCH SERVER SIDE. GATEWAY IS OK SEVER NOT!!!!!!!!
#### DONT RUN >>iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Allow VPN establishment
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p udp --sport 53 -j ACCEPT
iptables -A OUTPUT -p udp --dport 1912 -j ACCEPT
iptables -A INPUT -p udp --sport 1912 -j ACCEPT
# Accept all tun0 (VPN tunnel) connections
iptables -A OUTPUT -o tun0 -j ACCEPT
iptables -A INPUT -i tun0 -j ACCEPT
# Allow for nslookup to not throw an error
ip6tables -I OUTPUT 1 -p udp -s 0000:0000:0000:0000:0000:0000:0000:0001 -d 0000:0000:0000:0000:0000:0000:0000:0001 -j ACCEPT
# Drop everything else (ipv4)
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
# Drop everything (ipv6)
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROP
#End Killswitch
#Gatewate forward
#ssh port 80 etc are set to route to enp6s0 via mark 65 rule created here:
ip rule add fwmark 65 table novpn > /dev/null 2>&1;
ip route add default via 192.168.1.1 dev enp6s0 table novpn > /dev/null 2>&1;
ip route flush cache > /dev/null 2>&1;
#VPN Gateway
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i tun0 -o enp6s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i enp6s0 -o tun0 -j ACCEPT
#Set connection port for Access from WAN
#ssh force private global IP IN AND OUT
iptables -A INPUT -p tcp --sport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -o enp6s0 -p tcp -m tcp --sport 22 -m comment --comment "ssh-22" -j ACCEPT
iptables -A OUTPUT -o enp6s0 -p tcp -m tcp --dport 22 -m comment --comment "ssh-22" -j ACCEPT
iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 65
iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 65
iptables -A INPUT -i tun0 -p tcp -m tcp --sport 22 -j DROP
iptables -A INPUT -i tun0 -p tcp -m tcp --dport 22 -j DROP
iptables -A OUTPUT -o tun0 -p tcp -m tcp --sport 22 -j DROP
iptables -A OUTPUT -o tun0 -p tcp -m tcp --dport 22 -j DROP
#Private VPN ON tun1 IN AND OUT - use TCP! UDP will give endless TLS errors, that are currently unresolved.
iptables -A INPUT -p tcp --sport 1194 -j ACCEPT
iptables -A INPUT -p tcp --dport 1194 -j ACCEPT
iptables -A OUTPUT -o enp6s0 -p tcp -m tcp --sport 1194 -m comment --comment "vpn-1194" -j ACCEPT
iptables -A OUTPUT -o enp6s0 -p tcp -m tcp --dport 1194 -m comment --comment "vpn-1194" -j ACCEPT
iptables -t mangle -A OUTPUT -p tcp --sport 1194 -j MARK --set-mark 65
iptables -t mangle -A OUTPUT -p tcp --dport 1194 -j MARK --set-mark 65
iptables -A INPUT -i tun0 -p tcp -m tcp --sport 1194 -j DROP
iptables -A INPUT -i tun0 -p tcp -m tcp --dport 1194 -j DROP
iptables -A OUTPUT -o tun0 -p tcp -m tcp --sport 1194 -j DROP
iptables -A OUTPUT -o tun0 -p tcp -m tcp --dport 1194 -j DROP
#http force private global IP OUT ONLY
iptables -A INPUT -p tcp --sport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -t mangle -A OUTPUT -p tcp --sport 80 -j MARK --set-mark 65
iptables -t mangle -A OUTPUT -p tcp --sport 80 -j MARK --set-mark 65
iptables -A INPUT -i tun0 -p tcp -m tcp --sport 80 -j DROP
iptables -A INPUT -i tun0 -p tcp -m tcp --dport 80 -j DROP
#End WAN access
#Specific private VPN server setup on tun1 service Activate with>> openvpn@server start/stop/status
#Private VPN Server iptables -t nat -A POSTROUTING -o tun1 -j MASQUERADE iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp6s0 -j MASQUERADE iptables -A FORWARD -i tun1 -o enp6s0 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i enp6s0 -o tun1 -j ACCEPT
#VPN server forward iptables -A FORWARD -i tun1 -o enp6s0 -s 10.8.0.0/24 -j ACCEPT iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
#Local LAN access iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp6s0 -j SNAT --to-source 192.168.1.100
# VPN Server enable tun1 iptables -A INPUT -i tun1 -j ACCEPT iptables -A FORWARD -i tun1 -j ACCEPT iptables -A OUTPUT -o tun1 -j ACCEPT
#End Private VPN Server
#save and make persistant - remove all putput from logfile
iptables-save >/etc/iptables/rules.v4 > /dev/null 2>&1;
ip6tables-save >/etc/iptables/rules.v6 > /dev/null 2>&1;
netfilter-persistent save > /dev/null 2>&1;