| 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. |