Skip to content

Instantly share code, notes, and snippets.

@Necklaces
Last active June 2, 2024 05:26
Show Gist options
  • Save Necklaces/18b68e80bf929ef99312b2d90d0cded2 to your computer and use it in GitHub Desktop.
Save Necklaces/18b68e80bf929ef99312b2d90d0cded2 to your computer and use it in GitHub Desktop.
GNU/Linux UFW VPN kill switch tutorial

GNU/Linux UFW VPN kill switch tutorial

This is a quick guide for setting up a kill switch using UFW (Uncomplicated FireWall). It is assumed you are using OpenVPN and optionally Network-Manager with network-manager-openvpn.

1. (Optional) IP Addresses

Before we can start we're going to need the IP address (or the IP addresses) of your VPN so that we can whitelist those later on, write them down. They are obviously going to be different for every VPN and VPNs with multiple servers, so I'll leave this up to you.

2. Install UFW

On some systems UFW is installed and enabled by default (Ubuntu, for example). Installation procedure is going to be different for every distribution of GNU/Linux, but it's usually something like

sudo {package-manager} {install-command} ufw

3. (Optional) Add remote protocols

In step 4 we are going to enable the firewall, but if you're remotely connected to the machine, this might kick you out of it. In order to ensure we're not kicked out we have to add the protocol rules before we enable UFW.

SSH:

sudo ufw allow 22/tcp

VNC:

sudo ufw allow 5901:5910/tcp

4. Enable UFW

sudo ufw enable

5. Block All Traffic

Block all outgoing traffic:

sudo ufw default deny outgoing

And also block all incoming traffic:

sudo ufw default deny incoming

6. Make an exception for OpenVPN

It is assumed you are using TUN as a network adapter (if you're unsure you most definitely are). Allow outgoing traffic on tun0:

sudo ufw allow out on tun0 from any to any

And optionally allow incoming traffic on tun0 (if you're a seeder, for example):

sudo ufw allow in on tun0 from any to any

Choose 7.A. or 7.B. depending on your VPN situation

7.A. (Optional) Make an exception for your VPN with a static address or range

At this point you're technically done, but with this setup you would need to disable UFW every time OpenVPN needed to connect to your VPN and then re-enable UFW when it has connected. Instead of doing that you could add the IP addresses mentioned earlier as exceptions to UFW.

To add a single IP:

sudo ufw allow out from any to 123.123.123.123

To add a range, use a mask:

sudo ufw allow out from any to 123.123.123.0/24

Go to step 8.

7.B.1 (Optional) Make another exception for OpenVPN

If you didn't follow 7.A, and your VPN service changes/rotates IP addresses you will at least need to allow OpenVPN to somehow communicate to the outside:

sudo ufw allow out 1198/udp
sudo ufw allow in 1198/udp

Here, 1198 is the port number that OpenVPN uses, but be careful as default is actually 1194, you might have to check your VPN configuration files (the line that begins with remote {server} {port} ... or a line with rport {port}) for the actual port number used. Might also need to add the same rules for tcp.

This will allow you to at least disable ufw, connect to your VPN, and then enable ufw again to turn the kill switch back on. You will, however, have to do this every time you want to connect or if you're disconnected, which isn't entirely desirable.

7.B.2 (Optional) Force OpenVPN to use a specific port when authenticating to allow reconnecting

By default, OpenVPN will use a random port when connecting to the VPN. Replace the nobind option from your VPN configuration files with bind to force OpenVPN to use the desired port (1194 by default), and add the desired port (for example port 1198). But beware; this won't work on a system with multiple VPN clients on the same host, e.g. it will only work if you connect to one VPN at a time (unless you specifically bind different ports for different VPNs, of course, but you need to be aware of this).

Example; replace nobind in a /etc/openvpn/client/{name}.conf with:

local 0.0.0.0
lport 1198
bind

The local option is required (trivia: because "the C API" - bind() always takes an address and a port number, so you can't just bind to an address alone.)

There's a high possibility openvpn will try to resolve a host address, in that case add a rule for DNS:

sudo ufw allow out 53
sudo ufw allow in 53

8. Make sure UFW starts on boot

Systems with systemd can use

sudo systemctl enable ufw

9. Reboot and check that it's working

sudo ufw status

And test your internet connection!

10. You're done!

Congratulations, you've configured a VPN Kill switch on your GNU/Linux system!

Appendix

Help! I screwed up!

Disable ufw (also remember disable on boot, if you used systemd)

sudo ufw disable

Clear all rules:

sudo ufw reset

Thanks to:

@marathone
Copy link

Thanks for this! The above won't work for me unless I enable outgoing as default. Following your instructions to the 'T' didn't seem to work for me. My rules:
To Action From


Anywhere on tun0 ALLOW Anywhere
Anywhere (v6) on tun0 ALLOW Anywhere (v6)
Anywhere ALLOW OUT Anywhere on tun0
198.144.157.54 ALLOW OUT Anywhere
199.189.26.122 ALLOW OUT Anywhere
159.203.4.110 ALLOW OUT Anywhere
184.75.223.90 ALLOW OUT Anywhere
Anywhere (v6) ALLOW OUT Anywhere (v6) on tun0

@formeroosid
Copy link

formeroosid commented Feb 4, 2019

You probably need to allow your VPN connections to the outside world. The IP address solution above won't work if your VPN endpoint changes addresses. I would recommend just allowing the ports.

For OpenVPN, you would need the following:
sudo ufw allow out 1198/udp
sudo ufw allow in 1198/udp

@hrvstr
Copy link

hrvstr commented Feb 26, 2019

Step 5 does not seem to work for me. I have to disable the firewall to reconnect to my VPN.

You probably need to allow your VPN connections to the outside world. The IP address solution above won't work if your VPN endpoint changes addresses. I would recommend just allowing the ports.

For OpenVPN, you would need the following:
sudo ufw allow out 1198/udp
sudo ufw allow in 1198/udp

Also tried this without any luck.

@Necklaces
Copy link
Author

Hey guys, sorry I didn't see this until now, not getting notifications for gists apparently. I've updated the gist a bit, thanks for your input!

@nodecentral
Copy link

nodecentral commented Feb 21, 2020

Hi, I have my vpn running as a VM on my NAS , and I don’t want to break any controls/connections the NAS might need, plus I’d still like to be able to access it locally via ssh.

Being relatively new to Linux (Debian 10 specifically) how best can I see all the ports that are currently in use today ? That way I can have a list to add ones back in when/if needed ?

Also if I am connecting to the virtual machine over VNC, am I correct in saying if I do step 3 I will lose all connectivity ? If so, Is there a way to do this without losing connectivity ?

@Necklaces
Copy link
Author

Necklaces commented Feb 24, 2020

@nodecentral

how best can I see all the ports that are currently in use today?

I recommend using nmap to scan for ports in use:

sudo nmap -sTU localhost

If you're scanning from another machine you need to switch localhost with the address of the machine.

There are other ways, too. On older machines netstat and newer machines ss, but the outputs of those commands are not very beginner friendly, IMO. Nmap will give you a nice simple output:

Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-24 08:27 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00015s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 1337 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
666/udp  open  satand

Then just add the ports/protocol using ufw, for example

sudo ufw allow 666/udp

I am connecting to the virtual machine over VNC, am I correct in saying if I do step 3 I will lose all connectivity ?

I believe that is true, yes. In order to go around it without losing connectivity, simply insert a step 2.5 that allows remote traffic with the protocols you're using, before you block everything else.

For SSH:

sudo ufw allow 22/tcp

For VNC (it seems VNC uses many different ports, you might have to enable more than just these):

sudo ufw allow 5901:5910/tcp

You should probably also read about VNC security, as it doesn't really have a good rep.

@nodecentral
Copy link

Many thanks @Necklaces

A few more quick things

  1. Rather than specify individual ports ? If I trust my LAN, would it be best to add that subnet to the firewall rule ? (Would that all local IPs/Ports then ?)

  2. Can I specify my individual ufw rules first - before doing step 5 the deny/blocks all ? If the permit rules are added first in sequence, would they be preserved ?

  3. Regarding 7A, It looks like I only have domain names - not an IP address - for my VPN provider. How best can I add or facilitate that within a rule ? (I tried adding the url directly- but ufw didn’t like it/returned an error)

** sorry for all the questions - it’s just that the machine I’m looking to apply these rules to is at a remote site ; so I wouldn’t want to risk losing connectivity. **

@Necklaces
Copy link
Author

No problem, @nodecentral, these are interesting questions. Do note I am not really an expert.

  1. Sure, if you trust your LAN then go ahead and add the subnet: sudo ufw allow from 10.0.2.0/24 This should indeed allow incoming traffic to all ports from the LAN (worked sshing into a VM that only had deny all and the aforementioned allow from LAN, at least).

  2. Yep, go right ahead. All rules are preserved as they are added, you can check as you go with sudo ufw show added.

  3. It might be better to see if you need to go for 7B, but if you expect to always get the same IP from that domain you can just add it using the IP you get from pinging it: ping www.google.com (leave out the http / https part). Otherwise, it unfortunately seems to be impossible to use a url

@plasmah77
Copy link

Thanks much for this tut! Pretty easy to follow and works perfect. Cheers!

@reinthal
Copy link

<3 exactly what I needed. Thank you. allow 53 didnt work for me, allowed my local dns server instead which worked.

@SmurgBurglar
Copy link

I got up to step 7.B.2 but there was no file in /etc/openvpn/client/

@Necklaces
Copy link
Author

Necklaces commented Jun 8, 2022

@SmurgBurglar I don't know which distro you are on, some of them use different folders. Install mlocate and run locate 'openvpn/client' to see if there's another location, you may have to run updatedb as root before doing so.

Edit: Obviously, if you haven't set up OpenVPN yet then you won't have any client files. So you should do that before following this guide. It's usually as simple as; install openvpn -> download openvpn config files from your vpn provider -> add the config files using NetworkManager network configuration. If you're using an app for your VPN without a built in killswitch I would buy a different VPN, and lastly, if you're using WireGuard which most people are nowadays - including me - then this is the wrong killswitch guide.

@wangyifan349
Copy link

wangyifan349 commented Dec 15, 2022

How to solve this problem, my openvpn domain name needs to be resolved, I set ufw allow 853 in ufw but still can't connect, the following is the firewall record
How can I solve this problem?thank you very much.

Dec 15 20:26:34 my computer kernel: [ 3708.372733] [UFW BLOCK] IN= OUT=wlp4s0 SRC=192.168.123.107 DST=1.1.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=62134 DF PROTO=TCP SPT=59952 DPT=853 WINDOW=64240 RES=0x00 SYN URGP=0
Dec 15 20:26:43 my computer kernel: [ 3717.076459] [UFW BLOCK] IN= OUT=wlp4s0 SRC=192.168.123.107 DST=1.1.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=56263 DF PROTO=TCP SPT=59954 DPT=853 WINDOW=64240 RES=0x00 SYN URGP=0
Dec 15 20:27:03 my computer kernel: [ 3737.300394] [UFW BLOCK] IN= OUT=wlp4s0 SRC=192.168.123.107 DST=1.1.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=11199 DF PROTO=TCP SPT=34680 DPT=853 WINDOW=64240 RES=0x00 SYN URGP=0
Dec 15 20:27:23 my computer kernel: [ 3757.780218] [UFW BLOCK] IN= OUT=wlp4s0 SRC=192.168.123.107 DST=1.1.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=36600 DF PROTO=TCP SPT=48052 DPT=853 WINDOW=64240 RES=0x00 SYN URGP=0
Dec 15 20:27:42 my computer kernel: [ 3776.575553] [UFW BLOCK] IN=wlp4s0 OUT= MAC=01:14:5e:00:01:23:8c:dD:f9:bf:fa:15:08:00 SRC=192.168.123.1 DST=224.0.0.1 LEN=32 TOS=0x00 PREC=0xC0 TTL=1 ID=0 DF PROTO=2
Dec 15 20:27:44 my computer kernel: [ 3778.260510] [UFW BLOCK] IN= OUT=wlp4s0 SRC=192.168.123.107 DST=1.1.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=54772 DF PROTO=TCP SPT=57178 DPT=853 WINDOW=64240 RES=0x00 SYN URGP=0

@Necklaces
Copy link
Author

Necklaces commented Dec 20, 2022

@wangyifan349 Sorry for the late reply.

Judging from the logs you posted your ufw seems to be blocking outgoing connections to 853.

If you only did ufw allow 853 that only added rules to allow incoming connections to 853, not outgoing, so to fix your issue you will have to run:

sudo ufw allow out 853

@aagontuk
Copy link

aagontuk commented Feb 7, 2024

Hi, I am trying to set this up in my raspberry pi. I have done the following in order:

sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw default deny outgoing
sudo ufw default deny incoming
sudo ufw allow out on tun0 from any to any
sudo ufw allow in on tun0 from any to any
sudo systemctl enable ufw
sudo reboot

But after reboot I can't ssh to my pi even if I have allowed port 22.

EDIT: I think the problem is with denying outgoing connection. If I execute sudo ufw default deny outgoing, then I can't ssh anymore.

@hrvstr
Copy link

hrvstr commented Feb 7, 2024

But after reboot I can't ssh to my pi even if I have allowed port 22.

I think you need to add an exception for your LAN.

ufw allow in to 192.168.178.0/24
ufw allow out to 192.168.178.0/24

@Necklaces
Copy link
Author

@aagontuk This is sort of the same problem as wang had above.

ufw allow 22/tcp basically means ufw allow in 22/tcp, meaning it will only allow incoming connections. It was added in case you're setting up a server. While you say you're setting these up on your Pi, after testing on a VM these rules would definitely work if you put them on a Pi and then SSH-ed into it from a different non-firewall blocked machine. What I suspect is that you're setting up these rules on your main machine and then trying to access your Pi on the local network. This killswitch blocks local network access.

If you trust your entire local network you could use the suggestion from @hrvstr (I think only outgoing is necessary).

If you don't care about SSH potentially not going through your VPN you could add ufw allow out 22/tcp.

I have to stress though: you will need ufw default deny outgoing, otherwise it's not a killswitch. Do not omit this rule.

@aagontuk
Copy link

aagontuk commented Feb 8, 2024

@hrvstr @Necklaces Thank you so much for the pointers.

The problem was with DNS. By default RPI supports mDNS. I was trying to access pi through its DNS host name: ssh username@hostname. Later I noticed If I use raw IP instead of hostname everything works as expected. So I have allowed mDNS port 5353 in ufw and now everything is working just fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment