VPN technologies like IPsec and OpenVPN have been around for a very long time. While they have proven to serve their purpose of securely establishing a tunnel between two networks, I have always found them very cumbersome to setup and run. OpenVPN connections have always been brittle, caused routing issues for me and have poor performance, both in terms of bandwidth and battery life (for mobile clients).
Recently a new, modern, VPN technology was developed with a focus on the following attributes:
- Security: The protocol, cryptography and implementation have been formally verified
- Maintainability: The initial implementation of the kernel module was less than 4 kloc, compared to OpenVPNs 200+
- Ease of Use: Wireguard requires minimal configuration and is compatible with existing linux networking tools
- Performance: Wireguard easily outperforms OpenVPN and aims to be usable on devices ranging from embedded devices to fully routed backbone routers
A big reason why I personally like Wireguard’s approach, is because it really adheres to the Unix philosophy: it does one thing and does it well. Wireguard leaves related concerns, like user management, to other tools. The authors have also taken a very good look at the UX of other workhorse tools that get out of a user’s way, like OpenSSH and Mosh.
Wireguard was initially made available as a linux kernel module, but there is now a userspace golang implementation which brings it to a variety of other platforms, including the Mac, iOS and Android. This makes it the ideal technology to securely make some of my home automation services, which I can’t run on a cloud provider, accessible when I’m away from the local network.
Wireguard Basics
Wireguard does not really have the concept of a client and a server. Both endpoints of a tunnel are peers. Depending on how they are configured, a peer can act as a traditional server, client or something in between.
Wireguard uses a concept called Cryptokey Routing. Each Wireguard network interface has a private key and a list of peers. The peer is defined by its public and a range of IP addresses that should be routed through the tunnel. This setup is very similar to how OpenSSH works. By manipulating the ranges of allowed IPs a on peer, you can use Wireguard to peer 2 distinct network segments together, route all traffic through a remote server or add a single device into the network.
Wireguard on Ubuntu
The first thing we need to do on Ubuntu is install the kernel module and Wireguard tools:
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt-get update
sudo apt-get install wireguard
Temporary Configuration Using Wireguard Tools
The simplest way to create a Wireguard tunnel is to use the wireguard tools that are installed alongside the kernel module. This allows you to experiment with the technology, but the network interface will not persist across a reboot of the machine.
All the command snippets are assuming that they are being executed as root.
The first step is to create a network interface that uses the wireguard driver
ip add dev wg0 type wireguard
Next we set an IP address to our interface
ip addr add 192.168.2.1/24 dev wg0
The IP you chose will depend on what you want to achieve. If you just want to add the peer to the local network you should make this an IP from your current subnet that is not administered by dhcp. I have chosen to have my VPN clients arrive in a different subnet, so I can put some additional firewall protections in place.
We also need to create and set a private key to our interface. This can be done by writing it to a file first
wg genkey > private wg set wg0 private-key ./private
or inline
wg set wg0 private-key `wg genkey`
The public key, needed to add this interface as a peer to another interface, can be generated from a private key using
wg pubkey < ./private
The public key from an interface can be retrieved with the following command
wg show wg0
Finally we should add at least one peer to the interface (this can also be done when the interface is already in the up state)
wg set wg0 peer <some-public-key> allowed-ips 192.168.2.2/32
In this scenario we’re adding a peer with a single IP of which we don’t know the public endpoint. This means that only the peer can start the tunnel.
The interace is now ready to be used
ip link set wg0 up
All of this setup, except for the creation of the interface, can also be added to a configuration file. The above configuration would look like the following wireguard.conf
:
[Interface]
ListenPort = 39604
PrivateKey = <some-private-key>
[Peer]
PublicKey = <some-public-key>
AllowedIPs = 192.168.2.2/32
You can then set this configuration on the interface by running a single command:
wg setconf wg0 wireguard.conf
If you configured the interface by hand using the tools, you can ask wireguard to create the corresponding conf file for you:
wg showconf wg0
Persistent Configuration Using /etc/network/interfaces
In order for the wireguard interface to persist across reboots, we’ll need to add the following to /etc/network/interfaces
.
auto wg0
iface wg0 inet static
address 192.168.2.1
netmask 255.255.255.0
pre-up ip link add $IFACE type wireguard
pre-up wg setconf $IFACE /etc/wireguard/$IFACE.conf
post-down ip link del $IFACE
We also need to place our wireguard configuration file at /etc/wireguard/wg0.conf
. If you read the stanza closely, you’ll see that the pre-up entries are essentially running the same commands as our manual configuration.
Once you’ve created these files, you can reload the server networking configuration with:
service networking restart
Wireguard Android Client
Now that we have a peer configured on ubuntu server, it’s time to configure the mobile client. The first thing is to get the Android app, which is currently in preview, either on the Play Store or FDroid. In case you run a custom rom that has the Wireguard module, the application will use it, otherwise it’ll default to the userland golang implementation. This is all transparent to the user.
The Android app is configured like any other Wireguard client. You can either add all the parameters by hand into UI, or you can create the config somewhere else and get it on the phone by scanning a QR code. Given that entering a random public key with an autocorrecting keyboard is probably not anybody’s idea of fun, we’ll be taking the latter option.
Wireguard config
Let’s dive straight in, and look at the config. We’ll explain the different parameters below:
[Interface]
ListenPort = 39604
PrivateKey = <android-private-key>
Address = 192.168.2.2/24
[Peer]
PublicKey = <some-public-key>
AllowedIPs = 192.168.2.1/32
Endpoint = <server-public-ip>:39604
- Interface.PrivateKey: This is a new private key for the mobile client
- Interface.Address: This is the private IP address that we want the mobile client to have
- Peer.PublicKey: This is the public key of the ubuntu server
- AllowedIPs: This is the IP of the wireguard interface on the server, along with the range of IPs that should be routed into this interface. If you want to route the entire subnet through the wireguard tunnel, you would put
192.168.2.1/24
. If you want to route all your traffic through the wireguard tunnel, you would put0.0.0.0
. - Endpoint: This is a hostname or IP address that points to the peer over the public internet. In the server config we did not specify this value, because the mobile client will be changing IPs all the time.
Create a QR code
Since a QR code is just another way to render a random string, we can turn the config into one and scan it on the phone, so we don’t have to manually enter the entire configuration.
In order to do this we’re going to use the qrencode
program on linux.
apt-get update
apt-get install -y qrencode
qrencode -t ansiutf8 < client.conf
This will render a QR code in your terminal, which you can scan with the Android app.
Conclusions
We’ve covered extending a private LAN to a mobile device using Wireguard by configuring it on a Ubuntu server and using the Wireguard app on a mobile device. This really just scratches the surface of what is possible with Wireguard. It’s high performance, support for multiple platforms and the fact that it can be used inside a container allow for much more jessfraz type experiments. I really do hope more projects take a page out of Wireguard’s book, because this is how security related projects should work.