Set up a modern point-to-point VPN with WireGuard

WireGuard logo

WireGuard is a new, simple, secure and fast way to set up a point-to-point VPN between two machines. Cryptography naturally adds an overhead to the communication, so it is important its implementation is as fast as possible. WireGuard works as a Kernel module to provide faster performance compared to more popular solutions such as OpenVPN.

Good things to know about WireGuard before you start

WireGuard:

  • hasn’t been audited yet.
  • is still in its early stages and it is subject to change.
  • uses state-of-the-art cryptography.
  • uses asymmetric key cryptography just like SSL.
  • doesn’t have (yet) many options; as such it is dead-simple to configure.
  • used to run on Linux only but it now runs on many other platforms.
  • is faster than OpenVPN and other userland solutions because it runs inside the kernel.
  • isn’t yet merged into mainline… it uses DKMS for the time being.

Let’s start: Installation

Important
I take absolutely NO responsibility of what you do with your machine; use this tutorial as a guide and remember you can possibly cause data loss if you touch things carelessly.

This guide will show you how to install WireGuard on Ubuntu and CentOS, once installed the commands are the same for every platform.

Before starting you need to make sure you have the kernel headers for your current kernel:

UbuntuCentOS
$ sudo apt-get install linux-headers-$(uname -r)
$ sudo yum install kernel-headers-$(uname -r) kernel-devel-$(uname -r)

Now that you installed the kernel headers you can proceed with the installation of the module. If you encounter problems from here on, make sure you have the right headers for your current kernel.

UbuntuCentOS
$ sudo add-apt-repository ppa:wireguard/wireguard
$ sudo apt-get update
$ sudo apt-get install wireguard
$ sudo modprobe wireguard
$ sudo curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo
$ sudo yum install epel-release
$ sudo yum install wireguard-dkms wireguard-tools
$ sudo modprobe wireguard

Generating a public/private key pair

In order to work, WireGuard requires a public/private key pair. You should repeat this step on each machine you want to connect.

Terminal
$ sudo mkdir /etc/wireguard
# wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey

Adding a new interface

Now we will add a WireGuard interface. These interfaces are just like any other physical interface, except all the traffic sent through the interface will be encrypted. Each machine should have a WireGuard interface and should be assigned a unique private IP address. The IP address used for this purpose isn’t the same as the private IP address you already have.

Terminal

Replace 172.16.0.1/24 with your desired IP address:

$ sudo ip link add dev wg0 type wireguard
$ sudo ip addr add dev wg0 172.16.0.1/24
$ sudo wg set wg0 listen-port 51820
$ sudo wg set wg0 private-key /etc/wireguard/privatekey

Setting up peers

For reference, we will use the following table to clarify the process of setting up peers.

Machine name Public IP WG IP Port Public Key Private Key
Host 1 203.0.113.15/24 172.16.0.1/24 51820 Key1 Secret1
Host 2 203.0.113.172/24 172.16.0.2/24 47890 Host2 Secret2
Host 3 203.0.113.30/24 172.16.0.3/24 71200 Wire3 Secret3

In this step we will set up the connection between the machines. Now pay attention: the following steps must be repeated for each machine you want to connect.

Terminal

Replace PEER_PUBLIC_KEY with the one you want to connect to (not the one on the machine you are on). Then replace PEER_WG_INTERFACE with a matching rule that allows the communication from the other endpoint (e.g. /24 or /32 if you want only one IP allowed). Then replace PEER_IP:PEER_PORT with the public IP and port of the other machine you’re connecting.

$ sudo wg set wg0 peer PEER_PUBLIC_KEY allowed-ips PEER_WG_INTERFACE endpoint PEER_IP:PEER_PORT

For example: if we wanted to connect Host1 to Host 3 we would do:

  • On Host 1:
    $ sudo wg set wg0 peer Wire3 allowed-ips 172.16.0.0/24 endpoint 203.0.113.30:71200
  • On Host 3:
    $ sudo wg set wg0 peer Key1 allowed-ips 172.16.0.0/24 endpoint 203.0.113.15:51820

Once done you might need to open the aprropriate ports through the firewall. You can follow these guides:

Flipping the switch

At this point everything should work, it is just a matter of enabling the interface (on each machine):

Terminal
$ sudo ip link set up dev wg0

You can now verify the network stack by pinging the interface address from the same machine. If you get a response you can now try pining the other associated machines, everything should work. If it doesn’t you probably have misconfigured the peer section, I suggest you to rev.

You can now review the configuration by using the following command:

Terminal
$ sudo wg
interface: wg0
  public key: Key1
  private key: (hidden)
  listening port: 51820

peer: Wire3
  endpoint: 203.0.113.30:71200
  allowed ips: 172.16.0.0/24
  latest handshake: 7 seconds ago
  transfer: 6.02 MiB received, 7.31 MiB sent

Storing permanent configuration

If everything worked find you can now store your configuration in a configuration file! Without this step you will need to repeat the procedure every time you reboot the machine, and that’s not very useful. Fortunately it is just a simple command:

Terminal
$ sudo wg showconf wg0 | sudo tee /etc/wireguard/wg0.conf
[Interface]
ListenPort = 51280
PrivateKey = Secret1

[Peer]
PublicKey = Wire3
AllowedIPs = 172.16.0.0/24
Endpoint = 203.0.113.30:71200

By default the IP configuration is handle on the interface itself and not by WireGuard. This means every time you will bring up the interface using the configuration file the interface won’t have an IP address. This is an undesired behavior if you want to reboot and have your VPN up and running. In order to make it work you must add the highlighted line with the proper IP address (for each machine):

Terminal

Following the previous example. Open with your favorite text editor the file located at /etc/wireguard/wg0.conf and add the following line:

[Interface]
Address = 172.16.0.1/24
ListenPort = 51280
PrivateKey = Secret1

[Peer]
PublicKey = Wire3
AllowedIPs = 172.16.0.0/24
Endpoint = 203.0.113.30:71200

Starting WireGuard at boot

In order to start the interface at boot time, WireGuard comes with a handy systemd unit called wg-quick, so you can simply do:

Terminal

Following the previous example. Open with your favorite text editor the file located at /etc/wireguard/wg0.conf and add the following line:

$ sudo systemd enable --now wg-quick@wg0

Managing interfaces with wg-quick

In addition to the systemd unit, wg-quick is also a useful command to manage the state of an interface.

$ sudo wg-quick wg0 up    # Enables the interface by using the config file /etc/wireguard/wg0.conf
$ sudo wg-quick wg0 down  # Removes the interface
$ sudo wg-quick wg0 save  # Saves the current settings to /etc/wireguard/wg0.conf

Conclusion

You now know how to install and configure WireGuard to create your own VPN. The software is under active development so it might not quite be ready for production, and above all it hasn’t been audited. But that’s sure to change in the future.

WireGuard has a lot of potential and it is still in the early stages. A Windows client is not yet available, but with its low footprint (about 4000 lines) and performance it could become a widely used software in routers. On top of that the installation procedure is simple compared to other VPN solutions like OpenVPN.

Image courtesy of mark | marksei
mark

You may also like...

4 Responses

  1. Ruud says:

    When I try this command on Ubuntu 18.04 I get an error:

    sudo ip link dev wg0 172.16.0.1/24
    Command “dev” is unknown, try “ip link help”.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: