📜 ⬆️ ⬇️

Kill switch for OpenVPN based on iptables

It is known that when you connect to open Wi-Fi networks, your traffic can be easily listened to. Of course, now more and more sites are using HTTPS. However, this is still far from 100%. There is a natural desire to protect your traffic when connected to such open Wi-Fi networks.

A popular solution to this problem is to connect via VPN. In this case, your traffic is transmitted in encrypted form to the VPN server, and from there it goes to the Internet.

This solution has a slight drawback: while the VPN connection has not yet been established, all applications on your computer (including open browser tabs) access the Internet bypassing the VPN connection.
')
In this article I will tell you how to avoid this.

Idea


How will we solve this problem?

In general terms, we want to block all access to the network to all applications with two exceptions:We will do this with iptables.

If the first point is solved very simply, the second raises questions. In iptables, you cannot write rules that would match the name of the process.

You can allow in iptables access to a hard-coded IP of the VPN server to all processes (assuming that no other connections are made over this IP). This solution is bad because we will not be able to connect to the server via the hostname. In addition, there are problems with the captive portal. If some access point requires you to go to the web page and click “I agree” there, you will have to manually set an exception. Therefore, this solution is far from ideal.

One solution is based on the use of groups. iptables can match packets by the GID of the process that sent these packets. This solution is very simple and effective. But if some process suddenly wants to change its GID, it will immediately lose access to the Internet.

The second option is to use cgroups. We can create a special cgroup and put there processes that need free access to the Internet, and all packages sent by such processes will be marked with a label on which to match them in iptables. The advantage of this approach is that there is no need to change the process group that can be used for something else. You only need to assign a specific cgroup to the net_cls subsystem. Plus, in contrast to the variant with regular groups, the process can be transferred from one cgroup to another “on the fly”, without restarting. Disadvantages of the method: more complex setup, requires recently released iptables v1.6.0 (which has not yet been added to most distributions).

I will in more detail consider the option with regular groups, and at the end of the post I will briefly describe how this can be done with cgroups.

Requirements


It is assumed that you already have a VPN based on OpenVPN and you are able to pass all traffic through it (for example, using the redirect-gateway def1 option).

We will also assume that you have a fairly fresh kernel with CONFIG_NETFILTER_XT_MATCH_OWNER and iptables.

Iptables configuration


The first step is to create a special group. Let's call it killswitch.
groupadd --system killswitch 

Now add iptables rules:
 iptables -A OUTPUT -m owner --gid-owner killswitch -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited 
What is going on here?
  1. Packets sent by processes with GID killswitch are passed to the network.
  2. Packets on the tun0 interface are passed through unconditionally. This is the same network interface that is implemented over the VPN connection. If your network interface is called differently (the dev option in the OpenVPN config allows you to give it a fixed name instead of tunN), change it in the iptables rules above.
  3. Packages on the lo interface are also skipped. lo is the loopback interface on which the well-known 127.0.0.1 (localhost) resides. Since some applications use localhost for communication between processes, it is undesirable to block it.
  4. All other packages are blocked. Blocking occurs when sending the ICMP packet “administratively prohibited” (the error code does not play a significant role). This is better than just dropping packets, because in this case, the program will immediately receive an error, and not hang until the timeout.

If everything goes right, at this moment you should have no access to the Internet.

To run a program with Internet access, you can use the sg utility (and if you add your user to this group, you can call it without sudo). Let me remind you that until this group becomes an effective GID, the process will not have access to the Internet without VPN. iptables does not check supplementary GIDs!
 sg killswitch 'ping ya.ru' 
Now all that is left is to run an OpenVPN client inside with sg.
 sg killswitch 'openvpn config.ovpn' 
Everything! From this point on, you have to earn the Internet in all other programs.

Bonus: captive portals


What to do if, for access to the Internet, you first need to go to the page in the browser and click “agree” there, such as in the Moscow metro, for example?

This problem is easily solved. Just as the OpenVPN client runs, you can run any other application.

So, for such cases I have a special Firefox profile in perpetual private mode. If you run it via sg killswitch, it will also get access to the Internet without VPN.
 sg killswitch 'firefox -P my_special_profile_name -no-remote' 

Bonus: cgroups


The cgroups solution requires iptables v1.6.0 and kernels with the CONFIG_NETFILTER_XT_MATCH_CGROUP option.
 cgcreate -g net_cls:killswitch #  cgroup killswitch   net_cls echo 0x00100001 > /sys/fs/cgroup/net_cls/killswitch/net_cls.classid #  ,     (10:1) chmod 666 /sys/fs/cgroup/net_cls/killswitch/tasks #        cgroup iptables -A OUTPUT -m cgroup --cgroup 0x00100001 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited cgexec -g net_cls:killswitch ping ya.ru cgexec -g net_cls:killswitch sudo openvpn config.ovpn cgexec -g net_cls:killswitch firefox -P my_special_profile_name -no-remote 
The following commands allow you to give access to the Internet without VPN Firefox, and then take it back:
 pgrep -w firefox | xargs cgclassify -g net_cls:killswitch pgrep -w firefox | xargs sudo cgclassify -g net_cls:/ 

Bonus: xtables-addons condition


Sitting through a VPN at home or at work may not be a particular need. -m condition --condition killswitch from xtables-addons, added to the last iptables rule (which is with REJECT), can make the killswitch easily switchable via echo <0|1> > /proc/net/nf_condition/killswitch.

Conclusion


The article reviewed two approaches to the implementation of the killswitch using iptables: through normal groups and through cgroups. Both options work very flexibly, allowing, if necessary, to give arbitrary processes access to the Internet without VPN. Both approaches are almost equivalent, but the way through cgroups is a bit more difficult to configure, it requires a very fresh iptables, but it allows you to issue and withdraw access right during the process.

Source: https://habr.com/ru/post/274445/


All Articles