Configuring Wireguard as a reverse tunnel

Stretching your home network for fun
Notes homeassistant wireguard network linux

What?

I configured Wireguard as a reverse tunnel between my home network and a VPS.

I used OpenVPN for this exact purpose previously, and while it worked great I wanted to test out Wireguard.

Why?

I run Home Assistant and other services at my home which I’d like to access from outside my own network. However, my ISP does not provide me with a permanent IP. This means that I cannot simply point a domain towards my IP, as it will break in the future when it changes. A common solution for this is to use one of the countless Dynamic DNS (DDNS) providers, but I wanted more control over my own setup.

Also as a precaution, I’d like my Home Assistant server not to be associated to my home IP. This has (in my opinion) advantages for security and anonymity, as internet scanners and the like will not be able to associate me with my home services.

This is also a good solution for those with ISPs that might not allow you hosting servers at home, or if you are behind CGNAT.

Note, if all you need is to expose Home Assistant in a safe and secure way, then I highly recommend just subscribing to Home Assistant Cloud via Nabu Casa instead. It’s way easier than this, and you will be supporting the further development of Home Assistant.

How?

Wireguard is ran on two hosts in different networks to stretch the home network towards the VPS IP endpoint.

The VPS host (hereby referred to as wg-vps) has a public IP associated to it. The host at home (referred to as wg-home) has a private IP and is behind a NAT’ed connection.

As our home connection is behind NAT, and it’s upstream router has a changing IP, we need to perform the VPN connection in reverse. Eg. wg-vps cannot connect to wg-home, as it’s not directly reachable (and we do not want it to be), therefore we need to configure wg-home to be the one to establish the connection instead and create the tunnel.

Technical details

The network we are creating here consists of several subnets.

  1. wg-vps has it’s own local network on whatever cloud it exists on (example 172.16.1.31/24). This is not relevant for the setup
  2. wg-vps also has a direct connection to the internet via a Public address (example 93.184.216.34)
  3. wg-home resides in a local home network connection with a RFC-1918 address (example 192.168.1.30/24)
  4. In addition to these, we will create a subnet for the tunnel connection between wg-vps and wg-home. This may be what-ever you choose yourself, as long as it’s not used for anything else (example, 192.168.137.0/24)

Setup guide

  1. Install Wireguard on both hosts. If you use Ubuntu, or a related distro its as easy as sudo apt install wireguard
  2. There are multiple ways of configuring Wireguard, you may either use the wg commandline directly, or use the wg-quick wrapper script. After fumbling with both, I decided that using wg-quick and a wireguard .conf file fit my usecase the best.

Initial configuration of wg-vps

# Everything is done as root
sudo su

# Generate a private-key
wg genkey > /etc/wireguard/private.key
chmod go= /etc/wireguard/private.key

# Generate public key
cat /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key

# Configure /etc/wireguard/wg0.conf, replace <contents> with proper values

[Interface]
Address = <Custom wireguard network address (eg. 192.168.137.2/24>
SaveConfig = false
ListenPort = 51820
PrivateKey = <Key from /etc/wireguard/private.key>

[Peer]
PublicKey = <Leave blank for now, should be public.key from wg-home afterwards>
AllowedIPs = <Comma-seperated list of networks to make available on wg-vps, eg. 192.168.0.0/24, 192.168.1.0/24>
PersistentKeepalive = 60

Configuring wg-home

# Everything is done as root
sudo su

# Generate a private-key
wg genkey > /etc/wireguard/private.key
chmod go= /etc/wireguard/private.key

# Generate public key
cat /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key

# Configure /etc/wireguard/wg0.conf, replace <contents> with proper values

[Interface]
Address = <Custom wireguard network address (eg. 192.168.137.1/24>
SaveConfig = false
ListenPort = 51820
PrivateKey = <Key from /etc/wireguard/private.key>

[Peer]
PublicKey = <Contents of /etc/wireguard/public.key from _wg-vps_>
AllowedIPs = <Custom wireguard network (eg. 192.168.137.0/24)
Endpoint = <Public IP of wg-vps with port-designation, eg. 93.184.216.34:51820>
PersistentKeepalive = 60

Start and enable Wireguard on boot

systemctl start wg-quick@wg0.service
systemctl enable wg-quick@wg0.service

Configuring NAT on wg-home

# Create /etc/nftables.d/nat.conf with the following contents, replace "ens18" with your network interface (get from 'ip link')
table ip nat {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
        }

        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                oifname "ens18" masquerade
        }
}

## Ensure that /etc/nftables.conf looks similar to this, check that it includes /etc/nftables.d/*.conf like so:
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
        }
}

include "/etc/nftables.d/*.conf"
- Uncomment "net.ipv4.ip_forward=1" in /etc/sysctl.d/99-sysctl.conf

Enable nftables to set changes on boot
$ systemctl enable nftables

$ reboot

Finish configuration of wg-vps

  1. Set PublicKey in /etc/wireguard/wg0.conf to contents of /etc/wireguard/public.key from wg-home
  2. Start and enable Wireguard on boot
systemctl start wg-quick@wg0.service
systemctl enable wg-quick@wg0.service

Check configuration

After you’ve started Wireguard via the systemctl start wg-quick@wg0.service on both hosts you should be up if you’ve configured everything. To ensure, we can check with the following steps:

# The "wg show" command on either host should show you that the peer is connected, check the latest handshake. 
$ wg show
interface: wg0
  public key: XXXXXXXXXXXXXXXXXXXXXXXXXXX
  private key: (hidden)
  listening port: 60193

peer: XXXXXXXXXXXXXXXXXXXXXXXXXXX
  endpoint: 93.184.216.34:51820
  allowed ips: 192.168.137.0/24
  latest handshake: 2 minutes, 22 seconds ago
  transfer: 1.43 MiB received, 10.63 MiB sent
  persistent keepalive: every 1 minute

# You should be able to ping the other Wireguard instance on the address you gave it in wg0.conf. Eg, from node with address 192.168.137.1, ping 192.168.137.2:
$ ip address show dev wg0
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 192.168.137.2/24 scope global wg0
       valid_lft forever preferred_lft forever

$ ping 192.168.137.1
PING 192.168.137.1 (192.168.137.1) 56(84) bytes of data.
64 bytes from 192.168.137.1: icmp_seq=1 ttl=64 time=19.9 ms
64 bytes from 192.168.137.1: icmp_seq=2 ttl=64 time=16.1 ms
64 bytes from 192.168.137.1: icmp_seq=3 ttl=64 time=56.2 ms
^C

# If you also did the NAT masquerade steps, you should be able to ping other hosts in your home network from wg-vps:
$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=19.9 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=16.1 ms
64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=56.2 ms
^C

Done!

And thats it! Hopefully this page found it’s way and was of help. These notes are quite rough and based on my bash-history after the fact, so I’ve might have missed something. If you are stuck even after reading this, then feel free to contact me at andreas@whynot.guide .

Remember to enable automatic upgrades on your hosts, and to ensure that firewall rules are in place and sane.

Buy Me a Coffee at ko-fi.com