Tagged in: vpn

I’ve had enough of paying for 1Gbps fiber connection and only using 100Mbps because of using my server at Kimsufi as my main gate to the internet.

The goal is now to use my VPN connection to protect my websurf only (no webmaster needs to get my home IP). The rest is more or less not-as-private, and will be using my ISP’s (high bandwidth) connection (games, GPG-signed updates). This usage differs from other examples that make seldom use of the physical realm: here we will use the physical connection by default, and “containing” specific applications.

Our objective, see the digraph’s source at sourcehut

You should probably be up to speed with what WireGuard is and how it works: see e.g. a previous entry.

§ steps

§ manual

Create an empty netns and populate it with lo.

ip netns add vpn
ip -n vpn link set lo up

The wg1 interface’s “place of birth” is the physical realm. Thus, we first need to create wg1 in the unnamed namespace before moving it to the vpn netspace. This is needed because wg1 will send (encrypted) packets to the server through a real interface. If we create wg1 in the vpn namespace, WireGuard won’t know how to reach the outside world (including the WireGuard server).

ip link add wg1 type wireguard
ip link set wg1 netns vpn

Then we use wg(8) and ip(8) to setup our VPN link

ip netns exec vpn wg setconf wg1 /etc/wireguard/wg1.conf
ip -n vpn a add 10.X.Y.PPP/24 dev wg1
ip -n vpn a add aaaa::ffff/64 dev wg1
ip -n vpn link set wg1 up
ip -n vpn route add default dev wg1
ip -n vpn -6 route add default dev wg1

Don’t forget to put a resolv.conf(5) file in /etc/netns/vpn as written in ip-netns(8)’ manpage. Now, testing time:

for v in 4 6; do
 printf '%s' "default IPv$v: " ; curl -s$v https://icanhazip.com
 printf '%s' "VPN IPv$v:     " ; ip netns exec vpn curl -s$v https://icanhazip.com

It is not yet possible to use systemd.networkd(5) to set everything up correctly, though there seems to be ongoing work on that front; let’s hope it gets a bit more attention than my own bug report to systemd.

§ usage

% ip netns exec vpn ping -c1 try.popho.be
setting the network namespace "vpn" failed: Operation not permitted

ip netns can’t run as user. I can’t be bothered to install and configure sudo properly, so we’ll rely on yet another piece of software instead: firejail(1)

% firejail --noprofile --netns=vpn firefox

firejail does its job, but this is a pain to type each time. Instead, we’ll wrap firefox in a short script.

We put run-with-vpn in one of the first items of $PATH (in ~/bin for example):


# Aptly linking this script allows us to run any program in a network namespace
# Removing the first item of PATH revokes the precedence of our script, and we
# SHOULD end up using firejail on the REAL binary we target.
# We suppose that firejail does NOT reside next to our script, but further away
# in $PATH

if [ -e /var/run/netns/vpn ] ; then # Network NameSpace exists
  if command -v firejail >/dev/null 2>&1 ; then
    PATH=${PATH#*:} firejail --noprofile --netns=vpn "$(basename $0)" "$@" &
  notify-send "NO VPN" "$(basename $0) is running with NO VPN"
  PATH=${PATH#*:} "$(basename $0)" "$@" &

Now, we create a symlink firefox to run-with-vpn:

% cd ~/bin
% ln -s run-with-vpn firefox
% command -v firefox
% sh -x "$(!!)"
+ '[' -e /var/run/netns/vpn ']'
+ command -v firejail
++ basename /home/moviuro/Documents/setup/bin/firefox
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/home/moviuro/.local/share/flatpak/exports/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
+ firejail --noprofile --netns=vpn firefox

With this, we can simply launch firefox from our shell or rofi, and it’ll be run in our VPN NetNS.

§ automatic with a systemd.service(5)

Description=Start a VPN Network Namespace

ExecStart=/usr/bin/ip netns add vpn
ExecStart=/usr/bin/ip -n vpn link set lo up
ExecStart=/usr/bin/ip link add wg1 type wireguard
ExecStart=/usr/bin/ip link set wg1 netns vpn
ExecStart=/usr/bin/ip netns exec vpn wg setconf wg1 /etc/wireguard/wg1.conf
ExecStart=/usr/bin/ip -n vpn a add 10.X.Y.PPP/24 dev wg1
ExecStart=/usr/bin/ip -n vpn a add aaaa::ffff/64 dev wg1
ExecStart=/usr/bin/ip -n vpn link set wg1 up
ExecStart=/usr/bin/ip -n vpn route add default dev wg1
ExecStart=/usr/bin/ip -n vpn -6 route add default dev wg1
ExecStop=/usr/bin/ip netns delete vpn


§ side-effects

The software that runs in a seperate NetNS can’t possibly leak your other addresses (so, that’s cool if you use any modern browser shipping far too many “features” such as: WebRTC…).

My desktop now has one IP that will only do websurf: TCP/80 and 443, UDP 443 and 53; I can filter even more what is allowed outbound in pf.

My machine is no longer blocked by my server’s pf if it tries to initiate connection to known-bad IP addresses (C&C, SpamHaus’ DROP list, etc.).

§ conclusion

I achieved what I set out to do, however I’m a bit dissatisfied about having two VPN interfaces (wg0 and wg1) connecting to the same VPN on the same machine, and the tooling is far from perfect:

Situation in the end, see source here