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.
Proxmox Version: 6.1-3
Container: Ubuntu 18.04
Android Phone: Anything compatible with the official WireGuard app.
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
echo "deb http://deb.debian.org/debian/ unstable main" > /etc/apt/sources.list.d/unstable-wireguard.list printf 'Package: *\nPin: release a=unstable\nPin-Priority: 90\n' > /etc/apt/preferences.d/limit-unstable
Now we can install WireGuard and enable the kernel module. We also need to add
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.
apt install wireguard modprobe wireguard echo "wireguard" >> /etc/modules-load.d/modules.conf
Finally, we need to enable packet forwarding. To do this edit
/etc/sysctl.conf and uncomment the needed line. Then we'll enable it with
#net.ipv4.ip_forward=1 # This is the default, uncomment to match below. net.ipv4.ip_forward=1
That's it for the host, on to the container.
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.
apt update apt install software-properties-common add-apt-repository ppa:wireguard/wireguard apt install --no-install-recommends wireguard-tools
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
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
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
51820 can be changed to your choosing, just make sure
Endpoint entry in the next step reflects this. Also, note the interface
name used in the
PostDown entries, here it is
eth0 but if
yours differs (check by running
ip addr) change it accordingly.
[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
ListenPort chosen earlier is copied here if changed from the default
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
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
[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 email@example.com systemctl start firstname.lastname@example.org 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!