bella.network Blog

WireGuard, a fast and secure VPN

A VPN solution providing a fast, slim, modern and secure tunnel between clients and servers which can be configured easily
The view through a VPN tunnel into paradise, a secure network
The view through a VPN tunnel into paradise, a secure network (Photo by Erlend Ekseth on Unsplash)

I liked to use OpenVPN to allow my servers to communicate securely with each other and to enable communication with my home network. However, I had increasing problems with stability and latency with OpenVPN, what made me looking for an alternative.

WireGuard is a relatively new VPN technology, developed with a focus on security, speed and simplicity. In 2020 Linux 5.6 was released, where WireGuard was built directly into the kernel. This allows better performance to be achieved than with other technologies like OpenVPN, IPSec, …. Due to the comparatively small amount of code of WireGuard, audits can be carried out more efficiently.

In contrast to other VPN technologies, WireGuard does not have many configuration options. The existing settings are more than sufficient for the intended use.
WireGuard communicates exclusively via UDP and uses, among others, CHACHA20, POLY1305, Curve25519 and other secure cryptography algorithms.

Mutual trust between remote servers is similar to SSH.
Each side uses its own private key and knows the public key of the remote server where the connection is established to. This way, only two servers can communicate to each another without the risk of someone intercepting traffic in between.

In most Linux distributions, WireGuard is available through the default package manager. Ubuntu 20.04 LTS uses Kernel 5.4, whereby WireGuard was ported back from Kernel 5.6 to 5.4 and is therefore also available without additional extras.

Setup

Under Ubuntu 20.04 / Debian 11, WireGuard can be installed via the package manager:

1
apt install wireguard

Next, a private and public key is generated. This is used to secure and authenticate the VPN connection.
This key must be generated on the client and on the server.

1
2
3
4
5
6
7
8
9
# Make newly created files only readable by root
umask 077

# Save keys at /root/
cd /root/
wg genkey | tee privatekey.server | wg pubkey > publickey.server

# Generate preshared key to be used on client and server [optional]
wg genpsk > presharedkey.server

The tunnel configurations are saved in the /etc/wireguard/ folder. In this case the configuration can be saved in /etc/wireguard/tunnel.conf.

The following configuration is an extended example of a tunnel configuration for WireGuard:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Interface]
# Local address of server to be used
Address = 10.20.24.1/24,fd42:10:20:24::1/64
# Port to listen on the server where the client connects to
ListenPort = 2588
# Private key previously generated, use the content from server:/root/privatekey.server
PrivateKey = <server private key>

[Peer]
# PresharedKey is optionally used when needed. Insert from file server:/root/presharedkey.server
# or comment out the following line
PresharedKey = <pre-shared key>
# Public key from file client:/root/publickey.server
PublicKey = <client public key>
# Endpoint is only used on the client to connect to the server
# it can be used on both without NAT for Server<->Server communication
#Endpoint = <server public ip>:2588
# AllowedIPs contains the networks which should be routed trough WireGuard
# Server -> Client only allows the client IP to be routed trough the tunnel
AllowedIPs = 10.20.24.10/32, fd42:10:20:24::10/128
# PersistentKeepalive sends a packet every n seconds to allow traffic trough NAT,
# mostly used in home networks
PersistentKeepalive = 25

The configuration for WireGuard looks very similar on the client. Only minimal adjustments are necessary here:

 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
[Interface]
# Local address of client to be used
Address = 10.20.24.10/24,fd42:10:20:24::10/64
# Private key previously generated, use the content from server:/root/privatekey.server
PrivateKey = <server private key>
# DNS contains the DNS server to be set by the client
DNS = 10.20.24.1

[Peer]
# PresharedKey is optionally used when needed. Insert from file server:/root/presharedkey.server
# or comment out the following line
PresharedKey = <pre-shared key>
# Public key from file server:/root/publickey.server
PublicKey = <client public key>
# Endpoint must contain the address of the server inclusing port where it can be reached
# Prefer to use a DNS name here with IPv4 + IPv6
Endpoint = <server domain / public ip>:2588
# AllowedIPs contains the networks which should be routed trough WireGuard
# All networks defined here will be routed trough the tunnel (Split Tunneling)
AllowedIPs = 10.20.24.10/24, fd42:10:20:24::10/64
# If we are communicating Client -> Server, we can also allow to send all traffic
# to the destination server, disabling split tunnel
#AllowedIPs = 0.0.0.0/0, ::/0
# PersistentKeepalive sends a packet every n seconds to allow traffic trough NAT,
# mostly used in home networks
PersistentKeepalive = 25

The tunnel can be activated on the client as well as on the server with the command wg-quick up tunnel, deactivated with wg-quick down tunnel.
The service can be activated using systemd so that the tunnel is started automatically when the system is started.

1
2
systemctl enable wg-quick@tunnel
systemctl start wg-quick@tunnel

After successful activation and tunnel establishment, the tunnel status can be displayed with the command wg show:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
wg show

interface: tunnel
  public key: [...]
  private key: (hidden)
  listening port: 2588

peer: [...]
  endpoint: [...]:28588
  allowed ips: 10.20.24.10/32, fd42:10:20:24::10/128
  latest handshake: 1 minute, 27 seconds ago
  transfer: 2.28 GiB received, 864.48 MiB sent
  persistent keepalive: every 25 seconds

With the command ip route you can see that a new route is stored in the system and that traffic in the defined network is processed via the WireGuard tunnel.

1
2
3
4
ip route
default via 10.20.23.254 dev ens19 proto static
10.20.23.0/24 dev ens19 proto kernel scope link src 10.20.23.253
10.20.24.0/24 dev tunnel proto kernel scope link src 10.20.24.1

If there are connection problems with WireGuard, logging to Syslog / Kernel Log can be activated using the following command:

1
2
3
4
5
6
7
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

tail -f /var/log/syslog

Dec  8 08:08:05 routing.bella.pm kernel: [567818.453278] wireguard: tunnel: Receiving handshake initiation from peer 1 (<client ip address>:62518)
Dec  8 08:08:05 routing.bella.pm kernel: [567818.453279] wireguard: tunnel: Sending handshake response to peer 1 (<client ip address>:62518)
Dec  8 08:08:05 routing.bella.pm kernel: [567818.453417] wireguard: tunnel: Keypair 12345 created for peer 1

The following command is used to deactivate logging again:

1
echo module wireguard -p > /sys/kernel/debug/dynamic_debug/control

Next steps

With the configuration described, a client can communicate with the server at 10.20.24.1 and other devices in the 10.20.24.0/24 network.
If other networks should be reached, NAT and/or routing must be set up.

To activate routing, the following commands must be executed. IP forwarding is activated for IPv4 and IPv6.

1
2
3
echo "net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1" > /etc/sysctl.d/wg.conf
sysctl -p

Within the [Interface] part of /etc/wireguard/tunnel.conf the PostUp and PostDown directives can be added to enable NAT.
This translates every packet coming from the tunnel and leaving the ens3 interface. (Please replace the interface ens3 with your outgoing interface)

1
2
3
[Interface]
PostUp = iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE; ip6tables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE; ip6tables -t nat -D POSTROUTING -o ens3 -j MASQUERADE

After configuring your client and server with this guide, you have a running WireGuard VPN connection which can be used to transfer traffic between networks securely.

Upcoming posts

In upcoming posts, I will describe how to connect your mobile devices like your Smartphone to your home network and what benefits you gain from this setup.

I also plan to publish an extended setup guide on how to set up dynamic routing using WireGuard and BGP to connect multiple locations dynamically, together with redundancy. This guide will be based on my current setup of my Homelab - https://thomas.bella.network/homelab