VPN Killswitch with UFW
2021 March 23
Updated 2021 November 13 to fix a code issue.
I've written about Virtual Private Networks (VPNs) before:
As I've argued before, VPNs as a privacy tool are mostly hype that addresses obsolete security issues and shifts trust from one ISP to another, rather than providing a strong form of anonymity. I don't think they're useless, but I think you should understand how they work and what benefits they do and don't provide for you. I don't recommend paying for a VPN subscription just because you saw an ad that told you it would protect you from hackers.
Supposing you are using a VPN to proxy your traffic for privacy, you should set up a killswitch. If your VPN fails, you may fall back to connecting directly to the destination, allowing it to see your IP address and allowing your ISP to see that you are connecting to that destination. A killswitch blocks all traffic if your VPN drops, preventing this issue.
Uncomplicated Firewall (UFW) is what it sounds like. We will be using it to set up the killswitch. This guide assumes you're running GNU/Linux and have root access through sudo. It also assumes you have /usr/local/bin in your PATH. This method does not require you to know the IP address of your VPN server, which is nice because that may change each time you connect. Unfortunately, if your VPN fails, you will not be able to reconnect without first disabling the killswitch.
1. Install UFW
The ufw package is probably available through your package manager.
2. Connect to your VPN
You must connect to the VPN before enabling the killswitch. This will vary some by distribution. Likely, you have NetworkManager available and can use it to set up a VPN connection using configuration files from your VPN provider.
I will assume here that the VPN is running on the tun0 interface. You can run:
ip link show
to see your network interfaces if you aren't sure.
3. Set UFW rules
Now that we have a VPN connection on tun0 (replace tun0 with your interface if applicable), we'll set UFW rules to allow traffic through only the VPN tunnel. We'll run the following commands:
sudo ufw reset
sudo ufw default deny incoming
sudo ufw default deny outgoing
These instructions tell our firewall not to allow any traffic. Now we need to allow VPN connections.
sudo ufw allow out on tun0 from any to any
(Replace tun0 with your VPN interface.) This says, "Allow any outgoing connections over the established VPN tunnel."
(Outgoing means that you initiate the connection by sending a message to the server. Once this connection has been established, the server can respond. Unless you're running a server, you probably don't need to and should not accept incoming connections.)
If you also need to allow incoming connections, also use a command like
sudo ufw allow in on tun0 from any to any
We'll allow connections on the local network as well. Also run the line:
sudo ufw allow out to 192.168.1.0/24
Here, I've assumed local IP addresses start with 192.168.1. If you have a different local subnet, adapt this line accordingly. Again, we're only allowing outgoing connections here.
Finally, enable UFW:
sudo ufw enable
4. Disable IPv6
IPv6 causes problems for many VPN configurations. Your VPN provider may have specific instructions for IPv6, which you may want to follow instead of disabling IPv6 altogether. As a generic safe step, we will disable IPv6 so that all of our traffic is IPv4 over the VPN tunnel.
sudo sysctl net.ipv6.conf.all.disable_ipv6=1
sudo sysctl net.ipv6.conf.default.disable_ipv6=1
sudo sysctl net.ipv6.conf.lo.disable_ipv6=1
Disabling the killswitch
To disable the killswitch, we'll provide new UFW rules. Below are standard rules for a normal user, which allow all outgoing connections and deny incoming connections. You may need different rules.
sudo ufw reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
Re-enabling IPv6
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
sudo sysctl net.ipv6.conf.lo.disable_ipv6=0
Automating it
killswitch-enable
To make this easier, we'll make a script which enables the killswitch and disables IPv6. Open a file at /usr/local/bin/killswitch-enable. I'll use nano for this:
sudo nano /usr/local/bin/killswitch-enable
Paste in this code:
#!/bin/sh ufw reset ufw default deny incoming ufw default deny outgoing ufw allow out on tun0 from any to any ufw allow out to 192.168.1.0/24 ufw enable sysctl net.ipv6.conf.all.disable_ipv6=1 sysctl net.ipv6.conf.default.disable_ipv6=1 sysctl net.ipv6.conf.lo.disable_ipv6=1
Again, replace tun0 and the local subnet if applicable.
Save the file (in nano, Ctrl+s) and exit (in nano, Ctrl+x).
Now, we need to make the file executable.
sudo chmod +x /usr/local/bin/killswitch-enable
killswitch-disable
Now, we'll make a script which disables the killswitch and enables IPv6. Open a file at /usr/local/bin/killswitch-disable. Again, I'll use nano:
sudo nano /usr/local/bin/killswitch-disable
Paste in this code:
#!/bin/sh ufw reset ufw default deny incoming ufw default allow outgoing ufw enable sysctl net.ipv6.conf.all.disable_ipv6=0 sysctl net.ipv6.conf.default.disable_ipv6=0 sysctl net.ipv6.conf.lo.disable_ipv6=0
Save the file (in nano, Ctrl+s) and exit (in nano, Ctrl+x).
Now, we need to make the file executable.
sudo chmod +x /usr/local/bin/killswitch-disable
Using it
Now, after you connect to your VPN, you can run:
sudo killswitch-enable
After you disconnect from your VPN, run:
sudo killswitch-disable
Note that both of these scripts must be run as root.
One thing to know is that if your VPN disconnects, your killswitch will prevent it from reconnecting (because that would require traffic outside the VPN tunnel). In this event, make sure to stop all processes that will cause network traffic you don't want outside your VPN. Then, disable the killswitch, reconnect to your VPN, and enable the killswitch again.