WireGuard creates an encrypted network tunnel operating at layer 3 of the OSI model as a kernel virtual network interface. A WireGuard VPN (Virtual Private Network) connection is established by exchanging public keys. This feature is referred to as crypto key routing. Crypto key routing associates public keys with a list of IP-addresses. The WireGuard server holds a private key and a list of nodes. Each node has a public key. Public keys are used to authenticate to the VPN server. Keep in mind that these keys should be communicated by an out-of-band method, similar to how one deals with SSH keys.
WireGuard relies on "quantum-resistant" crypto. This means traditional cryptographic algorithms such as RSA, AES and HMAC, which are used in OpenVPN, are no longer used in WireGuard. An attacker can potentially break currently used encryption algorithms such as RSA and ECC with the help of quantum computing. Today’s encrypted communication is being stored by (nation-state) attackers and can be decrypted years later with these quantum computers. This means all data could be recovered in clear-text from recording traffic and breaking the public-key scheme.
We will deploy WireGuard on top of the OpenBSD Operating System (OS) in Vultr. OpenBSD is rather stable and holds a track record of having the least amount of Remote Code Executions (RCE) in decades, and does not violate RFC based network standards. Besides that, the OpenBSD team is also the creator of the widely implemented OpenSSH. Long story short, the OpenBSD team knows their stuff when it comes to security.
Network overview
On Vultr.com, which is an alternative to AWS and DigitalOcean, we deploy a Virtual Machine (VM), also referred to as a Virtual Private Server (VPS). We pick the smallest available instance with 25GiB SSD, 1GB memory and 1000GB bandwidth for just $5 USD per month. Vultr allows us to upload custom ISO's. This way we can upload the OpenBSD ISO from the official repository so we can verify the integrity of the OS. Also note that Vultr accepts prepaid credit cards for obvious privacy reasons and supports U2F (Universal 2nd Factor) authentication and OTP (One Time Password) tokens when logging in to the cloud web portal.
Figure 1 shows the overall VPN architecture where we have one WireGuard VM and one application server VM. You can see a malicious entity present at the WAN side and also at the client-side. As a security professional, you should always assume the worst, which is that your perimeter has already been breached and you are in an untrusted network. This assumption is according to the zero trust network model. The tun0 interface forwards 10.0.0.0/24 traffic to the vio0 interface gateway. This way all network traffic from the clients is routed through the VPN server to the WAN. Clients are also able to access a local application server on a different local subnet.
WireGuard cryptography
Advantages of WireGuard are that it uses a small code base, relies on kernel-based routing route(8) and firewalling pf(4), cross-platform compatible and is rather fast. However, WireGuard does not have formal governmental security accreditations. This might be because governments would have a hard time breaking the strong and elegant crypto implementation of WireGuard. Keep in mind that WireGuard is also officially still in beta.
WireGuard relies on the Noise Protocol Framework which is mainly Bernstein's implementation of quantum-resistant crypto algorithms with Perfect Forward Secrecy (PFS) to defend against these intrusions on our privacy:
Algorithm | Purpose |
---|---|
ChaCha20 | For symmetric data encryption |
Poly1305 | For message authentication |
Curve25519 | For key exchange |
BLAKE2 | For hashing and keyed hashing |
SipHash24 | For hash tables |
HKDF | For key derivation |
WireGuard has built-in protection against Denial of Service (DoS) attacks using a new crypto-cookie mechanism for IP address attributability.
OpenBSD Full Disk Encryption
We start off by uploading the OpenBSD install66.iso to your Vultr account. Next, deploy a new VM and attach this ISO file. Start the console view of this VM and press s to open up a shell and follow the following instructions:
Welcome to the OpenBSD/amd64 6.6 installation program.
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell? s
cd /dev
sh MAKEDEV sd0
dd if=/dev/urandom of=/dev/rsd0c bs=1m
fdisk -iy sd0
Writing MBR at offset 0.
disklabel -E sd0
Label editor (enter '?' for help at any prompt)
a a
offset: [64] 64
size: [2490031] *
FS type: [4.2BSD] RAID
sd0*> w
sd0> q
No label changes.
bioctl -c C -r 8192 -l /dev/sd0a softraid0
New passphrase:
Re-type passphrase:
[...]
softraid0: CRYPTO volume attached as sd1
exit
We write the whole /dev/rsd0c volume with random data from the urandom source. This will make it harder to determine how large binaries are on the virtual drive from a forensics perspective.
Next we use the bioctl utility to initialize and encrypt the virtual drive. The -r parameter dictates the number of KDF iterations. The default is 16 which we raise to 8192 rounds. The whole disk is encrypted as a softraid volume - hence the -c C parameters.
The passphrase is used to encrypt and decrypt data on your drive to protect against offline attack vectors. Make sure you think of a strong passphrase that is not easily breakable.
Bear in mind that Vultr can potentially still access this VM. You will have to enter the passphrase through an HTTPS encrypted VNC console every time the VM is rebooted. This VNC session is under Vultr's controls, which means Vultr could potentially intercept and log your passphrase. Vultr claims not to do this.
Furthermore, Vultr could potentially make a snapshot of this VM and dump the complete memory content to disk. This gives Vultr access to all data currently in memory, which includes the encryption key of the virtual drive. Vultr claims not to do this.
Vultr also controls the hypervisor which runs the VM, this means Vultr controls all computations the VM makes. In theory, Vultr could potentially leverage this control to break any cryptography which happens on this VM. Vultr claims not to do any of this. You would have to trust Vultr's privacy policy and assume no nation-state actors will pressure Vultr into executing any of these attack scenario's. Alternatively, host your own on-premise VPS where are in full control.
Now it's time to decide where OpenBSD should be installed:
Welcome to the OpenBSD/amd64 6.6 installation program.
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell? i
Choose your keyboard layout [default] ENTER
[...]
Available network interfaces are: vio0 vio1 vlan0.
Which network interface do you wish to configure? (or 'done') [vio0]
IPv4 address for vio0? (or 'dhcp' or 'none') [dhcp]
IPv6 address for vio0? (or 'autoconf' or 'none') [none]
Available network interfaces are: vio0 vio1 vlan0.
Which network interface do you wish to configure? (or 'done') [done]
Default IPv4 route? (IPv4 address or none) [124.112.22.23]
[...]
Available disks are: sd0 sd1.
Which disk is the root disk? ('?' for details) [sd0] sd1
No valid MBR or GPT
Use (W)hole disk MBR, whole disk (G)PT or (E)dit? [whole]
Setting OpenBSD MBR partition to whole sd1...done.
[...]
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a]
Next, either download the signatures for the installation binaries or omit signature verification which is not recommended for integrity reasons. Once the installation of the OpenBSD packages has been completed, remove the ISO image and reboot the server. We now have an OpenBSD VM with AES-XTS-256 Full Disk Encryption (FDE) enabled.
WireGuard installation
WireGuard is available in Golang from the official OpenBSD repository. This way you can easily update the package when an update is available.
$ fw_update
$ syspatch
$ pkg_add -u
$ pkg_add wireguard-go wireguard-tools
$ pkg_info | grep wireguard
wireguard-go-0.0.20191012v0 implementation of WireGuard in Go
wireguard-tools-0.0.20191012v0 fast and secure VPN
OpenBSD network configuration
It is now time to make our OpenBSD system ready to be used as a VPN sever. First we configure a virtual tunnel interface tun0:
$ vi /etc/hostname.tun0
inet 10.0.0.1 255.255.255.0 10.0.0.255 description "WireGuard"
!/sbin/route add -inet 10.0.0.0/24 10.0.0.1
We bring up this tun0 interface:
$ sh /etc/netstart /etc/hostname.tun0
OpenBSD does not pass on network traffic by default. We allow IPv4 packets to travel between vNICs (virtual Network Interface Cards) which transforms our OpenBSD box into a router. We control this traffic with Packet Filter pf(4):
$ echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
$ vi /etc/pf.conf
[...]
set skip on {lo tun0}
[...]
#WireGuard VPN firewall NAT settings
pass in quick on egress proto udp to port 53
pass out on egress inet from (tun0:network) nat-to (egress:0)
[...]
WireGuard uses UDP connectionless for the encapsulated VPN data streams. We leverage the IANA (Internet Assigned Numbers Authority) defined default DNS port 53 to tunnel out. This is because most Internet Service Providers (ISPs) and corporate network firewalls do not block UDP 53.
We load the new PF rules into memory:
$ pfctl -vf /etc/pf.conf
$ sh /etc/netstart && sh /etc/netstart
WireGuard configuration
Next we create a directory where we can save the key-pairs and configuration files:
$ mkdir /etc/wireguard
$ chmod 0700 /etc/wireguard/
$ cd /etc/wireguard
$ wg genkey | tee /etc/wireguard/server-private.key | wg pubkey > /etc/wireguard/server-public.key
$ wg genkey | tee /etc/wireguard/laptop-private.key | wg pubkey > /etc/wireguard/laptop-public.key
$ wg genkey | tee /etc/wireguard/phone-private.key | wg pubkey > /etc/wireguard/phone-public.key
$ wg genpsk > /etc/wireguard/preshared.key
Now we create a VPN server side configuration file. Use unique IP-addresses and key-pairs for each device connecting to the VPN server:
$ vi /etc/wireguard/server.conf
[Interface]
#WireGuard server private key
PrivateKey = zF02smb1sa9Q6aQ3a1BVTUM38aEWyBEMN3OTv6L3V1o=
ListenPort = 53
[Peer]
#Laptop public key
PublicKey = XPOKKDLA8vql+FoVj+vx/wTNfmReaSa6cXkyZ5WdgTQ==
PresharedKey = Hqc4AxV9dWulI82WFUmP7AnNqwTlaPFn7LOa3uJfFnc=
AllowedIPs = 10.0.0.2/32, 172.16.1.0/24
[Peer]
#Phone public key
PublicKey = yxoPnQylrmABai6V+8VBApYIvXT6EV2mdA82/Blc5Bk=
PresharedKey = Hqc4AxV9dWulI82WFUmP7AnNqwTlaPFn7LOa3uJfFnc=
AllowedIPs = 10.0.0.3/32
Configure the right security permissions so other users cannot read the key-pairs:
$ chmod 0600 /etc/wireguard/*
$ chown root:wheel /etc/wireguard/server.conf
We start the WireGuard service and make it persistent on boot:
$ rcctl enable wireguard_go
$ rcctl set wireguard_go flags tun0
$ rcctl start wireguard_go
Next we test the server.conf on possible errors:
$ wg setconf tun0 /etc/wireguard/server.conf
Now we make the server.conf persistant so this file is automatically loaded after a reboot:
$ echo '/usr/local/bin/wg setconf tun0 /etc/wireguard/server.conf' >> /etc/rc.local
We create the following client-side config. We leverage AdGuard DNS servers for blocking internet ads and malicious content:
$ vi /etc/wireguard/phone.conf
[Interface]
#Phone private key
PrivateKey = AAOKKDLA2vql+FoVj+vx/wTNfmRxaSaAcXkyZ5WdgsA=
Address = 10.0.0.3/32
DNS = 176.103.130.130, 176.103.130.131
[Peer]
#WireGuard server public key
PublicKey = ZXmNA6CCRttOMSE/EX9S+CobCXfzmxfMFL10hZVq6hU=
PresharedKey = Hqc4AxV9dWulI82WFUmP7AnNqwTlaPFn7LOa3uJfFnc=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 124.112.22.23:53
PersistentKeepalive = 25
We convert the iPhone config to a QR-code so we can easily transfer all parameters and connect:
$ pkg_add libqrencode
$ qrencode -t ansiutf8 < /etc/wireguard/phone.conf
We check the status of the server and the connected iPhone:
$ wg
interface: tun0
public key: ZXmNA6CCRttOMSE/EX9S+CobCXfzmxfMFL10hZVq6hU=
private key: (hidden)
listening port: 53
peer: XPOKKDLA8vql+FoVj+vx/wTNfmReaSa6cXkyZ5WdgTQ=
endpoint: 49.212.123.23:59847
allowed ips: 10.0.0.2/32
latest handshake: 1 minute, 44 seconds ago
transfer: 284.02 KiB received, 1.26 MiB sent
peer: yxoPnQylrmABai6V+8VBApYIvXT6EV2mdA82/Blc5Bk=
allowed ips: 10.0.0.3/32
Make sure you remove all your .key files after testing from the WireGuard server:
$ rm /etc/wireguard/server-p* /etc/wireguard/laptop-p* /etc/wireguard/phone* /etc/wireguard/preshared.key
Network exposure
We use a netcat and nmap query to get an overview of the exposed WireGuard VPN port. However, we cannot detect much from the outside;)
$ nc -uvz 124.112.22.23 53
Warning: forward host lookup failed for 124.112.22.23.vultr.com: No address associated with name
136.244.100.44.vultr.com [124.112.22.23] 53 (domain) open
$ nmap -sS -sU -Pn -O 124.112.22.23 -p 53
Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-20 09:47 W. Europe Standard Time
Nmap scan report for 124.112.22.23.vultr.com (124.112.22.23)
Host is up.
PORT STATE SERVICE
53/tcp filtered domain
53/udp open|filtered domain
Too many fingerprints match this host to give specific OS details
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.04 seconds
Keep in mind that Deep Packet Inspection (DPI) systems detect and drop these 'malformed' DNS packets due to this covert method. Hence the following packet capture. All data is encrypted over UDP port 53:
Internet Protocol Version 4, Src: 49.212.123.23, Dst: 124.112.22.23
User Datagram Protocol, Src Port: 65041, Dst Port: 53
Domain Name System (query)
[Malformed Packet: DNS]
[Expert Info (Error/Malformed): Malformed Packet (Exception occurred)]
[Malformed Packet (Exception occurred)]
[Severity level: Error]
[Group: Malformed]
Considerations
There are a few downsides to WireGuard. First of all, WireGuard does not support U2F (Two Factor Authentication) tokens such as YubiKey's or a simple virtual OTP from your Google Authenticator app. This means that anyone can connect to your VPN server if they have a copy of your client VPN config file.
Furthermore, WireGuard does not provide any authentication logging. This means there is little to no auditability of who authenticates to your VPN server. Also, Hardware Security Module (HSM) integration would be a great feature. Storing private keys on your local machine in plaintext is bad security practice.
You can also look into configuring this WireGuard VPN server to another WireGuard server, SOCKS5 and/or to proxychains in order to achieve a multi-hop VPN setup for better privacy. Keep in mind that you will always connect from a static IP at Vultr which nobody else should connect from. This makes it non-trivial to track you, which undermines the fundamentals of privacy.
Lastly, make sure to update your WireGuard packages regularly since WireGuard is still under heavy modifications.
Edit (October 2020): WireGuard has now been upstreamed in the OpenBSD 6.8 and Linux 5.6 kernel.