WireGuard VPN Walkthrough

This article was last updated: March 29th 2018

Part of the Wireguard series:


With the rise of privacy consciousness, people are looking to solutions like a hosted VPN (I hear one should never use a free service), or self hosted like streisand and algo. How does a VPN (a remote access VPN – not a site-to-site VPN for the pedantic) help maintain privacy?

In the scenario of maintaining privacy or getting around geographic content blocking, the VPN connects you to a server, oftentimes in a different country, where it forwards all your traffic to the intended recipient. The recipient responds to the server, which dutifully forwards back to you. So, if you live in the US, but are VPNed into a German server and request content from India, India will think you’re in Germany (this assuming countries have thoughts </joke>).

I’m going to show how to self host WireGuard, which bills itself as easier to configure than IPSec and OpenVPN, while being faster and more powerful. WireGuard is a component feature of of streisand, but since we’re going to be dealing with only a linux client and server setup we cut out the streisand middleman and just use WireGuard. Theoretically, this cuts down on the bloat and attack surfaces that are inherent with the wide array of software that streisand installs (streisand is planning on supporting modular install in the future).

It should be noted:

WireGuard is not yet complete. You should not rely on this code. It has not undergone proper degrees of security auditing and the protocol is still subject to change.

This demonstration will be on a DigitalOcean Ubuntu 16.04 box, but it should be easily adaptable for other platforms (as long as they are linux based).

Script

The following script is to be executed on one’s server. This script will be subsequently broken down.

#!/bin/bash

# The client's public key (generated in subsequent section client side)
CLIENT_PUBLIC="<ENTER>"

sysctl -w net.ipv4.ip_forward=1
add-apt-repository --yes ppa:wireguard/wireguard
apt-get update
apt-get install --yes wireguard-dkms wireguard-tools

wg genkey | tee privatekey | wg pubkey > publickey

PRIVATE=$(cat privatekey)
echo "public: $(cat publickey)"

cat > /etc/wireguard/wgnet0.conf <<EOF
[Interface]
Address = 10.192.122.1/24
SaveConfig = true
ListenPort = 51820
PostUp = iptables -A FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PrivateKey = $PRIVATE

[Peer]
PublicKey = $CLIENT_PUBLIC
AllowedIPs = 10.192.122.2/32
EOF

Simple enough, how does this work?

sysctl -w net.ipv4.ip_forward=1

Sysctl allow modifying kernel parameters at runtime, so here we’re allowing the kernel to forward packets from one network interface to another. We need this, as wireguard works by creating the VPN on another network interface (commonly called wg0 or wgnet0). This interface, by itself, does not have internet access, but with ip forwarding we can foward traffic from the VPN to the interface that can communicate with the internet.

Forwarding is only important for the server because once connected to the VPN the default client interface won’t be used anymore.

sysctl -w is only for changing kernel parameters at runtime. To persist these settings, edit the relevant line in /etc/sysctl.conf

If you’re interested in having the VPN initialized on start up execute systemctl enable [email protected] after setting everything up.

add-apt-repository --yes ppa:wireguard/wireguard
apt-get update
apt-get install --yes wireguard-dkms wireguard-tools

These commands fetches the latest wireguard version and installs it. Since WireGuard hooks into the kernel, it attempts to automatically detect the correct kernel to hook into. This should work flawlessly.

The one problem I’ve had is that for DigitalOcean controls the kernel through their web interface (one can use a custom kernel but that is outside of the scope of this post). Anyways, if you had tried to install a custom kernel ontop of the one in DigitalOcean, wireguard will skip the correct kernel as it believes it’s chrooted. Sorry for the tangent, but since I experienced this problem, I figured I should document it for others.

wg genkey | tee privatekey | wg pubkey > publickey

Both the client and server need to generate a pair of keys. The server does not need to know the client’s private key and vice versa; however they do need to know each other’s public key to permit only authorized use of the VPN (else anyone who knew your VPN server’s address could use your VPN).

[Interface]
Address = 10.192.122.1/24
ListenPort = 51820

When clients connect to the server, they can communicate directly with by using the 10.192.122.1. We know that 10.192.122.1 can’t possibly be an internet facing box because it falls under a private network. The /24 is a CIDR subnet mask that states that this VPN will is capable of housing 254 clients. WireGuard then listens on port 51820 for interested clients.

PostUp = iptables -A FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wgnet0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

Probably the most convulted section of the config, yet this step must not be skipped. This is how those lines configure the firewall.

When the VPN is created:

Then when the VPN is destroyed everything in our firewall is deleted.

If you forget those lines, when you go to connect as a client your requests will blackhole and it may appear as if you lost internet connection.

[Peer]
PublicKey = $CLIENT_PUBLIC
AllowedIPs = 10.192.122.2/32

The peer section is for client information. The client that connects with the given client public key is assigned 10.192.122.2 for their IP address.

Client Config

The server is all setup so what does the client configuration look like?

[Interface]
Address = 10.192.122.2/32
PrivateKey = CLIENT_PRIVATE_KEY
DNS = 10.192.122.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = SERVER_IP:51820

Breaking down the config:

[Interface]
Address = 10.192.122.2/32
DNS = 10.192.122.1

The 10.192.122.2/32 matches the same address as the server.

We set the client’s DNS server to that of the VPN server. This is not needed, but I recommend it, as you want to communicate with servers (eg. google.com) that are closest to your VPN server to minimize latency. For instance, if you live in the US, VPNed into Singapore, and wanted google.com, you’d want to talk to Singapore Google server (and not the one’s in the US) so that packets travel the least distance.

These DNS queries will be forwarded to the server and need to be interpreted by something. While you can use a PREROUTING iptables rule to forward to your favorite DNS server, I find it easier to have a local DNS server. I use dnsmasq with the following config in /etc/dnsmasq.conf

server=1.1.1.1
server=8.8.8.8
server=8.8.4.4
interface=wgnet0

This way, dnsmasq only listens on VPN traffic and forwards queries to Cloudflare or Google.

Automation

It is actually possible to create and destroy VPN boxes on demand for next level privacy. Here is how one would do this:

And just like that you can create and destory VPNs all around the world in under a minute.

Comments

If you'd like to leave a comment, please email [email protected]