Wireguard roaming host routing

Posted by Ian Maxon on February 18, 2020

For a long time, I used OpenVPN when I needed to connect to my home network, and for the most part it worked well. However OpenVPN is stateful; it needs to keep the connection alive to the host. That means it has to be reconnected if it disconnects due to a network change, and this can get annoying.

IPSec doesn’t have this problem, but it is a total bear to set up correctly. Recently a new VPN sofware has come to the fore called Wireguard and it is the best of both worlds. Simpler to set up than even OpenVPN, behaves similarly to IPSec in that it is in the kernel and connectionless, and also very fast.

Setting up wireguard was very easy, and the documentation on wg-quick is extremely clear. However I ran into an issue upon roaming that was perplexing to me. Wireguard would work great while outside my home network, but would stall and freeze out my connection when I came back home.

My config was as follows:

[Interface]
Address = 192.168.27.2/24
PrivateKey = averysecretkey
ListenPort = 51821

[Peer]
PublicKey = Hvc2OKVDzSd0lVnt9fDdBIPv9xiu/CdiZCO63j2nSTc=
AllowedIPs = 192.168.27.0/24,192.168.26.0/24
Endpoint = foo.bar.baz:51820
PersistentKeepalive = 25

This config just bridges my entire LAN’s subnet over the VPN. So, when I got home, the routing table would end up looking something like this:

...
192.168.26.0/24 dev wg0 proto static scope link metric 50
192.168.26.0/24 dev wlp2s0 proto kernel scope link src 192.168.26.76 metric 600
...

Which was the culprit. This creates a routing loop, because all traffic needs to reach the gateway, but that gateway is being routed through the wireguard tunnel, which tries to reach the gateway through itself.

The solution is a metric- but this didn’t seem easy to make persistent with wg-quick. Another issue was that I wanted the tunnel to always be up, so I didn’t need to run wg-quick on reboot. As it turns out, Wireguard support was added to NetworkManager recently, and it can solve both these problems. The creator’s blog post about this feature does a better job of describing it than I can, but briefly you just have to import the existing config:

$ nmcli connection import type wireguard file /etc/wg0.conf
Connection 'wg0' (125d4b76-d230-47b0-9c31-bb7b9ebca861) successfully added.

Then configure it to be as we’d like in NetworkManager:

$ nmcli connection modify uuid 62eb2059-2697-4d99-99dc-e1b513ec96a4 \ 
  ipv4.route-metric 1000
  autoconnect yes

That’s it! Now the metric will be properly applied, avoiding a routing loop when the subnet is available directly, as well as automatically enabling the tunnel.