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 inman 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.