WireGuard VPN – Troubleshooting
Next, we create a WireGuard interface in the “init” (original) namespace:
Torguards ip checker
Like all Linux network interfaces, WireGuard integrates into the network namespace infrastructure. This means an administrator can have several entirely different networking subsystems and choose which interfaces live in each.
WireGuard does something quite interesting. When a WireGuard interface is created (with ip link add wg0 type wireguard ), it remembers the namespace in which it was created. “I was created in namespace A.” Later, WireGuard can be moved to new namespaces (“I’m moving to namespace B.”), but it will still remember that it originated in namespace A.
WireGuard uses a UDP socket for actually sending and receiving encrypted packets. This socket always lives in namespace A – the original birthplace namespace. This allows for some very cool properties. Namely, you can create the WireGuard interface in one namespace (A), move it to another (B), and have cleartext packets sent from namespace B get sent encrypted through a UDP socket in namespace A.
(Note that this same technique is available to userspace TUN-based interfaces, by creating a socket file-descriptor in one namespace, before changing to another namespace and keeping the file-descriptor from the previous namespace open.)
This opens up some very nice possibilities.
Ordinary Containerization
The most obvious usage of this is to give containers (like Docker containers, for example) a WireGuard interface as its sole interface.
container # ip addr 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 17: wg0: mtu 1423 qdisc noqueue state UNKNOWN group default qlen 1 link/none inet 192.168.4.33/32 scope global wg0 valid_lft forever preferred_lft forever
Here, the only way of accessing the network possible is through wg0 , the WireGuard interface.
The way to accomplish a setup like this is as follows:
First we create the network namespace called “container”:
# ip netns add container
Next, we create a WireGuard interface in the “init” (original) namespace:
# ip link add wg0 type wireguard
Finally, we move that interface into the new namespace:
# ip link set wg0 netns container
Now we can configure wg0 as usual, except we specify its new namespace in doing so:
# ip -n container addr add 192.168.4.33/32 dev wg0 # ip netns exec container wg setconf wg0 /etc/wireguard/wg0.conf # ip -n container link set wg0 up # ip -n container route add default dev wg0
And voila, now the only way of accessing any network resources for “container” will be via the WireGuard interface.
Note that Docker users can specify the PID of a Docker process instead of the network namespace name, to use the network namespace that Docker already created for its container:
# ip link set wg0 netns 879
Routing All Your Traffic
A less obvious usage, but extremely powerful nonetheless, is to use this characteristic of WireGuard for redirecting all of your ordinary Internet traffic over WireGuard. But first, let’s review the old usual solutions for doing this:
The Classic Solutions
The classic solutions rely on different types of routing table configurations. For all of these, we need to set some explicit route for the actual WireGuard endpoint. For these examples, let’s assume the WireGuard endpoint is demo.wireguard.com , which, as of writing, resolves to 163.172.161.0 . Further, let’s assume we usually connect to the Internet using eth0 and the classic gateway of 192.168.1.1 .
Replacing The Default Route
The most straightforward technique is to just replace the default route, but add an explicit rule for the WireGuard endpoint:
# ip route del default # ip route add default dev wg0 # ip route add 163.172.161.0/32 via 192.168.1.1 dev eth0
This works and is relatively straightforward, but DHCP daemons and such like to undo what we’ve just did, unfortunately.
Overriding The Default Route
So, instead of replacing the default route, we can just override it with two more specific rules that add up in sum to the default, but match before the default:
# ip route add 0.0.0.0/1 dev wg0 # ip route add 128.0.0.0/1 dev wg0 # ip route add 163.172.161.0/32 via 192.168.1.1 dev eth0
This way, we don’t clobber the default route. This also works quite well, though, unfortunately when eth0 goes up and down, the explicit route for demo.wireguard.com will be forgotten, which is annoying.
Rule-based Routing
Some folks prefer to use rule-based routing and multiple routing tables. The way this works is we create one routing table for WireGuard routes and one routing table for plaintext Internet routes, and then add rules to determine which routing table to use for each:
# ip rule add to 163.172.161.0 lookup main pref 30 # ip rule add to all lookup 80 pref 40 # ip route add default dev wg0 table 80
Now, we’re able to to keep the routing tables separate. Unfortunately the downside is that explicit endpoint rules still need to be added, and there’s no cleanup when the interface is removed, and more complicated routing rules now need to be duplicated.
Improved Rule-based Routing
The prior solution relies on us knowing the explicit endpoint IP that should be exempt from the tunnel, but WireGuard endpoints can roam, which means this rule may go stale. Fortunately, we are able to set an fwmark on all packets going out of WireGuard’s UDP socket, which will then be exempt from the tunnel:
# wg set wg0 fwmark 1234 # ip route add default dev wg0 table 2468 # ip rule add not fwmark 1234 table 2468 # ip rule add table main suppress_prefixlength 0
We first set the fwmark on the interface and set a default route on an alternative routing table. Then we indicate that packets that do not have the fwmark should go to this alternative routing table. And finally we add a convenience feature for still accessing the local network, whereby we allow packets without the fwmark to use the main routing table, not the WireGuard interface’s routing table, if it matches any routes in it with a prefix length greater than zero, such as non-default local routes. This is the technique used by the wg-quick(8) tool.
Improving the Classic Solutions
The WireGuard authors are interested in adding a feature called “notoif” to the kernel to cover tunnel use cases. This would allow interfaces to say “do not route this packet using myself as an interface, to avoid the routing loop”. WireGuard would be able to add a line like .flowi4_not_oif = wg0_idx , and userspace tun -based interfaces would be able to set an option on their outgoing socket like setsockopt(fd, SO_NOTOIF, tun0_idx); . Unfortuantely this hasn’t yet been merged, but you can read the LKML thread here.
The New Namespace Solution
It turns out that we can route all Internet traffic via WireGuard using network namespaces, rather than the classic routing table hacks. The way this works is that we move interfaces that connect to the Internet, like eth0 or wlan0 , to a namespace (which we call “physical”), and then have a WireGuard interface be the sole interface in the “init” namespace.
First we create the “physical” network namespace:
# ip netns add physical
Now we move eth0 and wlan0 into the “physical” namespace:
# ip link set eth0 netns physical # iw phy phy0 set netns name physical
(Note that wireless devices must be moved using iw and by specifying the physical device phy0 .)
We now have these interfaces in the “physical” namespace, while having no interfaces in the “init” namespace:
# ip -n physical link 1: lo: mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000 link/ether ab:cd:ef:g1:23:45 brd ff:ff:ff:ff:ff:ff 3: wlan0: mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000 link/ether 01:23:45:67:89:ab brd ff:ff:ff:ff:ff:ff # ip link 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
Now we add a WireGuard interface directly to the “physical” namespace:
# ip -n physical link add wg0 type wireguard
The birthplace namespace of wg0 is now the “physical” namespace, which means the ciphertext UDP sockets will be assigned to devices like eth0 and wlan0 . We can now move wg0 into the “init” namespace; it will still remember its birthplace for the sockets, however.
# ip -n physical link set wg0 netns 1
We specify “1” as the “init” namespace, because that’s the PID of the first process on the system. Now the “init” namespace has the wg0 device:
# ip link 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 17: wg0: mtu 1423 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/none
We can now configure the physical devices using the ordinary tools, but we launch them inside the “physical” network namespace:
# ip netns exec physical dhcpcd wlan0 # ip netns exec physical wpa_supplicant -iwlan0 -c/etc/wpa_supplicant/wpa_supplicant.conf # ip -n physical addr add 192.168.12.52/24 dev eth0
And so forth. Finally, we can configure the wg0 interface like usual, and set it as the default route:
# wg setconf wg0 /etc/wireguard/wg0.conf # ip addr add 10.2.4.5/32 dev wg0 # ip link set wg0 up # ip route add default dev wg0
Finished! At this point, all ordinary processes on the system will route their packets through the “init” namespace, which only contains the wg0 interface and the wg0 routes. However, wg0 has its UDP socket living in the “physical” namespace, which means it will send traffic out of eth0 or wlan0 . Normal processes won’t even be aware of eth0 or wlan0 , except dhcpcd and wpa_supplicant , which were spawned inside of the “physical” namespace.
Sometimes, however, you might want to open a webpage or do something quickly using the “physical” namespace. For example, maybe you plan to route all your traffic through WireGuard like usual, but the coffee shop at which you’re sitting requires you to authenticate using a website before it will give you a real Internet link. So, you can execute select processes (as your local user) using the “physical” interface:
$ sudo -E ip netns exec physical sudo -E -u \#$(id -u) -g \#$(id -g) chromium
This of course could be made into a nice function for .bashrc :
physexec()
And now you can write the following for opening chromium in the “physical” namespace.
$ physexec chromium
When you’re done signing into the coffee shop network, spawn a browser as usual, and surf calmly knowing all your traffic is protected by WireGuard:
$ chromium
Sample Script
The following example script can be saved as /usr/local/bin/wgphys and used for commands like wgphys up , wgphys down , and wgphys exec :
#!/bin/bash set -ex [[ $UID != 0 ]] && exec sudo -E "$(readlink -f "$0")" "$@" up() < killall wpa_supplicant dhcpcd || true ip netns add physical ip -n physical link add wgvpn0 type wireguard ip -n physical link set wgvpn0 netns 1 wg setconf wgvpn0 /etc/wireguard/wgvpn0.conf ip addr add 192.168.4.33/32 dev wgvpn0 ip link set eth0 down ip link set wlan0 down ip link set eth0 netns physical iw phy phy0 set netns name physical ip netns exec physical dhcpcd -b eth0 ip netns exec physical dhcpcd -b wlan0 ip netns exec physical wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant-wlan0.conf -iwlan0 ip link set wgvpn0 up ip route add default dev wgvpn0 >down() < killall wpa_supplicant dhcpcd || true ip -n physical link set eth0 down ip -n physical link set wlan0 down ip -n physical link set eth0 netns 1 ip netns exec physical iw phy phy0 set netns 1 ip link del wgvpn0 ip netns del physical dhcpcd -b eth0 dhcpcd -b wlan0 wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant-wlan0.conf -iwlan0 >execi() < exec ip netns exec physical sudo -E -u \#$SUDO_UID:-$(id -u)> -g \#$SUDO_GID:-$(id -g)> -- "$@" > command="$1" shift case "$command" in up) up "$@" ;; down) down "$@" ;; exec) execi "$@" ;; *) echo "Usage: $0 up|down|exec" >&2; exit 1 ;; esac
A small demo of the above:
© Copyright 2015-2022 Jason A. Donenfeld. All Rights Reserved. “WireGuard” and the “WireGuard” logo are registered trademarks of Jason A. Donenfeld.
This project is from ZX2C4 and from Edge Security, a firm devoted to information security research expertise.
WireGuard VPN – Troubleshooting
It can be helpful to leave a terminal open with the watch wg command. Here is a sample output showing a system with two peers configured, where only one has established the VPN so far:
Every 2.0s: wg j-wg: Fri Aug 26 17:44:37 2022 interface: wg0 public key: +T3T3HTMeyrEDvim8FBxbYjbz+/POeOtG3Rlvl9kJmM= private key: (hidden) listening port: 51000 peer: 2cJdFcNzXv4YUGyDTahtOfrbsrFsCByatPnNzKTs0Qo= endpoint: 10.172.196.106:51000 allowed ips: 10.10.11.2/32 latest handshake: 3 hours, 27 minutes, 35 seconds ago transfer: 3.06 KiB received, 2.80 KiB sent peer: ZliZ1hlarZqvfxPMyME2ECtXDk611NB7uzLAD4McpgI= allowed ips: 10.10.11.3/32
Kernel debug messages
WireGuard is also silent when it comes to logging. Being a kernel module essentially, we need to explicitly enable verbose logging of its module. This is done with the following command:
$ echo "module wireguard +p" | sudo tee /sys/kernel/debug/dynamic_debug/control
This will write WireGuard logging messages to the kernel log, which can be watched live with:
$ sudo dmesg -wT
To disable logging, run this:
$ echo "module wireguard -p" | sudo tee /sys/kernel/debug/dynamic_debug/control
Destination address required
If you ping an IP and get back an error like this:
$ ping 10.10.11.2 PING 10.10.11.2 (10.10.11.2) 56(84) bytes of data. From 10.10.11.1 icmp_seq=1 Destination Host Unreachable ping: sendmsg: Destination address required
This is happening because the WireGuard interface selected for this destination doesn’t know the endpoint for it. In other words, it doesn’t know where to send the encrypted traffic.
One common scenario for this is on a peer where there is no Endpoint configuration, which is perfectly valid, and the host is trying to send traffic to that peer. Let’s take the coffee shop scenario we described earlier as an example.
The laptop is connected to the VPN and exchanging traffic as usual. Then it stops for a bit (the person went to get one more cup). Traffic ceases (WireGuard is silent, remember). If the WireGuard on the home router is now restarted, when it comes back up, it won’t know how to reach the laptop, because it was never contacted by it before. This means that at this time, if the home router tries to send traffic to the laptop in the coffee shop, it will get the above error.
Now the laptop user comes back, and generates some traffic to the home network (remember: the laptop has the home network’s Endpoint value). The VPN “wakes up”, data is exchanged, handshakes completed, and now the home router knows the Endpoint associated with the laptop, and can again initiate new traffic to it without issues.
Another possibility is that one of the peers is behind a NAT, and there wasn’t enough traffic for the stateful firewall to consider the “connection” alive, and it dropped the NAT mapping it had. In this case, the peer might benefit from the PersistentKeepalive configuration, which makes WireGuard send a keepalive probe every so many seconds.
Required key not available
$ ping 10.10.11.1 PING 10.10.11.1 (10.10.11.1) 56(84) bytes of data. From 10.10.11.2 icmp_seq=1 Destination Host Unreachable ping: sendmsg: Required key not available
Can happen when you have a route directing traffic to the WireGuard interface, but that interface does not have the target address listed in its AllowedIPs configuration.
If you have enabled kernel debugging for WireGuard, you will also see a message like this one in the dmesg output:
wireguard: home0: No peer has allowed IPs matching 10.10.11.1