sylvain durand

Setting up a VPN with Wireguard

VPNs are very practical tools that allow you to secure the connection between two machines, to secure your connection to the Internet when you connect in public places or to bypass geographical access restrictions. When you have a personal server and you don’t want to expose it to the public, a VPN is ideal to reduce the attack surface and access your tools.

For a long time, I used OpenVPN, which was quite heavy to configure and set up, and not always easy to maintain… until the arrival of Wireguard.

Wireguard is a more recent tool, completely open source, very light, simple to set up, which claims to comply with the latest encryption standards and is particularly efficient. It has recently been integrated into the Linux kernel and has clients for Windows, Mac, iOS or Android.

For this example, we will connect a “server” with a “client”. In reality, all the devices connected through Wireguard are perfectly equivalent, and it is possible to configure the data transfers as you wish.

Installation

Under Arch Linux, Wireguard is already integrated in the Linux kernel, but installing wireguard-tools allows to get simple configuration tools.

Keys creation

To communicate, each device will need a public key and an associated private key. The public key must be shared with each machine with which we wish to communicate, and we must benefit in return from their public keys.

Thus, for this example, we will create a private key wg.key with the right permissions, and the corresponding public key wg.pub:

cd .ssh
(umask 0077; wg genkey > server.key)
wg pubkey < server.key > server.pub

We do the same by creating a set of keys for the client:

cd .ssh
(umask 0077; wg genkey > client.key)
wg pubkey < client.key > client.pub

Finally, although it is not mandatory, you can create pre-shared keys that will be used on both devices to strengthen security:

(umask 0077; wg genpsk > preshared.pub)

Server configuration

The configuration is done in the /etc/wireguard/wg0.conf file where we will start by declaring our interface. Specify the contents of the private key server.key in PrivateKey and the port in ListenPort. The PostUp and PostDown lines designate the commands to be run when activating or deactivating the Wireguard network respectively. These indicate that we have to reroute everything that is requested from or to our client. Your server will have the address 10.0.0.1 on the Wireguard network.

[Interface]
PrivateKey = ...
Address = 10.0.0.1/24
ListenPort = 51871
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp3s0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp3s0 -j MASQUERADE

The next step is to specify which clients can connect to the VPN. For each of them, we add in the same file a peer section. Specify the contents of the client’s public key client.pub in PublicKey, and always the pre-shared key in PreSharedKey. Your client will have the address 10.0.0.2 on the Wireguard network.

[Peer]
PublicKey = ...
PreSharedKey = ...
AllowedIPs = 10.0.0.2/32

Client configuration

On the client side, the configuration will also written in /etc/wireguard/wg0.conf. This time we give it the address 10.0.0.2 and we indicate the private key contained in client.key:

[Interface]
PrivateKey = ...
Address = 10.0.0.2/24
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp3s0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp3s0 -j MASQUERADE

It is also given the server as peer, the server’s public key contained in server.pub, and the pre-shared key. The EndPoint field must indicate the domain name or external IP where the server will be reachable, as well as the port (which must, of course, be externally accessible and routed if necessary). The PersistentKeepalive field is not mandatory but allows to solve connectivity problems when connecting through a NAT router.

[Peer]
PublicKey = ...
PreSharedKey = ...
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.domain.tld:51871
PersistentKeepalive = 25

The AllowedIPs field indicates which IPs will be passed through the VPN. With 0.0.0.0/0, all client IPs will be passed through the VPN. If you want to limit the use of the VPN to local services on the server (e.g. self-hosted apps), you can use 10.0.0.0/24 for example.

To start the VPN, you can run wg-quick up wg0 with root rights, both on the client and on the server. To turn it off, a simple wg-quick down wg0 is enough. It is also possible to use a service to start the VPN automatically at startup:

sudo systemctl enable --now wg-quick@wg0

On a macos client

With Homebrew, we install wireguard tools:

brew install wireguard-tools

We create the folder that will contain the configuration:

sudo mkdir -p /usr/local/etc/wireguard

We then edit /usr/local/etc/wireguard/wg0.conf:

[Interface]
PrivateKey =
Address = 10.0.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = ...
PreSharedKey = ...
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.domain.tld:51871

We can then launch Wireguard with:

sudo wg-quick up

Wireguard on your phone

Wireguard has the advantage of being easily available on both iOS and Android, allowing you to access your local network and applications from any wifi network or with cellular data.

In both cases, the application allows you to enter all the fields in the same way as wg0.conf, but of course, entering the certificates by hand would be very tedious.

Fortunately, it is also possible to proceed with a QR code!

Create a public key and a shared key for your phone, as described above, as well as a pre-shared key if necessary:

[Interface]
PrivateKey =
Address = 10.0.0.x/24
DNS = 1.1.1.1

[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.domain.tld:51820

Declare them as a peer on your server, and create a wg0-phone.conf file on your computer.

Once this is done, we can then use qrencode to transform this configuration file into a QR code directly readable (and importable) by the phone, which will be displayed in the terminal:

qrencode -t ansiutf8 < wg0-iphone.conf
█████████████████████████████████████████████████████
█████████████████████████████████████████████████████
████ ▄▄▄▄▄ █  ▄██▄▄▄ ▀▄  ▄▄█ ▄ █▀▄▄ ▀▀█▀▄█ ▄▄▄▄▄ ████
████ █   █ █  █▀ █▀▀▀▀▀▄█ █  ▄  █▀▄▀▀██ ▀█ █   █ ████
████ █▄▄▄█ █▀ ▄██ ▀▄▀▀ ▀ ▄▄▄ ▀ ▀▀█▀█▄█▄▄▄█ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀ █▄▀▄█▄█ █ █▄█ ▀▄▀▄█▄▀ █ █▄█▄▄▄▄▄▄▄████
████  █▀▄ ▄▀█  █▀▄ ▄███▄▄  ▄▄▀ █▄  ▀  █▀▀▀▀ █▄ ▄▄████
████ ▄▄▄█▄▄▄█▄█ ▄█ ▀ █▄█  ▄██▀▀  █ ▄▀▄ ▄██▀▀▀██▄▀████
████▄█▀  ▀▄▀  █▄▄▀▄███▄▄▄▀ ██▄▀█▀█▀ ▀█▄█ ▀ █▄▀ ▀█████
████▀▄▄█▀█▄ █▀  ▀▄  █▀██  ▄█▀█  ▀ ██  ▄▄▄█ █▀▄█▀ ████
████ ▀▀█▀█▄▀ █▄  ▀▀▀▀█ ▄█▀ █▀▄  █ ▀▀  ▄ ▀▄▀ ███▀▄████
█████▀█▄▄▀▄▄▀ ▀   ▄▄█▀▄▀▀ ▄   ▀▀ █ ▄█▄▀▀█▄▀▀▄ █ █████
████ █▀▀ ▄▄▄ ▄▄▄█  ▀█▄▄▄ ▄▄▄ ▀  █ ▄█  ▀█ ▄▄▄ ▀▀ ▀████
████▄ █  █▄█   ▀ ▀█▀ ▀▀  █▄█ █▄   ▄█▀▄   █▄█ ████████
██████ █▄ ▄  █▄▀▄▄ ▄█▄   ▄  ▄ ▄▀█▄▀█  ██▄ ▄▄  █▀ ████
████ ██▄▄█▄▄▄▀█▄ ▄▄▀ ▄▄▀█▀█▄█  ▀▄▄ ███▀▄▀██▄▄▀▀ ▀████
██████  ▄ ▄█▄ ▄▀▀ ▄█ ▄▄█ ██▄ ▄▀▀ ▄  ▄█▄▀▄  ▀▄█  █████
████ ▄▄ █▀▄▄▄▀ ▀▀▄ █▀▀▀█ ▀█▄▀▀ █  ▄▄█ ▄▄█ ▀▀▀▄▄▀▀████
████ ▀█   ▄██▀█▄ ▀▀▀▀██▀▄▄█ ▄ ▄▄▀█ ▀▄▄█ █▀█▀ ▀██▀████
█████▀▀▀ █▄▄▀ ▀▀█▄ ▄█▀▄█▄▀▀▄█▀▀▀ █ █ ▄▀▄ ▄▀▄█▀▀▄▀████
████▄██▄▄█▄▄▀ ██▄▀ ████  ▄▄▄  ███ ▄█▀ ▀▀ ▄▄▄ ▀  ▄████
████ ▄▄▄▄▄ █▀██▄▀▀█▄ ▄██ █▄█ █▀▀▄▄▀▄█    █▄█ ▄▄█▄████
████ █   █ █▄█ ▀▄▄ ██▄ █ ▄▄  ██▄█▄ ▄ ██▄   ▄ ▀█▀█████
████ █▄▄▄█ █▀▄ ▄ ▄▄  █ ▄  █ ▀▀   ▄ ▄▄█▀ █▀▄█ █▀██████
████▄▄▄▄▄▄▄█▄▄▄██▄██▄█▄▄█▄▄██▄▄█▄██▄▄█▄▄▄▄██████▄████
█████████████████████████████████████████████████████
█████████████████████████████████████████████████████