Jun 3, 2020
Organizations spanning more than one site often use Virtual Private Networks (VPNs) to protect sensitive data sent from site to site. VPNs allow private networks to be extended across a public network. Most software VPN implementations utilize encryption to further protect data in transit.
Two of the most common VPNs, IPsec and OpenVPN, are long established and feature rich, but often difficult to set up and deploy. A while ago I attempted to set up a IPsec VPN on my home pfSense firewall, but gave up because of the complex authentication setup required.
VPNs are also useful for individuals wanting to increase privacy and security. Away from home you can easily and safely connect to services on your LAN as an alternative to opening them up to the internet. Additionally you can keep your internet use private while on public Wifi as all your traffic will be tunneled to your VPN server.
A key thing to remember is VPNs act as a tunnel. Even though your internet traffic will be inaccessible to those at your local coffee shop, a VPN by itself does not provide true privacy or anonymity. Internet traffic sent through a VPN can still be theoretically snooped at the other endpoint. If you you’re looking for robust privacy you should look into Tor or Tails.
WireGuard, developed by Jason Donenfeld, is a relative newcomer yo the VPN scene reaching a 1.0.0 release in March 2020. WireGuard aims to be simple and easy to use while also having a minimal attack surface, sound cryptography, and high performance.
Simple and Easy-to-use - WireGuard aims to be as easy to configure and deploy as SSH.
Minimal Attack Surface - WireGuard has a lean source code with just about 4,000 lines of code as opposed to OpenVPN at 70,000 lines of code. WireGuard is meant to be comprehensively reviewable by individuals.
Cryptographically Sound - WireGuard uses the latest cryptography and has received a third-party security audit.
High Performance - WireGuard’s fast cryptography and integration with the Linux Kernel means it can run circles around other VPN software.
WireGuard was incorporated into the 5.6 Linux Kernel and backported into the 5.4 Kernel (in time for the Ubuntu 20.04 release) making installation on Focal Fossa a breeze.
For this tutorial I will be setting up a WireGuard Ubuntu 20.04 container using lxd on a Ubuntu 20.04 server. The container will map the arbitrary port 51820 with the host server. This project requires either a static public ip address or dynamic DNS and the ability to forward ports on your router. I completed this tutorial with a pfSense router and dynamic DNS set up using Cloudflare’s free service with a purchased domain.
Create a new lxd container:
$ lxc launch ubuntu:20.04 wireguard $ lxc exec wireguard -- /bin/bash
Install WireGuard and configure container firewall:
# apt update && apt upgrade -y # apt install -y wireguard # ufw allow 51820 && ufw enable
Generate as many keys as needed for the number of devices you will connect to your VPN.
Note: The specific keys used in this example have already been deleted and are not in use.
Generate WireGuard keys:
# cd /etc/wireguard && umask 077 # wg genkey | tee server.key | wg pubkey > server.pub # wg genkey | tee peer_a.key | wg pubkey > peer_a.pub # wg genkey | tee peer_b.key | wg pubkey > peer_b.pub
Keys needed for wg0.conf (copy your own keys generated in the previous step):
# cat server.key SCURF86WoPr4rsOaKNbtfw9EVSsozu27AkXEHWBNtmM= # cat peer_a.pub GQaw2aYS4N5UNy1r4WWunVpjXU3YuEHwOsXWhsag/g8= # cat peer_b.pub EcVSOjJ7y6wVAZkwq20b8DBJzwUVqBFc2CjYU3XF0F4=
Server and client IP addresses have been chosen for simplicity. A third client would have IP address 10.200.200.4/32 in the server config and 10.200.200.4/24 in the client config.
# vim /etc/wireguard/wgo.conf [Interface] Address = 10.200.200.1/24 ListenPort = 51820 PrivateKey = SCURF86WoPr4rsOaKNbtfw9EVSsozu27AkXEHWBNtmM= PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # peer_a PublicKey = GQaw2aYS4N5UNy1r4WWunVpjXU3YuEHwOsXWhsag/g8= AllowedIPs = 10.200.200.2/32 [Peer] # peer_b PublicKey = EcVSOjJ7y6wVAZkwq20b8DBJzwUVqBFc2CjYU3XF0F4= AllowedIPs = 10.200.200.3/32
In my case I am setting the DNS server of my clients to the IP address of my router. If your router has a DHCP server you can do the same or set client DNS to any other DNS server e.g. Cloudflare (18.104.22.168) or Google (22.214.171.124).
Keys needed for peer_a.conf
# cat peer_a.key +DRT3xfc7Hkf9I+HX1tKPXje/uTn1hxHO0KFkvkncGk= # cat server.pub YR1hPdS+kkhgHh0sC9zNaat9oACAb6eKjlKHjxmd7k8=
Peer A config file:
# vim /etc/wireguard/peer_a.conf [Interface] Address = 10.200.200.2/24 PrivateKey = +DRT3xfc7Hkf9I+HX1tKPXje/uTn1hxHO0KFkvkncGk= DNS = 10.0.0.1 [Peer] PublicKey = YR1hPdS+kkhgHh0sC9zNaat9oACAb6eKjlKHjxmd7k8= AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = wireguard.example.com:51820
Keys needed for peer_b.conf
# cat peer_b.key cPLzRaCjo95KV4lK8n7sVg67x5Pg5OA56qv+l0AHNnc= # cat server.pub YR1hPdS+kkhgHh0sC9zNaat9oACAb6eKjlKHjxmd7k8=
Peer B config file:
# vim /etc/wireguard/peer_b.conf [Interface] Address = 10.200.200.3/24 PrivateKey = cPLzRaCjo95KV4lK8n7sVg67x5Pg5OA56qv+l0AHNnc= DNS = 10.0.0.1 [Peer] PublicKey = YR1hPdS+kkhgHh0sC9zNaat9oACAb6eKjlKHjxmd7k8= AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = wireguard.example.com:51820
Enable and start WireGuard service
# systemctl enable --now firstname.lastname@example.org # exit
Firewalls and Port Forwarding
Add a LXD proxy device to map ports between the host server and the wireguard container:
$ lxc config device add wireguard myport51820 proxy listen=udp:0.0.0.0:51820 connect=udp:127.0.0.1:51820
Port forwarding is fairly straightforward in pfSense. First navigate to the pfSense web GUI. From the top menu select Firewall > NAT and click Add rule. Change Protocol to UDP, enter 51820 in the Destination port range line’s first Custom box, enter the host server’s IP address in the Redirect target IP line, and enter 51820 in the Redirect target port line’s Custom box.
I will be using an iPhone 7 as a client. The easiest method to transfer a WireGuard config file to a mobile device is by using a qrcode. You can either send the qrcode to straight to stdout or save it as a bitmap file.
$ lxc exec wireguard -- /bin/bash # apt install -y qrencode $ qrencode -t ansiutf8 -r peer_a.conf $ qrencode -t png -r peer_a.conf -o peer_a.png
Install the WireGuard app for iOS and open it up. Tap Add a tunnel in the middle of the screen and then tap Create from QR code. Name the tunnel wg0 and enable it by toggling the on switch. If testing at home make sure to turn off Wifi and open up a browser to test the VPN connection. The public IP address on your client device should now match your home public IP address. You can navigate to a site like whatismyip to check.
You can also see stats from the wireguard container with the wg command.
# wg interface: wg0 public key: YR1hPdS+kkhgHh0sC9zNaat9oACAb6eKjlKHjxmd7k8= private key: (hidden) listening port: 51820 peer: GQaw2aYS4N5UNy1r4WWunVpjXU3YuEHwOsXWhsag/g8= endpoint: 127.0.0.1:54564 allowed ips: 10.200.200.2/32 latest handshake: 12 seconds ago transfer: 7.04 KiB received, 38.15 KiB sent
If you want to dive further into WireGuard make sure to check out the WireGuard docs, and the Arch Wiki is always is a great resource. If you’re having any issues getting your own VPN server set up, feel free to contact me via Twitter or Telegram.