Setup a WireGuard server using systemd-networkd

WireGuard is an extremely simple, fast and modern VPN that is built into Linux kernel 5.6 (released on Mar 29, 2020) and above. It mimics the model of SSH and requires VPN peers to know each others public keys. I highly recommend to read https://www.wireguard.com regardless of this post.

When it comes to WireGuard, there's one interesting aspect: it has no notion of 'server', it's distributed by design and no blockchain is involved. Blockchain enthusiasts may found this surprising but distributed software were invented long before the blockchain 😅. Each peer may connect to other peers assuming they know each other (i.e. public key, IP) and there's a connectivity between them. The 'server' is rather a behaviour one may expect from a certain peer. There are 3 key points that are normally expected from a 'server' peer:

There are number of ways to configure WireGuard on a Linux machine. My favorite one is to use systemd-networkd, a system service that manages both networks and network devices. It's distributed as part of systemd suite, so most likely you have it installed, and it supports WireGuard starting with v237. It's a good choice for a Linux server because it requires no extra software.

For instance we want to setup the following VPN network:

Option Value
Network 10.0.0.0/24
Server 10.0.0.1
Peer A 10.0.0.20
Peer B 10.0.0.30

First thing to do is to set up a virtual network device for a WireGuard tunnel. This can be achieved by means of a systemd.netdev(5) unit that must be created in /etc/systemd/network/ directory. The WireGuard network device must know about number of things:

This is how some /etc/systemd/network/wg0.netdev could look like:

[NetDev]
Name=wg0
Kind=wireguard
Description=wg0 - wireguard tunnel

[WireGuard]
ListenPort=51820
PrivateKeyFile=/etc/systemd/network/wg0.key

[WireGuardPeer]
AllowedIPs=10.0.0.20/32
PublicKey=9vzzasvYciJLmhjrt9Aj9aQYe1gnUxI44ShVLQPrDQA=

[WireGuardPeer]
AllowedIPs=10.0.0.30/32
PublicKey=9vzzasvYciJLmhjrt9Aj9aQYe1gnUxI44ShVLQPrDQA=

The content of /etc/systemd/network/wg0.key can be generated by invoking $ wg genkey command and must be readable by the systemd-network user. What's notable here is that $ wg genkey can be executed anywhere, even on your laptop, there's no need to install extra software on the server.

Next thing to do is to use a systemd.network(5) unit to setup a network. The purpose of the network is to assign a proper IP address on the network device, set proper routes and so on.

This how some /etc/systemd/network/wg0.network could look like:

[Match]
Name=wg0

[Network]
Address=10.0.0.1/24
IPForward=yes
IPMasquerade=yes

There are couple of things to note:

When both the network device and the network are configured, the only remained step is to run $ networkctl reload to pipe in and apply latest configuration.