Installing WireGuard, the Modern VPN

Co-authored by tmlxs and adr13n

WireGuard is a network tunnel (VPN) for IPv4 and IPv6 that uses UDP.  Currently most of the code resides in the Linux kernel but cross platform implementations are under way. WireGuard features an authentication scheme similar to that of SSH, whereby the VPN server and each client have their own asymmetric key pair. Authorizing a new client is as simple as adding their public key in the server configuration file. Note that WireGuard can be configured to use pre-shared keys as an additional layer of security on top of the existing asymmetric keys. This is optional but improves post-quantum resistance.

WireGuard shines by its simplicity and auditability: it consists of ~4 KLoC whereas most alternatives are much more difficult to audit with over 100 KLoC. Its use of modern cryptographic primitives (Curve25519, HKDF, ChaCha20, Poly1305, BLAKE2s, SipHash24) and its efficient implementation makes it an excellent replacement for OpenVPN and IPsec. Wireguard operates as a virtual network interface (for example, wg0). This means that the interface can be managed using the standard ip(8) tool.

WireGuard is also the fastest VPN around (see the benchmarks) exceeding the performance of OpenVPN in throughput tests. It is stealthy and silent since it doesn’t send a reply to unauthenticated messages, making it hard to be discovered by network scanners. Moreover it provides perfect forward secrecy.

Note that WireGuard is still experimental and is therefore not ready for production.

WireGuard introduces the concepts of EndpointsPeers and AllowedIPs. A peer is a remote host and is identified by its public key. Each peer has a list of AllowedIPs. From the server’s point of view, the AllowedIPs are IPs that a peer is allowed to use as source IP addresses. For the client, they work as a sort of routing table, determining which peer a packet should be encrypted for. If a peer sends a packet with a source IP that is not in the list of AllowedIPs on the server, then the packet will be simply dropped on the server’s side, for example. An endpoint is a pair of IP address (or hostname) and port of a peer. It is automatically updated to the most recent source IP address and port of correctly authenticated packets from the peer. This means that a peer that is for example jumping between mobile networks (and whose external IP address changes) will still be able to receive incoming traffic because its endpoint will be updated whenever he sends an authenticated message to the server. This is possible because the peer is identified by its public key.

Below is a complete walkthrough on setting it up as a VPN. Thanks a lot to Jason for his help and for answering all our questions !

Installation

The installation of WireGuard is quite easy. Check if it is packaged for your distribution under the following link: https://www.wireguard.io/install/#option-a-distribution-packages. If it isn’t, you will need to compile it from source as explained here.

The installation process has to be done on the server as well as on the various clients that want to connect.

Server setup

First make sure IP forwarding is enabled by adding the following to /etc/sysctl.conf

net.ipv4.ip_forward=1

Run the following command to apply the above setting:

$ sudo sysctl -p

Also make sure the port used by WireGuard is opened on your firewall (default port UDP/51820 is used in this tutorial).

Now WireGuard will be setup. A range for the hosts in the tunnel needs to be chosen. For this blogpost, we chose to use 192.168.3.0/24 where 192.168.3.1 is assigned to the server and 192.168.3.2 to the client.

Here is the command to run as root to generate the config file for the server (will be saved under /etc/wireguard/wg0.conf):

# umask u=rwx,go= && cat > /etc/wireguard/wg0.conf << _EOF
[Interface]
Address = 192.168.3.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o IFACE -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o IFACE -j MASQUERADE
PrivateKey = $(wg genkey)
SaveConfig = true

[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = CLIENT_VPN_IP/32
_EOF

Make sure you update IFACE with your internet-facing interface’s name (eth0 or similar). The [Peer] block can be completed by the client information as explained in the section below. The following needs to be replaced in that block:

  • CLIENT_PUBLIC_KEY is the client’s public key.
  • CLIENT_VPN_IP is the IP given to the client within the tunneled range.

The PostUp and PostDown commands are executed by Bash after setting the interface up or down respectively. They enable and disable NAT and forwarding using iptables.

Finally start WireGuard with

$ sudo wg-quick up wg0

The VPN service can be shutdown with:

$ sudo wg-quick down wg0

Note that wg-quick(8) is just a Bash wrapper around wg(8) which might be integrated into ip(8) directly.

The SaveConfig = true entry in the config file tells WireGuard to automatically update the config file when new clients are added as explained below under Adding new clients on the server.

Once the interface is up, the different connected peers can be viewed using the following command:

$ sudo wg show

The server’s public key is shown when running above command. One could also retrieve it by pasting the private key (contained in the generated configuration file) in the command wg pubkey and hitting CTRL+d. This public key will be needed to set up the client below.

Client setup

Clients are also required to install WireGuard. This can be performed the same way as described in the Installation section above. When done, you may proceed.

The tool resolvconf is used to dynamically change the DNS server(s) when starting the WireGuard tunnel. If your distribution does not come with resolvconf, either install it or change the resolvconf invocation below to something suitable for your particular configuration.

Now setup the client configuration file by running following command as root:

# umask u=rwx,go= && cat > /etc/wireguard/wg0.conf << _EOF
[Interface]
Address = 192.168.3.2/24
PostUp = echo nameserver DNS_SERVER | resolvconf -a tun.%i -m 0 -x
PostDown = resolvconf -d tun.%i
PrivateKey = $(wg genkey)

[Peer]
PublicKey = SERVER_PUB_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = PUBLIC_VPN_IP:51820
_EOF

Where:

  • DNS_SERVER is to be replaced with the DNS server’s IP you’d like to use while tunneling through the VPN (for example 8.8.8.8 to use google’s DNS).
  • SERVER_PUB_KEY is the server’s public key generated in previous section.
  • PUBLIC_VPN_IP is the VPN public IP to which the client will connect to.

As for the server the public key can be retrieved by pasting the private key created in the configuration file into the command wg pubkey and hitting CTRL+d.

Enabling/Disabling the VPN tunnel

On the client the VPN can be enabled using wg-quick(8):

$ sudo wg-quick up wg0

To disable the VPN:

$ sudo wg-quick down wg0

Information about the connection can be retrieved with following command:

$ sudo wg show

Adding new clients on the server

Every new client must perform the client setup detailed above. Then simply add the new client on the server with

$ sudo wg set wg0 peer CLIENT_PUB_KEY allowed-ips CLIENT_VPN_IP/32

Thanks to the config entry SaveConfig = true, the new added client will be saved in the config file next time WireGuard is brought down (wg-quick down wg0). CLIENT_PUB_KEY needs to be replaced with the client’s public key and the CLIENT_VPN_IP is the IP given to the client within the tunneled range (same entry as Address in the client’s configuration file).

Going further

Here we have set up a VPN that will route all traffic through the tunnel (using the 0.0.0.0/0, ::/0 AllowedIPs on the client’s config) but this can be reduced to a specific range only (thus allowing only specific IPs to be routed through the tunnel). Also multiple configurations might co-exist on each client such that different setups can be used. Simply create different configuration files within /etc/wireguard/ and use them with sudo wg-quick up NAME where name refers to the config file’s name without its .conf extension.

wg-quick uses a rule selector (fwmark) in order to route the different packets to the right interface and keeping all the existing routes clean. All the WireGuard encrypted packets tagged with the mark get sent to the WireGuard route while other packets get sent to the existing routes.

References

4 comments

  1. Thanks for the great guide! It works but took a lot of trial and error because I misread a lot of your instructions.

  2. Perfect setup! Just one thing, you are missing a /24 on the CLIENT configuration.
    [Interface]
    Address = 192.168.3.2
    should be:
    [Interface]
    Address = 192.168.3.2/24

    If you don’t add that, you will not see a wg0 route when typing “route -n”
    Thanks!

  3. Thanks for your post.
    However I’m wondering why there are different netmasks used, i.e. /24 and /32?

Leave a Reply