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:

  1. A home server running Docker and Traefik (steps per our other article - this assumes you're on that footing!)

  2. An internet connection (you're reading this, aren't you?)

  3. A router with some ability to amend IP config/routing (if required) and port forwarding (required)

  4. A domain name, with registrar that provides an ACME V2 API (i.e. one of the ones listed here...)

  5. A mobile internet device to test external things quickly via 3G/4G or other connection (Android ideally...)

  6. Time

Step 1 : DNS Prep

  1. 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

  1. 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

  1. Login to your Traefik/Docker/OMV server

  2. Run
    sudo nano /srv/dev-disk-by-label-OMVDataVolume/Docker/traefik/traefik.toml

  3. Press CTRL-W and type [entryPoints] and press return

  4. Underneath the [entrypoints] line, type (or paste) the following
    [entryPoints.wireguard]
    address = ":51820/udp"

  5. Press Ctrl-X and press Y to save the file

  6. Run
    sudo docker restart traefik

The above means Traefik will now be listening for traffic on port 51820

  1. Run
    sudo nano /srv/dev-disk-by-label-OMVDataVolume/Docker/traefik/dynamic/dynamic.toml

  2. Find the section that reads:

[http.middlewares.internal-only-ip]

[http.middlewares.internal-only-ip.ipWhitelist]
sourceRange = [ "192.168.1.0/24" ]

  1. 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

  1. Login to your Traefik/Docker/OMV server and get to a command prompt

  2. Create a folder within your Docker shared folder, ala

sudo mkdir /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard

  1. First off, fire up your Portainer GUI and login

  2. Navigate into your Docker instance

  3. Click on Containers

    1. Click on + Add container

    2. For Image, use linuxserver/wireguard:latest

  4. Scroll down to the Advanced Container Settings section

  5. Click the Volumes tab

    1. Click + map additional volume twice

    2. Change both mappings to be Bind mounts

    3. Set the first container to be /config

    4. Set the first host to be /srv/dev-disk-by-label-OMVDataVolume/SFDocker/wireguard

    5. Set the second container to be /lib/modules

  6. Click the Network tab

    1. Ensure Network is set to bridge

    2. Set the Hostname to wireguard

    3. Set the Domain Name to be your subdomain.yourdomain.com

    4. Set your Primary DNS Server IP to be your pihole DNS IP

  7. Click the Env tab

    1. Click + add environment variable four times

    2. Set the first entry to be called TZ with a value of your timezone (i.e. Europe/London)

    3. Set the second entry to be called PEERS with a value of 1

    4. Set the third entry to be called PEERDNS with a value of your pihole DNS IP

    5. Set the fourth entry to be called SERVERURL with a value of wg.yoursubdomain.yourdomain.com

  8. Click the Labels tab

    1. Click + add label four times

    2. Set the first entry to be called traefik.enable with a value of true

    3. Set the second entry to be called traefik.udp.routers.wireguard.entrypoints with a value of wireguard

    4. Set the third entry to be called traefik.udp.routers.wireguard.servicewith a value of wireguard

    5. Set the fourth entry to be called traefik.udp.services.wireguard.loadbalancer.server.port with a value of 51820

  9. Click the Restart policy tab

    1. Change the Restart policy to Unless stopped

  10. Click the Capabilities tab

    1. Ensure NET_ADMIN is enabled

  11. 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

  1. On your mobile, install WireGuard (Google Play Store) (Apple Store)

  2. On your server, maximise your console (full screen!) and execute the following:
    docker exec wireguard /app/show-peer 1

  3. On our mobile, start the WireGuard app (at this point we're presuming you're on Android - we don't have iPhone!)

  4. Click the blue + button to add a new connection

  5. Select SCAN FROM QR CODE

  6. Point your mobile camera at the QR code in your terminal window

  7. You should now have an interface listed on the main page of WireGuard. Click it (but not the enable switch)

  8. Click the pen/edit icon

  9. In the Interface section

      1. Change Name to be something meaningful to you, like My VPN

      2. Change DNS Servers to be your pihole DNS IP (internal)

  10. Scroll down to the Peer section

      1. Check Endpoint is wg.yoursubdomain.yourdomain.com:51820

      2. 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

  11. 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!

  1. On your mobile

    1. Disconnect WiFi

    2. Start WireGuard

    3. Next to your connection, drag the slider on your VPN connection to start the connection

    4. 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".

    1. 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:

  1. Login to your Traefik/Docker/OMV server and get to a command prompt

  2. 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:

  1. Load the WireGuard client on your mobile

  2. Click your VPN connection name

  3. Click the pen/edit icon

  4. Scroll down to the Peer section

  5. Enter the PSK in the "Pre-shared key" box

  6. Click the disk/save icon

  7. Click the left/back arrow

  8. Disable and re-enable your VPN connection

  9. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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

  5. 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 redisplay

  6. If 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.