DEV Community

Ihor Kalnytskyi
Ihor Kalnytskyi

Posted on • Originally published at kalnytskyi.com on

Setup a WireGuard client using systemd-networkd

Please check out «Setup a WireGuard server using systemd-networkd» to learn more about WireGuard in general and network topology outlined in this post.

On Feb 24, 2022, Russia began a full scale invasion of Ukraine. The terror they brought to my country is devastating, and so publication of this post has been postponed till better times. Only now I finally found some strength inside to finish and publish this writing.

WireGuard is a great VPN choice for your home, and maybe even for your company. It's simple, fast, built into Linux kernel 5.6 and above, and can be configured via systemd-networkd in no time. The systemd suite supports WireGuard starting with v237 and is most likely installed on your Linux machine.

Let us try to setup a wireguard 'client' for the following VPN network:

Option Value
Network 10.0.0.0/24
Server 10.0.0.1
Client 10.0.0.20

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:

  • The private key of the peer.
  • The endpoint of the 'server' peer and its public key.

Unlike 'server', a 'client' peer doesn't require port configuration because it's the 'client' that initiates connection not the other way round. This is how some /etc/systemd/network/wg0.netdev could look like:

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

[WireGuard]
PrivateKeyFile=/etc/systemd/network/wireguard-keys/wg0
FirewallMark=0x8888

[WireGuardPeer]
PublicKey=TPouHhVqvgAMfa9mNwZOh59kifUAsdn9Vtgsj2IsKVU=
AllowedIPs=0.0.0.0/0
Endpoint=vpn.example.com:51820
Enter fullscreen mode Exit fullscreen mode

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.

There are couple of things to note:

  • The FirewallMark option takes a number and is used to mark outgoing WireGuard packets. Please remember that mark, it will be later used for network configuration.

  • The AllowedIPs option is set to 0.0.0.0/0 because we're interested to route all the traffic via the tunnel, i.e. surfing the Internet via VPN. If your plans for the VPN is to connect multiple devices into a single network, you should go with the network address only, e.g. 10.0.0.0/24.

  • The Endpoint option takes a wireguard 'server' IP address or hostname, followed by a colon, and then a port the 'server' accepts connection on. The endpoint must be reachable from 'client' peers even when VPN is down.

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.20/24
DNSDefaultRoute=true
DNS=1.1.1.1

[Link]
ActivationPolicy=manual

[RoutingPolicyRule]
FirewallMark=0x8888
InvertRule=true
Table=1000
Priority=10

[Route]
Gateway=10.0.0.1
GatewayOnLink=true
Table=1000
Enter fullscreen mode Exit fullscreen mode

There are a bunch of important stuff going on:

  • Since /24 network mask is used, systemd-networkd will automatically add a route for the whole network to be routed via the WireGuard tunnel. Without that mask, it'd be up to a user to properly configure routing on the system.

  • With ActivationPolicy set to manual, the VPN is not brought up on system boot, and requires manual activation via $ networkctl up wg0. One case use the up value to always activate VPN on system boot.

  • Both RoutingPolicyRule and Route come together and are only required if you want to route all the traffic over the tunnel. The Route section essentially creates a default route that routes packets to the wireguard server. The RoutingPolicyRule section, on the other hand, says that this new default route should be applied only for the packets not coming from the wireguard network device (i.e. not being marked with a firewall mark).

  • When VPN is used as a gateway for the Internet, it's quite common to set some non default DNS servers to resolve domain names. It could be your own VPN server, or some third-party provider such as Cloudflare. In order to set custom DNS servers when VPN connection is up, one can use DNSDefaultRoute and DNS options. The former tells the system to use the latter to resolve all domain names.

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 followed by $networkctl up wg0 to bring the VPN up.

Top comments (0)