WireGuard has risen in popularity over the last year or so with several adoptions by commercial VPN services. Its simplicity and speed make it a great choice for a private VPN replacement and having recently been accepted into the net-next maintainer tree for inclusion in an upcoming kernel, I figured now was a good time to give it a try. So, I retired my Raspberry Pi running PiVPN at home and instead set up a LXC in Proxmox to tunnel all my mobile devices through. I was able to find several guides floating around the net but none of them covered exactly what I wanted to do. Having to jump between several browser tabs to piece together all the information is a pain, so I decided to write this up for anyone else who might find it useful. Here is a (hopefully complete) guide to setting up WireGuard in a container on Proxmox with a sample Android device configuration.

Assumptions

  • Proxmox Version: 6.1-3+

  • Container: Ubuntu 18.04+

  • Android Phone: Anything compatible with the official WireGuard app.

Proxmox Host

First, we need to make sure the kernel headers are installed for our current kernel. It's a good idea to make sure everything is up to date first, if a new kernel is installed, boot into it prior to installing the headers.

apt update
apt dist-upgrade
apt install pve-headers

Now to install WireGuard.

As of this writing WireGuard is packaged in Debian Unstable, so a little work needs to be done before it's available to install. The below will create an unstable apt source list file then create an apt preference file to limit automatic installation from this repo. More info about this is available on the Debian Wiki: WireGuard and AptConfiguration

Update 11/2020:

A bit after writing this guide WireGuard was accepted into testing and shortly after backports. Until Proxmox moves onto the 5.6 kernel the following is the best method of installation.

We need to enable backports in our sources.list file, update apt to re-sync the package index, then install WireGuard specifically from backports. More info about this can be found here.

vim /etc/apt/sources.list
# Add the following line to the existing sources.list file.

deb http://deb.debian.org/debian buster-backports main
apt update
apt install -t buster-backports wireguard-dkms

We can now enable the kernel module. We also need to add WireGuard to modules.conf so that it will be forced to load at boot time. This is required as the host isn't aware of the container at boot time and won't know to load the module automatically.

modprobe wireguard
echo "wireguard" >> /etc/modules-load.d/modules.conf

That's it for the host, on to the container.

LXC

Setting up an LXC container is a simple process and many distributions and versions are available, so I'm going to assume you already have one of your preference ready to go. I chose Ubuntu 18.04 because I had a template ready to quickly clone. Here is a link to the WireGuard install page with instructions for all other operating systems. The key difference to keep in mind is that since we're installing into a container that uses the host's kernel, we only need to install the user-space tools and not the kernel module itself.

Update 11/2020: Later releases of Ubuntu do NOT require you to add the ppa repo as WireGuard has been included in the main repo.

apt update
apt install software-properties-common              # NOT required in later Ubuntu releases.
add-apt-repository ppa:wireguard/wireguard          # NOT required in lster Ubuntu releases.
apt install --no-install-recommends wireguard-tools

Next, we need to enable forwarding. To do this edit /etc/sysctl.conf and uncomment the required line. Then enable it with the sysctl command.

Update 11/2020: As this guide was originally written this step was incorrectly located under the ‘Proxmox Host’ section. This was a mistake and forwarding should only be enabled for the container. If you've followed this guide in the past check to make sure this is corrected on your system.

vim /etc/sysctl.conf
#net.ipv4.ip_forward=1 # This is the default, uncomment to match below.
net.ipv4.ip_forward=1
sysctl -p

That should take care of the installation, on to the configuration.

Simple WireGuard VPN Configuration

If you're not familiar with WireGuard, I recommend going here and reading through the conceptual overview to get more background on what's being done here. That said, you should be able to copy these configuration files, substitute values where needed and have a working installation.

The first thing we need to do is create encryption keys for both the container and each device we want to connect. Normally this would be done on each device individually but, since we're going to use qrencode to generate a QR code from the configuration data for the Android app, we'll create the configuration for both the container and phone alongside each other in the container. For other devices, like a laptop or a remote computer, the configuration and encryption key generation should be done on the device itself for security purposes.

Let's move into the /etc/wireguard directory, creating it if necessary, and generate key pairs for both the server and the phone. Note that we are using umask here to ensure the proper directory permissions are set prior to key generation.

cd /etc/wireguard/
umask 077
wg genkey | tee server-privatekey | wg pubkey > server-publickey
wg genkey | tee android-privatekey | wg pubkey > android-publickey

Next, let's create a configuration file for the server. Using your favorite editor create a file for the interface configuration. By convention, as this is our first WireGuard interface, we'll call the file wg0.conf. The interface needs to be on a different subnet than the host to avoid conflicts, for simplicity here I've chosen 192.168.2.1/24 as the host in this example is on the 192.168.1.0/24 subnet. The entries for private and public keys need to be substituted with the contents of the previously created key files. The default ListenPort of 51820 can be changed to your choosing, just make sure the Endpoint entry in the next step reflects this. Also, note the interface name used in the PostUp and PostDown entries, here it is eth0 but if yours differs (check by running ip addr) change it accordingly.

vim wg0.conf
[Interface]
Address = 192.168.2.1/24
PrivateKey = <server-privatekey file contents>
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <android-publickey file contents>
AllowedIPs = 192.168.2.2/32

Now we need to create a configuration file for the Android phone. You'll need to substitute your server's IP address here for the Endpoint entry and make sure the ListenPort chosen earlier is copied here if changed from the default 51820.

If you don't have a static IP address for your server you'll want to use a service such as DynDNS or some other method to keep a domain pointed to your server's IP if it changes. I usually purchase a cheap domain name from a registrar that provides free dynamic DNS service, then I keep it updated using ddclinet on the server. You will also want to make sure to have the ListenPort you've chosen forwarded through any router or firewall you may have to the IP/port of the WireGuard LXC to enable access from outside your LAN.

The DNS entry can be inside the local network or outside. Again, the entries for private and public keys need to be substituted with the contents of the previously created key files. The PersistentKeepalive entry is helpful if you're behind NAT and need to keep the connection alive, 25 is the recommended value.

vim android.conf
[Interface]
PrivateKey = <android-privatekey file contents>
Address = 192.168.2.2/24
DNS = 192.168.1.1

[Peer]
PublicKey = <server-publickey file contents>
Endpoint = <server-ip>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Finally, lets ensure proper permissions are set for the WireGuard directory, enable and start the service, then create the QR code we need to transfer the configuration over to the phone.

chown -R root:root /etc/wireguard
chmod -R og-rwx /etc/wireguard
systemctl enable wg-quick@wg0.service
systemctl start wg-quick@wg0.service
apt install qrencode
qrencode -t ansiutf8 < android.conf

After this last step, you should have a QR code displayed in your terminal. If you haven't already, install the official WireGuard Android app from either the Google Play Store or F-Droid. The app is very simple to use and you should only have to open the plus menu on the bottom right, choose ‘create from QR code’, and scan the code. You will be allowed to name the connection and then enable it. If all went well you'll now have a VPN setup that routes all traffic from your phone through the LXC and your local LAN should be accessible. Hope this was helpful, happy tunneling!

Feel free to send me an email with any questions, comments, or requests and if you're feeling generous, help keep me caffeinated.