Preventing VPN leaks on Linux

A Raspberry Pi running OpenVPN with a D-Link DUB-1312 Ethernet adapter
A Raspberry Pi model B running OpenVPN with a D-Link DUB-1312 Ethernet adapter

If you connect to the internet through a public WiFi hotspot, or at school or at work, the metadata and contents of your online communications can be intercepted and viewed (or altered) by the network operator or another malicious party on the network. One of the ways to protect your privacy is to send all of your internet traffic through a Virtual Private Network.

Unfortunately a lot of personal data can leak out when you join a new network but have not yet connected the VPN. This guide shows you how to configure the Linux iptables firewall to block all normal outgoing connections, and only allow connections to your VPN server.

This guide assumes that you are connecting to your VPN on UDP port 443, or to a known server IP address. Visit an IP address checker such as IPLeak to confirm that your VPN is working before you start changing your firewall settings.

My ethernet port facing the internet (connected to the modem) is called eth0. The VPN connection is called tun0. Your network interfaces might be named differently. You can see a list of all network interfaces with the ifconfig command.

All these commands must be run as the root user or with sudo.

Begin by deleting all existing firewall rules:

iptables --flush
iptables --delete-chain
iptables -t nat --flush
iptables -t nat --delete-chain

Block all outgoing connections by default:

iptables -P OUTPUT DROP

Allow the special loopback device:

iptables -A INPUT -j ACCEPT -i lo
iptables -A OUTPUT -j ACCEPT -o lo

Method 1: Allow outgoing connections only for DNS and OpenVPN. This is simpler, but you might leak DNS lookup requests on port 53 when your VPN is disconnected:

iptables -A OUTPUT -j ACCEPT -p udp --dport 53
iptables -A OUTPUT -j ACCEPT -p udp --dport 443

Method 2: Alternately, you can allow outgoing connections to only specific IP addresses for your VPN servers. This method is immune to DNS leaks when the VPN is disconnected, but you will need to know the IP address of your VPN servers:

iptables -A OUTPUT -j ACCEPT -d 190.93.241.85
iptables -A OUTPUT -j ACCEPT -d 141.101.241.85
iptables -A OUTPUT -j ACCEPT -d 89.248.172.45

Finally allow outgoing connections through the VPN interface tun0:

iptables -A OUTPUT -j ACCEPT -o tun0

You should now be able to connect to your VPN server:

torguardlinux4

When the VPN is disconnected you should have no internet access:

torguardlinux5

Your firewall settings will be forgotten after you reboot. Save your list of commands in a bash file called /root/firewall.sh and remember to include the bash header. You can also include your command to launch OpenVPN here!

#!/bin/bash
iptables --flush
iptables --delete-chain
iptables -t nat --flush
iptables -t nat --delete-chain
iptables -P OUTPUT DROP
iptables -A INPUT -j ACCEPT -i lo
iptables -A OUTPUT -j ACCEPT -o lo
iptables -A OUTPUT -j ACCEPT -p udp --dport 53
iptables -A OUTPUT -j ACCEPT -p udp --dport 443
iptables -A OUTPUT -j ACCEPT -o tun0
openvpn /root/config.ovpn

Set it to run on boot by adding this line to the end of /etc/crontab:

@reboot root /root/firewall.sh