WireGuard & Traefik
Abstract
This article deals with setting up a WireGuard VPN service under Docker, so you can roam in and out of your home WiFi, and keep using home services (like pihole) when you're out and about!
Table of Contents
Introduction
You may or may not have heard about WireGuard. It's a "challenger" VPN protocol, which is smaller, faster and lighter than many other open source VPN clients out there, such as OpenVPN.
If you've got Docker setup, and/or you've been through our other article, you may want to try WireGuard out for yourself - so here are some instructions for you :)
Prerequisites
This article assumes you have the following:
A home server running Docker and Traefik (steps per our other article - this assumes you're on that footing!)
An internet connection (you're reading this, aren't you?)
A router with some ability to amend IP config/routing (if required) and port forwarding (required)
A domain name, with registrar that provides an ACME V2 API (i.e. one of the ones listed here...)
A mobile internet device to test external things quickly via 3G/4G or other connection (Android ideally...)
Time
Step 1 : DNS Prep
Login to your DNS provider and create a DNS record that points wg.yoursubdomain.yourdomain.com to the external IP of your Traefik/Docker host.
NOTE : We're using "wg" (and not "wireguard") for a little bit of obfuscation.
The above sets up a valid external DNS record to point to your external IP.
Step 2 : Router Prep
Login to your router and setup a port forward for external port 51820 to point to port 51820 of the internal IP of your Traefik/Docker/OMV host.
The above lets any traffic on port 51820 hitting your external IP be routed to your OMV host.
Step 3 : Traefik Config
Login to your Traefik/Docker/OMV server
Run
sudo nano /srv/dev-disk-by-label-OMVDataVolume/Docker/traefik/traefik.tomlPress CTRL-W and type [entryPoints] and press return
Underneath the [entrypoints] line, type (or paste) the following
[entryPoints.wireguard]
address = ":51820/udp"Press Ctrl-X and press Y to save the file
Run
sudo docker restart traefik
The above means Traefik will now be listening for traffic on port 51820
Run
sudo nano /srv/dev-disk-by-label-OMVDataVolume/Docker/traefik/dynamic/dynamic.tomlFind the section that reads:
[http.middlewares.internal-only-ip]
[http.middlewares.internal-only-ip.ipWhitelist]
sourceRange = [ "192.168.1.0/24" ]
Change the sourceRange line above to read:
sourceRange = [ "192.168.1.0/24","172.17.0.0/16" ]
The above means that any Docker containers you have fronted with Traefik "internal only" IP restrictions will now allow you to access them when you are connected via WireGuard.
Step 4 : Install WireGuard
Login to your Traefik/Docker/OMV server and get to a command prompt
Create a folder within your Docker shared folder, ala
sudo mkdir /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard
First off, fire up your Portainer GUI and login
Navigate into your Docker instance
Click on Containers
Click on + Add container
For Image, use linuxserver/wireguard:latest
Scroll down to the Advanced Container Settings section
Click the Volumes tab
Click + map additional volume twice
Change both mappings to be Bind mounts
Set the first container to be /config
Set the first host to be /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard
Set the second container to be /lib/modules
Click the Network tab
Ensure Network is set to bridge
Set the Hostname to wireguard
Set the Domain Name to be your subdomain.yourdomain.com
Set your Primary DNS Server IP to be your pihole DNS IP
Click the Env tab
Click + add environment variable four times
Set the first entry to be called TZ with a value of your timezone (i.e. Europe/London)
Set the second entry to be called PEERS with a value of 1
Set the third entry to be called PEERDNS with a value of your pihole DNS IP
Set the fourth entry to be called SERVERURL with a value of wg.yoursubdomain.yourdomain.com
Click the Labels tab
Click + add label four times
Set the first entry to be called traefik.enable with a value of true
Set the second entry to be called traefik.udp.routers.wireguard.entrypoints with a value of wireguard
Set the third entry to be called traefik.udp.routers.wireguard.servicewith a value of wireguard
Set the fourth entry to be called traefik.udp.services.wireguard.loadbalancer.server.port with a value of 51820
Click the Restart policy tab
Change the Restart policy to Unless stopped
Click the Capabilities tab
Ensure NET_ADMIN is enabled
Scroll up a little and click Deploy the container
WireGuard will now startup, and create the first peer (connection) configuration. The Traefik labels will also mean that Traefik will know to route port 51820 traffic hitting your host, to the WireGuard container.
Step 5 : Install & configure the WireGuard mobile app
On your mobile, install WireGuard (Google Play Store) (Apple Store)
On your server, maximise your console (full screen!) and execute the following:
docker exec wireguard /app/show-peer 1On our mobile, start the WireGuard app (at this point we're presuming you're on Android - we don't have iPhone!)
Click the blue + button to add a new connection
Select SCAN FROM QR CODE
Point your mobile camera at the QR code in your terminal window
You should now have an interface listed on the main page of WireGuard. Click it (but not the enable switch)
Click the pen/edit icon
In the Interface section
Change Name to be something meaningful to you, like My VPN
Change DNS Servers to be your pihole DNS IP (internal)
Scroll down to the Peer section
Check Endpoint is wg.yoursubdomain.yourdomain.com:51820
Change Allowed IPs to be 192.168.1.0/24 , 10.13.13.0/24
NOTE : If you want to use Android's "Always On VPN" setting, read item 5 in the Gotchas section at the bottom
Click the disk/Save icon at the top
NOTE: 10.13.13.0/24 is the "internal" default network WireGuard creates for itself. Outside of WG and client config, you don't need to worry about it, it does all the iptables SNAT etc itself.
Step 6 : Test WireGuard!
On your mobile
Disconnect WiFi
Start WireGuard
Next to your connection, drag the slider on your VPN connection to start the connection
Try to access an internal resource, like traefik.yoursubdomain.yourdomain.com
NOTE: Assuming all is well, your WireGuard client will have connected over your 3G/4G connection, looked up your external IP, and (via port forwarding) connected to it. That's it. You're in.
If you're using pihole, you will now find that when connected, your DNS returns internal IP addresses, which WireGuard will route for you. Anything not internal will use your 3G/4G connection as normal. If you're also using an upstream DNS filter (i.e. Cisco OpenDNS) you will now benefit from DNS filtering "on the move".
Now switch on your mobile WiFi and check you can still access everything (internal and external)
NOTE: You don't (or shouldn't) need an internal IP entry for wg.yoursubdomain.yourdomain.com - as most routers should "hairpin" correctly - i.e. internally your pihole will resolve the external address, and try to connect to it. Your router should "spot" the attempt to connect to the external IP and just re-route it internally. It should "just work".
Step 7 : Set it and forget it!
If you're happy it's working, then you might want to consider switching WireGuard on permanently, which (on Android) you can do via Settings > Network > Advanced > VPN, selecting WireGuard and then switching Always-on VPN to On.
Step 8 : Enhancements
8.1 Speedier Access to wg
Rather than keep having to "docker exec" shell into your WireGuard container, do this:
Login to your Traefik/Docker/OMV server and get to a command prompt
Cut/paste the following
sudo bash
cat << EOF > /usr/local/sbin/wg
#!/bin/bash
for var in "$@"
do
argstopass="$argstopass $var"
done
exec /usr/bin/docker exec wireguard wg $argstopass
EOF
chmod u+rx /usr/local/sbin/wg
Now if you run "wg" from your command line, it will run 'in' the container and pass out put back to you. Running wg on it's own will show you client status etc.
8.2 Pre-Shared Keys
If you want to, you can add a pre-shared key (PSK) to WireGuard on a per device basis. This means that if someone got your QRCode or keys, they still couldn't access the network.
If you've setup the wg script in 8.1, you can just run
wg genpsk
...and WireGuard will generate you a random PSK to use. Once you have one you want, copy it, and then run
sudo nano /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard/wg0.conf
Find the section that starts #peer1 and under the line that starts PublicKey add a new line which reads:
PreSharedKey = <the key that wg genpsk generated earlier>
Press CTRL X and then Y, and RETURN to save your file, and then run
docker restart wireguard
Your mobile client will get disconnected as it now doesn't match config. This isn't a problem, simply:
Load the WireGuard client on your mobile
Click your VPN connection name
Click the pen/edit icon
Scroll down to the Peer section
Enter the PSK in the "Pre-shared key" box
Click the disk/save icon
Click the left/back arrow
Disable and re-enable your VPN connection
Check things are working :)
NOTE: The author likes to use Google Keep (or GMail) to get the PSK to the device without typing it in...
NOTE: You can also do the above edit in the peer1/peer1.conf file - so if you need to regenerate your QR Code it will generate it including the PSK - although that's obviously less safe, as "all your eggs are in one basket"
8.3 Add another device
Want to add another device? Don't worry, run this from a root shell on your host:
docker exec -it wireguard /bin/bash
/app/add-peer
exit
Gotchas
If you're using pihole and you've set it as your WireGuard client DNS - you might find some external services (apps etc) get blocked by your pihole. The author has found some mobile parking apps use adverts/trackers, which get blocked when out and about, which prevents using the app. This can be confusing if you forget WireGuard and pihole are in play :)
If you find this happens, either disconnect WireGuard, or disable (temporarily) pihole and then figure out which sites were blocked by looking at the pihole logs.Enabling "Block connections without VPN" (on Android at least) means all traffic will go over WireGuard, which may not be ideal in all circumstances. If you disable (or don't enable) "Block connections without VPN" then you'll effectively get "split tunnelling" - access to your private LAN will go over WireGuard but "other" (i.e. internet) traffic will go as normal. If you enable "Private DNS" (such as diab) then regardless of the "Block connections without VPN" setting, your DNS traffic will always go to your server. You need to choose the level of security that is right for you.
Internal logs will now be bit "wrong". Your external (mobile) IP won't show up in your internal logs, and neither will the WireGuard 10.13.13.0/24 network. Unless you varied from the above, WireGuard will be running in bridge mode (on a 172.17.0.0/16 network) which in turn will transit onto your main (192.168.1.0/24) network via the macvlan "dockershim" adaptor. Any WireGuard client will appear to be coming from/behind the dockershim IP. If you move WireGuard onto the "host" network, you'll still appear from the host IP, and not the end device.
On one occasion, a "docker pull" update of the WireGuard image overwrote the wg0.conf with PSKs. It is worth making a backup of your WireGuard folder and contents
Lost your configuration? Don't worry - WireGuard saved it all in /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard/peerX (where X is the number of your peer client config)
If you need to get a QRCode back up again, run this from a root shell on your host:
docker exec -it wireguard /bin/bash
/app/show-peer 1
exit
Replace the 1 above with whichever peer you want to redisplayIf you want to use Android's "Always On VPN" setting, you'll need to change Step 5, item 10, point 2 from:
Change Allowed IPs to be 192.168.1.0/24 , 10.13.13.0/24
to
Change Allowed IPs to be 192.168.1.0/24 , 10.13.13.0/24, 0.0.0.0/0
You can then enable "Always On VPN" and everything will route via your VPN.