Elouworld

WireGuard (via systemd-networkd)

WireGuard is a new VPN protocol and software, using modern cryptography (ChaCha20, Ed25519…). It is simple to use and configure, similarly to OpenSSH, you just need to share public keys between peers, compared to OpenVPN where you need to manage a private certificate authority (which has different advantages). Please note though that WireGuard is not made for having anonymous (not logged) clients unlike how OpenVPN can be used for, since it uses static IP addresses instead of DHCP (servers need to track the connections of clients). The main implementation lives in Linux kernel (in a kernel module) but other software implementations exist.

Its source code has been merged in Linux 5.6 and formal verification has been done for WireGuard protocol and implementation. The code base is way smaller than other related projects so it is easier to audit and maintain.

This tutorial presents an installation on a Linux-based operating system with systemd (and systemd-networkd). Other setups are also possible but I found this one simple to use, although platform-dependant. This article doesn’t cover routing Internet traffic through the private network created, but a new article is in preparation.

WireGuard installation

Here are instructions to install WireGuard on Debian, Ubuntu and Arch Linux. You can find other operating system instructions on the official website.

On Debian

At the time of writing, WireGuard is shipped in Debian stable (bullseye) and buster-backports repositories. The wireguard meta package depends on the kernel module and WireGuard tools.

Using instructions from Debian Wiki, let’s install the wireguard package. Note that this will install Linux kernel headers to build the WireGuard kernel module (using dkms), unless a built kernel module is out now.

1
apt update && apt install wireguard

On Ubuntu

Since Ubuntu 20.04, WireGuard support has been backported in the Linux kernel (the kernel packages provide wireguard-modules), there is no need to build a kernel module like on Debian. The instructions however are the same:

1
apt update && apt install wireguard

On Arch Linux

Since all kernel versions are higher than 5.6 on Arch Linux, all supported kernels include the WireGuard module without any additional package.

Using instructions from ArchWiki, install the wireguard-tools package to install the WireGuard tools.

1
pacman -Syu wireguard-tools

Configuration

You need to enable the systemd-networkd service, if not done already. One advantage is that it can manage both your WireGuard setup and networking at the same time, easing the configuration. I have also used it alongside another networking daemon (like networking on Debian or dhcpcd on Arch Linux), but I recommend not doing so.

On both server and client

You need to create a pair of keys on both server and client. Each client will need to get server’s public key and the server will need to get each client’s public key. Also a pre-shared key will be created, known to each peer.

1
2
3
4
5
6
7
8
9
10
11
# go to systemd-networkd configuration directory
cd /etc/systemd/network

# create files with right permissions (640) to prevent other system users to read secrets
umask 0027
touch wg0.netdev wg0.network wg-preshared.key wg-private.key wg-public.key
umask 0022
chown root:systemd-network *

# create a pair of keys
wg genkey | tee wg-private.key | wg pubkey > wg-public.key

wg0.netdev will be used for WireGuard configuration, wg0.network for networking configuration.

On systemd versions earlier than 242, wg0.netdev will contain secrets, on later versions it’s possible to reference other created files. At the time of writing, Debian oldstable (buster) uses systemd 241, which doesn’t support that feature.

On server

On the server, you need to create the pre-shared key (or on any other peer).

1
wg genpsk > wg-preshared.key

It needs to be copied to other peers.

You can also use a different pre-shared key for each peer, if they are not trusted.

Then here are sample server configuration files. I chose to use a subnet 10.213.213.0/24, but you can also use IPv6 or even dual-stack. For IPv6, you can create a prefix in the fd00::/8 prefix, which is allocated for unique local addresses.

Make sure to change <variable> to the appropriate key value. Less-than and greater-than signs (<>) must not be kept.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat <<EOF > wg0.netdev
[NetDev]
Name = wg0
Kind = wireguard
Description = wg server 10.213.213.0/24

[WireGuard]
# If running systemd >= 242
PrivateKeyFile = /etc/systemd/network/wg-private.key
# If running systemd < 242
#PrivateKey = <content of local wg-private.key>
ListenPort = 51820

# For any number of client:
[WireGuardPeer]
PublicKey = <content of client's wg-public.key>
AllowedIPs = 10.213.213.2/32
# If running systemd >= 242
PresharedKeyFile = /etc/systemd/network/wg-preshared.key
# If running systemd < 242
#PresharedKey = <content of wg-preshared.key>
EOF

AllowedIPs represents here the static IP address of the client. You need to choose one per client in the subnet.

You can change ListenPort to your needs (e.g. 53 to pass through firewalls, since it uses UDP).

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF > wg0.network
[Match]
Name = wg0

[Network]
Address = 10.213.213.1/32

[Route]
Gateway = 10.213.213.1
Destination = 10.213.213.0/24
EOF

Then restart systemd-networkd:

1
systemctl restart systemd-networkd

On client

Like on the server, here are sample configuration files. Make sure you change the private IP address on each client, according to server configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cat <<EOF > wg0.netdev
[NetDev]
Name = wg0
Kind = wireguard
Description = wg client 10.213.213.2

[WireGuard]
# If running systemd >= 242
PrivateKeyFile = /etc/systemd/network/wg-private.key
# If running systemd < 242
#PrivateKey = <content of local wg-private.key>

[WireGuardPeer]
PublicKey = <server's public key>
AllowedIPs = 10.213.213.0/24
Endpoint = <server's public IP address>:51820
# If running systemd >= 242
PresharedKeyFile = /etc/systemd/network/wg-preshared.key
# If running systemd < 242
#PresharedKey = <content of wg-preshared.key>
PersistentKeepalive = 25
EOF

cat <<EOF > wg0.network
[Match]
Name = wg0

[Network]
Address = 10.213.213.2/32

[Route]
Gateway = 10.213.213.1
Destination = 10.213.213.0/24
GatewayOnlink = true
EOF

When ListenPort is not entered, a random port will be chosen by systemd, this is not important for clients that connect to the server specified with Endpoint.

Then restart systemd-networkd:

1
systemctl restart systemd-networkd

Check the connection

You can check that systemd-networkd enabled the WireGuard network interface (wg0) with the networkctl command:

1
2
3
4
5
$ networkctl
IDX LINK             TYPE               OPERATIONAL SETUP     
  1 lo               loopback           carrier     unmanaged 
  2 eno1             ether              routable    configured
  3 wg0              wireguard          routable    configured

You should now be able to ping server and client addresses from each other.

If there is any issue, check the logs with journalctl -eu systemd-networkd. If you have Unknown lvalue errors, it means you used unknown configuration directives, e.g. mistyped or from a more recent systemd version.

You can also get more information using the wg command:

On server:

1
2
3
4
5
6
7
8
9
10
11
12
$ wg
interface: wg0
  public key: <redacted>
  private key: (hidden)
  listening port: 51820

peer: redacted=
  preshared key: (hidden)
  endpoint: <client's public IP address>:44195
  allowed ips: 10.213.213.2/32
  latest handshake: 24 seconds ago
  transfer: 968.57 KiB received, 50.94 MiB sent

On client:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ wg
interface: wg0
  public key: <redacted>
  private key: (hidden)
  listening port: 44195

peer: redacted=
  preshared key: (hidden)
  endpoint: <server's public IP address>:51820
  allowed ips: 10.213.213.0/24
  latest handshake: 49 seconds ago
  transfer: 50.94 MiB received, 971.40 KiB sent
  persistent keepalive: every 25 seconds

Additional configuration

  • [WireGuard] and [WireGuardPeer] sections in man 5 systemd.netdev (online versions: Debian, Arch, latest)
  • Quick Start on official website

History

  • Updated installation instructions, added Ubuntu, minor improvements

Latest edition:

Copyright © 2021, Elouan Martinet (Exagone313) — This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.