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:
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
/etc/systemd/network/wg0.netdev could look like:
[NetDev] Name=wg0 Kind=wireguard Description=wg0 - wireguard tunnel [WireGuard] PrivateKeyFile=/etc/systemd/network/wg0.key FirewallMark=0x8888 [WireGuardPeer] PublicKey=TPouHhVqvgAMfa9mNwZOh59kifUAsdn9Vtgsj2IsKVU= AllowedIPs=0.0.0.0/0 Endpoint=vpn.example.com:51820
The content of
/etc/systemd/network/wg0.key can be generated by invoking
$ wg genkey command and must be readable by the
There are couple of things to note:
FirewallMarkoption takes a number and is used to mark outgoing WireGuard packets. Please remember that mark, it will be later used for network configuration.
AllowedIPsoption is set to
0.0.0.0/0because 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.
Endpointoption 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=188.8.131.52 [Link] ActivationPolicy=manual [RoutingPolicyRule] FirewallMark=0x8888 InvertRule=true Table=1000 Priority=10 [Route] Gateway=10.0.0.1 GatewayOnLink=true Table=1000
There are a bunch of important stuff going on:
/24network 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.
manual, the VPN is not brought up on system boot, and requires manual activation via
$ networkctl up wg0. One can use the
upvalue to always activate VPN on system boot.
Routecome together and are only required if you want to route all the traffic over the tunnel. The
Routesection essentially creates a default route that routes packets to the wireguard server. The
RoutingPolicyRulesection, 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
DNSoptions. 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
$networkctl up wg0 to bring the VPN up.