Securing your Network Traffic with WireGuard VPN


WireGuard

There are many examples of network intermediaries monitoring or interfering with network traffic. Whether it is sniffing for passwords sent over open WiFi networks, an ISP injecting advertisements into web pages, or governments censoring politically inconvenient content, the internet can be a dangerous place. A Virtual Private Network (VPN) can help to solve some of these problems, by creating an encrypted and authenticated connection on top of an underlying untrustworthy network.

Many VPN software packages have a bewildering array of configuration options, some of which leave subtle security holes. WireGuard was developed as a reaction to this, and aims to be simpler for users to configure. It only allows a single choice of encryption and authentication algorithms, preventing both incompatibility problems and the possibility for users to select insecure parameters. This also reduces the size of the source code, making it easier for external developers to audit.

Installation

WireGuard is relatively new, and is therefore not always available in older operating system versions. For example, it first appears in the default software repositories in Ubuntu 19.10, Debian 11 and Fedora 30. On these systems, you can install with the standard package manager, e.g. for Ubuntu:

apt install wireguard

If you are using an older version of your operating system, you can either upgrade to a newer release or install WireGuard from somewhere outside the default repositories. For details on how to do this, see the installation instructions on the WireGuard website.

Usage

For full control over the configuration of a WireGuard interface, you can use the wg command-line tool. To configure how network packets are routed through the interface, you would then use ip addr, ip route etc.

For a simpler, easier-to-use alternative, you can use wg-quick. This allows defining an interface, its peers and IP addresses in a single configuration file. By default, these files are stored in /etc/wireguard. Here is an example of the file format:

# Comments are marked with #

[Interface]
# Private key, generated by `wg genkey`
# !! Do not reuse this one; generate your own !!
PrivateKey = MKENjh9U+yD2q8hepN4nTdNIQjOLaYPvzpIc6I2fEFQ=
# ListenPort: The UDP port where encrypted traffic arrives over the network *outside* the VPN
ListenPort = 27516
# Address: The IP address of this host *inside* the VPN
Address = 172.16.4.2
# Multiple Address lines are allowed: e.g. to specify both IPv4 and IPv6 addresses
Address = fd11:dead:beef:0:1::1


[Peer]
# The public key corresponding to the PrivateKey of the peer
PublicKey = JZ7ZdRt5Q7sCU63uz3eNTXK48HZMOnAee6i1NC2HfWY=
# AllowedIPs defines what traffic should be sent over the VPN to this peer
AllowedIPs = 172.16.4.3/32
# Endpoint: Where to send the encrypted traffic for this peer (*outside* the VPN)
Endpoint = 1.2.3.4:5678

WireGuard interfaces defined with wg-quick can either be started directly with wg-quick up NAME or via systemd with systemctl start wg-quick@NAME.service. These commands correspond to a configuration file /etc/wireguard/NAME.conf.

It is also possible to configure WireGuard network interfaces with systemd-networkd. For this, you first define the virtual network device with a NAME.netdev file in /etc/systemd/network/, then configure the network connection that uses that file with a corresponding NAME.network file. This option also allows more complex setups such as bridging or bonding several VPN interfaces, advanced routing configuration and so on.

Point-to-Point VPN

The simplest case is a point-to-point connection, where two or more computers are already able to connect to each other directly, and just want to be able to do this in a secure way. Each endpoint needs to generate a private key and choose a VPN-internal IP address. For IPv4 addresses, this would normally be an address from the RFC1918-defined address spaces 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. These addresses should be chosen so as not to clash with private home or office networks as well as virtualized networking such as Docker or KVM. A more robust alternative, so long as all software being used to communicate over the VPN is IPv6-ready, would be to generate an IPv6 unique local address prefix and allocate all VPN IP addresses from that range.

Once this is done, the configuration would look like this:

# Peer 1 configuration
[Interface]
PrivateKey = <Peer 1 private key>
Address = <Peer 1 internal address>
ListenPort = 23456

[Peer]
PublicKey = <Peer 2 public key>
AllowedIPs = <Peer 2 internal address>
# Peer 2 configuration
[Interface]
PrivateKey = <Peer 2 private key>
Address = <Peer 2 internal address>

[Peer]
PublicKey = <Peer 1 public key>
AllowedIPs = <Peer 1 internal address>
Endpoint = <Peer 1 public IP address / DNS name>:23456

Only one peer needs an Endpoint line, as Peer 1 will learn Peer 2’s address and port when it receives encrypted VPN packets.

Accessing a Private LAN

Another common use case (and indeed the original justification for the concept of a Virtual Private Network) is to grant access to a private local network via the public internet. This requires a gateway machine which is connected both to the (non-virtual) private network and to the public internet. The gateway runs the VPN software and routes traffic between the virtual and physical private networks.

The other end of the VPN connection can either be an individual client machine (e.g. a staff member’s laptop) or another gateway (e.g. to pass traffic between the private LANs at two office sites). In general, addresses should be allocated to the different machines according to the same address scheme that was used in the private LAN.

Here is an example configuration for two gateways and a client:

# Gateway 1 configuration
[Interface]
PrivateKey = <Gateway 1 private key>
Address = 10.0.1.5  # IP address of Gateway 1 in the LAN
ListenPort = 23456

[Peer]
PublicKey = <Gateway 2 public key>
AllowedIPs = 10.0.2.0/24  # IP range of the LAN behind Gateway 2
Endpoint = gateway.site2.example.com:54321

[Peer]
PublicKey = <Client 1 public key>
AllowedIPs = 10.0.3.2  # Allocated VPN IP address for client 1
# Gateway 2 configuration
[Interface]
PrivateKey = <Gateway 2 private key>
Address = 10.0.2.5  # IP address of Gateway 2 in the LAN
ListenPort = 54321

[Peer]
PublicKey = <Gateway 1 public key>
AllowedIPs = 10.0.1.0/24  # IP range of the LAN behind Gateway 1
Endpoint = gateway.site1.example.com:23456

[Peer]
PublicKey = <Client 1 public key>
AllowedIPs = 10.0.3.2  # Allocated VPN IP address for client 1
# Client configuration
[Interface]
PrivateKey = <Client private key>
Address = 10.0.3.2

[Peer]
PublicKey = <Gateway 1 public key>
AllowedIPs = 10.0.1.0/24  # IP range of the LAN behind Gateway 1
Endpoint = gateway.site1.example.com:23456

[Peer]
PublicKey = <Gateway 2 public key>
AllowedIPs = 10.0.2.0/24  # IP range of the LAN behind Gateway 2
Endpoint = gateway.site2.example.com:54321

Once the VPN is up and running, the gateways must also be configured to forward packets between the VPN and their private LANs. In doing this, it is important to avoid accidentally enabling direct packet forwarding from the public internet to either the LAN or the VPN. The simplest way to do this is to enable a firewall with a “default deny” rule for packet forwarding, and add rules to allow forwarding packets between the WireGuard and LAN interfaces.

Routing All Your Traffic Through the VPN

To route all traffic through the VPN, simply set the AllowedIPs setting in the client to a value that includes all IP addresses. The gateway must then be configured to allow packet forwarding (usually with network address translation) from the VPN to its public internet interface.

# Client configuration
[Interface]
PrivateKey = <Client private key>
Address = <client VPN IP address

[Peer]
PublicKey = <Gateway public key>
AllowedIPs = 0.0.0.0/0  # All IPv4 addresses
AllowedIPs = ::/0       # All IPv6 addresses
Endpoint = gateway.example.com:23456

The wg-quick tool is smart enough to add an exception to the routing table so that the encrypted VPN packets destined for the gateway are not themselves sent over the VPN.

Conclusion

Previously existing VPN software was hard to configure and had many potential security pitfalls. WireGuard makes it much easier to set up a secure virtual private network, so that all network traffic between two hosts is encrypted and authenticated. Try it out and see if you can improve your security!

Further Reading

Man pages:

Chris Kerr