Title: WireGuard and Linux network namespaces | |
Author: Solène | |
Date: 02 July 2024 | |
Tags: security vpn network linux | |
Description: In this article, you will learn how to establish a VPN in | |
a dedicated network namespace on Linux | |
# Introduction | |
This guide explains how to setup a WireGuard tunnel on Linux using a | |
dedicated network namespace so you can choose to run a program on the | |
VPN or over clearnet. | |
I have been able to figure the setup thanks to the following blog post, | |
I enhanced it a bit using scripts and sudo rules. | |
Mo Ismailzai's blog: Creating WireGuard jails with Linux network namespaces | |
# Explanations | |
By default, if you connect WireGuard tunnel, its "allowedIps" field | |
will be used as a route with a higher priority than your current | |
default route. It is not always ideal to have everything routed | |
through a VPN, so you will create a dedicated network namespace that | |
uses the VPN as a default route, without affecting all other software. | |
Unfortunately, compared to OpenBSD rdomain (which provide the same | |
features in this situation), network namespaces are much more | |
complicated to deal with and requires root to run a program under a | |
namespace. | |
You will create a SAFE sudo rule to allow your user to run commands | |
under the new namespace, making it more practical for daily use. | |
# Setup | |
## VPN tunnel and namespace | |
You need a wg-quick compatible WireGuard configuration file, but do not | |
make it automatically used at boot. | |
Create a script (for root use only) with the following content, then | |
make it executable: | |
```shell | |
#!/bin/sh | |
# your VPN configuration file | |
CONFIG=/etc/wireguard/my-vpn.conf | |
# this directory is used to have a per netns resolver file | |
mkdir -p /etc/netns/vpn/ | |
# cleanup any previous VPN in case you want to restart it | |
ip netns exec vpn ip l del tun0 | |
ip netns del vpn | |
# information to reuse later | |
DNS=$(awk '/^DNS/ { print $3 }' $CONFIG) | |
IP=$(awk '/^Address/ { print $3 }' $CONFIG) | |
# the namespace will use the DNS defined in the VPN configuration file | |
echo "nameserver $DNS" > /etc/netns/vpn/resolv.conf | |
# now, it creates the namespace and configure it | |
ip netns add vpn | |
ip -n vpn link set lo up | |
ip link add tun0 type wireguard | |
ip link set tun0 netns vpn | |
ip netns exec vpn wg setconf tun0 <(wg-quick strip "$CONFIG") | |
ip -n vpn a add "$IP" dev tun0 | |
ip -n vpn link set tun0 up | |
ip -n vpn route add default dev tun0 | |
ip -n vpn add | |
# extra check if you want to verify the DNS used and the public IP assigned | |
#ip netns exec vpn dig ifconfig.me | |
#ip netns exec vpn curl https://ifconfig.me | |
``` | |
This script autoconfigure the network namespace and the VPN interface + | |
the DNS server to use. There are extra checks at the end of the script | |
that you can uncomment if you want to take a look at the public IP and | |
DNS resolver used just after connection. | |
Running this script will make the netns "vpn" available for use. | |
The command to run a program under the namespace is `ip netns exec vpn | |
your command`, it can only be run as root. | |
## Sudo rule | |
Now you need a specific rule so you can use sudo to run a command in | |
vpn netns as your own user without having to log in as root. | |
Add this to your sudo configuration file, in my example I allow the | |
user `solene` to run commands as `solene` for the netns vpn: | |
```sudoers | |
solene ALL=(root) NOPASSWD: /usr/sbin/ip netns exec vpn /usr/bin/sudo -u solene… | |
``` | |
When using this command line, you MUST use full paths exactly as in the | |
sudo configuration file, this is important otherwise it would allow you | |
to create a script called `ip` with whatever commands and run it as | |
root, while `/usr/sbin/ip` can not be spoofed by a local script in | |
$PATH. | |
If I want a shell session with the VPN, I can run the following | |
command: | |
``` | |
sudo /usr/sbin/ip netns exec vpn /usr/bin/sudo -u solene -- bash | |
``` | |
This runs bash under the netns vpn, so any command I'm running from it | |
will be using the VPN. | |
# Limitations | |
It is not a real limitation, but you may be caught by it, if you make a | |
program listening on localhost in the netns vpn, you can only connect | |
to it from another program in the same namespace. There are methods to | |
connect two namespaces, but I do not plan to cover it, if you need to | |
search about this setup, it can be done using socat (this is explained | |
in the blog post linked earlier) or a local bridge interface. | |
# Conclusion | |
Network namespaces are a cool feature on Linux, but it is overly | |
complicated in my opinion, unfortunately I have to deal with it, but at | |
least it is working fine in practice. |