Firewalld VPN killswitch

Firewalld VPN killswitch

We are going to use policies and zones to create a VPN kill switch and tell NetworkManager to automatically activate it for certain interfaces.

Firewalld 1.0.0 supports filtering outgoing traffic with policies. It became much more powerful and I might end up liking it better than UFW now.

Quick introduction to firewalld:

Zones: firewalld objects that can have interfaces assigned to it.

Policies: firewalld objects that regulate traffic flowing between zones. Policies have ingress zones (where the traffic is coming from) and egress zones (where the traffic is going to).

Rich rules: complicated rules that almost look like IPtables rules but still simpler.

+----------+    ESTABLISHED   +----------+
|          |  <-------------  |          |
|   HOST   |                  | VPN-Only |
|          |  ------------->  |          |
+----------+  VPN-Killswitch  +----------+

Here HOST is a symbolic zone representing traffic coming from this machine. VPN-Only is a zone we will create, it will contain the internet facing network cards. VPN-Killswitch is a policy that only allows outgoing traffic to the VPN servers and local network. ESTABLISHED is the default firewalld policy of allowing traffic back in.

Create the VPN-Only zone:

firewall-cmd --permanent --new-zone VPN-Only

Default target for the Zone (DROP means ignore everything we don’t explicit allow, the default is REJECT which rejects with a polite message):

firewall-cmd --permanent --zone VPN-Only --set-target DROP

Create the VPN-Killswtich policy:

firewall-cmd --permanent --new-policy VPN-Killswitch

Default target for the policy to DROP:

firewall-cmd --permanent --policy VPN-Killswitch --set-target DROP

Reload to apply the changes:

firewall-cmd --reload

Now let’s add the policy rules that allow outbound traffic to the VPN IPs. Repeat this rule for every region replacing 1.2.3.4 with your VPN’s IP. Change openvpn to wireguard if that’s what you are using.

firewall-cmd --policy VPN-Killswitch --add-rich-rule='rule family="ipv4" destination address="1.2.3.4" service name="openvpn" accept'

Allow outgoing traffic to the local network:

firewall-cmd --policy VPN-Killswitch --add-rich-rule='rule family="ipv4" destination address="192.168.1.0/24" accept'

Set the ingress zone:

firewall-cmd --policy VPN-Killswitch --add-ingress-zone HOST

And the egress zone:

firewall-cmd --policy VPN-Killswitch --add-egress-zone VPN-Only

Now we only need to add our networks cards to the VPN-Only zone. This can be done through the NetworkManager GUI (or CLI) or through firewalld.

With NetworkManager open the properties window for the connection you want to filter and on the tab “General configuration” set “Firewall zone” to “VPN-Only”. If you want to turn off the killswitch simply change the connection zone to “Public” or something else.

Or with firewalld:

firewall-cmd --permanent --zone VPN-Only --add-interface=enp0s8

Testing the setup

Activate the connection you associated with the “VPN-Only” zone and disable the VPN. Try to ping any IP outside the local network and it won’t work.

Make the changes permanent:

firewall-cmd --runtime-to-permanent

Notes from the future

If the IP of your VPN ever change, remove the old rich rule with:

firewall-cmd --policy VPN-Killswitch --remove-rich-rule='rule family="ipv4" destination address="1.2.3.4" service name="openvpn" accept'

References: