Wandering Thoughts archives

2021-07-16

The WireGuard VPN challenge of provisioning clients

I mentioned in yesterday's entry that at work I'm building a VPN server that will support WireGuard. I'm quite happy with WireGuard in general and I think it has some important attractive features (such as the lack of 'sessions'), but we won't be offering WireGuard for general use. I would like to, but every time I even consider the idea, I run headlong into the problem of provisioning, specifically of provisioning WireGuard clients in some way that ordinary people can successfully set them up.

Right now, to set up a WireGuard client you need the server's name and port (which every VPN needs), the server's public key, the IP the server expects you to have inside the WireGuard connection (its AllowedIPs setting for you), and a private key that the server has the public key for. We also need you to set your DNS server(s) to correctly point to us, and for general VPN usage you have to set your AllowedIPs to 0.0.0.0/0. This is a lot more things for you to set up than other VPN servers need, partly because other VPN servers will push your internal IP, the DNS servers to use, and often other information to you. Much of this is also sensitive to typos or, in the case of keys, must be cut and pasted to start with (no one is typing a base64 WireGuard key). If you get your client IP wrong, for example, things just quietly don't work (the server will discard your traffic).

The client keypair is an especially touchy problem. The ideal would be to securely generate it on the client and upload the public key. In practice this is asking a lot of people to do more or less by hand, so in a realistic setup we would probably want to generate your client keypair on the server and then somehow give you access to the private key for you to configure along side the server's public key. Given this, possibly the most generally usable way of provisioning WireGuard client connections would be to generate the wg.conf that a client would use with the normal WireGuard command line tools, then provide it to people and hope that any WireGuard client will be able to import it.

(The official WireGuard client for iOS and Android will apparently do this, including decoding the configuration from a QR code. I believe the official Windows client does as well. On Unix, you can use the wg.conf directly or import it into NetworkManager.)

An additional complication is that you need a separate WireGuard configuration on each device that you want to use WireGuard on at the same time. So we wouldn't have to just provision one WireGuard setup per person, we're looking at one for your laptop, one for your phone, one for your tablet, and so on. This also complicates naming them and keeping track of them (for people and for us), and likely would tempt people into reusing configurations across devices, which leads to fun problems if both devices are in use at the same time.

I don't blame the WireGuard project for this state of affairs. Provisioning is both a hard problem and a high level concern that is sort of out of scope for a project that's deliberately low level and simple. I'm honestly impressed (and happy) that there are official WireGuard clients on as many platforms as there are. I do wish there was some officially supported way to push configuration information to clients, although I understand why there isn't.

(Tailscale is not a solution for us for various reasons, including price. I do admire them for solving the provisioning problem, though.)

sysadmin/WireGuardProvisioningChallenge written at 23:56:07; Add Comment

Setting up a WireGuard client with NetworkManager (using nmcli)

For reasons beyond the scope of this entry, I've been building a VPN server that will support WireGuard (along with OpenVPN and L2TP). A server needs a client, so I spent part of today setting up my work laptop as a WireGuard client in a 'VPN' configuration, under NetworkManager because that's what my laptop uses. I was hoping to do this through the Cinnamon GUIs for NetworkManager, but unfortunately while NetworkManager itself has supported WireGuard for some time, this support hasn't propagated into GUIs such as the GNOME Control Center (cf) or the NetworkManager applet that Cinnamon uses.

I'm already quite familiar with WireGuard in general, so I found that the easiest way to start was to set up a basic WireGuard configuration file for the connection in /etc/wireguard/wg0.conf, including both the main configuration (with the laptop's key and my local port) and a [Peer] section for the server. Since I'm using WireGuard here in a VPN configuration, instead of to reach just some internal IPs, I set AllowedIPs to 0.0.0.0/0. After writing wg0.conf, I then imported it into NetworkManager:

nmcli connection import type wireguard file /etc/wireguard/wg0.conf

(For what can go in the configuration file, start with wg(8) and wg-quick(8). I suspect that NetworkManager doesn't support some of the more advanced keys. I stuck to the basics. The import process definitely ignores the various script settings supported by wg-quick(8). Currently, see nm_vpn_wireguard_import() in nm-vpn-helpers.c.)

Imported connections are apparently set to auto-connect, which isn't what I wanted, plus there were some other things to adjust (following the guide of Thomas Haller's WireGuard in NetworkManager):

nmcli con modify wg0 \
   autoconnect no \
   ipv4.method manual \
   ipv4.address 172.29.50.10/24 \
   ipv4.dns <...>

At this point you might be tempted to set ipv4.gateway, and indeed that's what I did the first time around. It turns out that this is a mistake, because these days NetworkManager will do the right thing based on the 'accept everything' AllowedIPs I set, right down to setting up policy based routing with a fwmark so that encrypted traffic to the WireGuard VPN server doesn't try to go over WireGuard. If you set ipv4.gateway as well, you wind up with two default routes and then your encrypted WireGuard traffic may try to go over your WireGuard connection again, which doesn't work.

(See the description of 'ip4-auto-default-route in the WireGuard configuration properties. The full index of available NetworkManager settings in various sections is currently here; the ones most useful to me are probably connection.* and ipv4.*.)

Getting DNS to work correctly requires a little extra step, or at least did for me. While the wg0 connection is active, I want all of my DNS queries to go to our internal resolving DNS server and also to have a search path of our university subdomain. This apparently requires explicitly including '~' in the NetworkManager DNS search path:

nmcli con modify wg0 \
  ipv4.dns-search "cs.toronto.edu,~"

This comes from Fedora bug #1895518, which also has some useful resolvectl options.

You (I) can see a lot of settings for the WireGuard setup with 'nmcli connection show wg0', including active ones, but this seems to omit NetworkManager's view of the WireGuard peers. To see that, I needed to look directly at the configuration file that NetworkManager wrote, in /etc/NetworkManager/system-connections/wg0.nmconnection. I'm someday going to need to edit this directly to modify the WireGuard VPN server's endpoint from my test machine to the production machine.

(The NetworkManager RFE for configuring WireGuard peers in nmcli is issue #358.)

With no GUI support for WireGuard connections, I have to bring this WireGuard VPN up and down with 'nmcli con up wg0' and 'nmcli con down wg0'. Once I have the new VPN server in production, I'll be writing little scripts to do this for me. Hopefully this will be improved some day, so that the NetworkManager applet allows you to activate and deactivate WireGuard connections and shows you that one is active.

If I wanted a limited VPN that only sent traffic to our internal networks over my WireGuard link, I would configure the server's AllowedIPs to the list of networks and then I believe that NetworkManager would automatically set up routes for them. However, I don't know how to make this work (in NetworkManager) if the WireGuard VPN server itself was on one of the subnets I wanted to reach over WireGuard. For my laptop, routing all traffic over WireGuard to work is no worse than using our OpenVPN or L2TP VPN servers, which also do the same thing by default.

(On my home desktop, I use hand built fwmark-based policy rules to deal with my WireGuard endpoint being on a subnet I want to normally reach over WireGuard. NetworkManager will build the equivalents for me when I'm routing 0.0.0.0/0 over the WireGuard link, but I believe not in other situations.)

(For information, I primarily relied on Thomas Haller's WireGuard in NetworkManager, supplemented with a Fedora Magazine article and this article.)

linux/NetworkManagerWireGuardClient written at 01:00:49; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.