How to setup a personal wireguard VPN

My work takes me to the United-States multiple times a year, and I've never been comfortable using the hotel Wi-Fi, or even my company VPN for that matter, when I'm there. I want to be assured that what I do online is my business and my business alone.

I had heard about Wireguard multiple times, how performant and simple it was compared to OpenVPN (I'd like to have a talk with whomever came up with the OpenVPN config file...). I decided to jump in and give it a try. The idea was to setup a VPN access point on my VPS, hosted in Paris, to which I could connect when I travel.

Installing wireguard

I followed Wireguard's official install instructions. However, I also needed to install the headers files for the kernel I was running so that dkms could compile the wiregard kernel module.

% apt-get install linux-headers-$(uname -r)
% add-apt-repository ppa:wireguard/wireguard
% apt-get update
% apt-get install wireguard

If everything is going according to plan, you should see the wireguard kernel module being compiled by dkms at install time:

DKMS: build completed.wireguard.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/X.Y.Z-ABC-generic/updates/dkms/

At that point, you should be able to see the module in the lsmod output and load it.

% lsmod | grep wireguard
wireguard             204800  0
ip6_udp_tunnel         16384  1 wireguard
udp_tunnel             16384  1 wireguard
% modprobe wireguard

Configuring the server peer

First off, we create the server wireguard peer's public and private keys.

% cd /etc/wireguard
% umask 077  # disable public access
% wg genkey | tee privatekey | wg pubkey > publickey

We now configure the server peer, assuming that the VPS public network interface is ens2. We'll use the subnet for all wireguard-related addresses, and assign IP to the server peer.

% cat <<EOF > /etc/wireguard/wg0.conf
# The IP assigned to the wg0 interface
Address =

# The port wireguard will listen on
ListenPort = <public port>

# The private key used by the local peer
PrivateKey = $(cat /etc/wireguard/privatekey)

# Accept traffic to the wg0 interface and allow NATing traffic from ens2 to wg0
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens2 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens2 -j MASQUERADE

% rm /etc/wireguard/privatekey

We also need to authorize UDP traffic on the <public port> port.

% iptables -i ens2 -p udp --dport <public port> -j ACCEPT

Once that's done, we're now able to use wg-quick to setup the wg0 network interface, as well as the MASQUERADE iptables rules that will NAT the traffic between the public ens2 interface to wg0. We can actually use systemd for that, as we're assured that the wg0 interface is re-created in case of a reboot.

% systemctl start wg-quick@wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens2 -j MASQUERADE

% systemctl enable wg-quick@wg0
Created symlink from /etc/systemd/system/ to /lib/systemd/system/wg-quick@.service.

Configuring the phone peer

I use the Wireguard Android app, and assign the address to my phone, as well as add the server peer details (as Wireguard is a point-to-point VPN without a client/server architecture).

The server peer public key is set to the content of the remote /etc/wireguard/publickey file, on my VPS. As I want to route all my phone traffic through wireguard, I set the Allowed IPs field to, and the peer endpoint to <server public ens2 IP>:<public port>.


Authorizing the phone peer

After having generated a public key for the phone peer, we also need to authorize it on the server peer and restart wireguard.

% cat <<EOF >> /etc/wireguard/wg0.conf

# Phone peer
PublicKey = <phone peer public key generated in app>
AllowedIPs =
% systemctl restart wg-quick@wg0

Testing the whole thing

My phone disconnected from the server wireguard peer, I'm now able to inspect the state of the wg0 server network interface:

% ifconfig wg0
wg0       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:  P-t-P:  Mask:
          UP POINTOPOINT RUNNING NOARP  MTU:1420  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

I then connect my phone to the server peer, open a random webpage, and voila, we can see traffic going through the server wg0 interface.

$ ifconfig wg0
wg0       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:  P-t-P:  Mask:
          UP POINTOPOINT RUNNING NOARP  MTU:1420  Metric:1
          RX packets:4084 errors:0 dropped:132 overruns:0 frame:0
          TX packets:4895 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:452436 (452.4 KB)  TX bytes:2954188 (2.9 MB)

A quick tcpdump shows that the data flowing to wg0 is indeed encrypted.

$ tcpdump -i wg0 -vv -c 100 -X
tcpdump: listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
15:14:05.096356 IP (tos 0x0, ttl 105, id 47301, offset 0, flags [none], proto TCP (6), length 332) > Flags [P.], cksum 0x8308 (correct), seq 1867855144:1867855424, ack 229885280, win 253, options [nop,nop,TS val 426814177 ecr 2017832], length 280
    0x0000:  4500 014c b8c5 0000 6906 fe02 4a7d 8cbc  E..L....i...J}..
    0x0010:  c0a8 0202 146c b631 6f55 3528 0db3 c560  .....l.1oU5(...`
    0x0020:  8018 00fd 8308 0000 0101 080a 1970 aae1  .............p..
    0x0030:  001e ca28 1703 0301 13e7 c1f4 5089 ed04  ...(........P...
    0x0040:  aba6 ef67 2cbe a7b3 f0cc 02d0 caaa d675  ...g,..........u

I now have have a personal VPN I can use whenever I travel abroad.

Thanks to Thomas for being patient with me while answering networking questions at 11pm, and for proof-reading this article. Any remaining mistake is my own.