Nested WireGuard tunnels to my home
My setup to safely (I hope) access my home network via WireGuard VPN.
TL;DR: WG from my home to a VPS, to expose another WG endpoint bypassing CGNAT.
I love the simplicity of WireGuard. It feels to me like fresh air when compared to the disorienting huge amount of OpenVPN options, or to the weapons-grade accuracy needed to set up an IKE2/IPsec tunnel. Maybe one day I’ll try iked(8) and I’ll love it, but for now I think setting up IPsec for a road warrior device is a nighmare. I’d still like to know why a W1nd0w$ PC wasn’t liking the IKE chiper suites I was offering him a couple years ago.
I simply want a tunnel between my PC and my home network, using modern cryptography. The problem is my ISP hiding my home behind CGNAT. For my road warrior PC to be able to create the tunnel, the other WG peer must be reacheable.
First of all, since I had a nice MikroTik RB750Gr3 doing nothing, it was chosen to serve my home. RouterOS 6 doesn’t support WG. At the time of writing, ROS 7.1 beta with WG support is in testing and not very stable, but I have no choice so that’s what I’m using.
Since this MT is not directly facing WAN, it acts as a router-on-a-stick. Routing and firewall rules set on my main firewall that make all this work are beyond the scope of this post, and more related to my LAN.
Now I need a VPS to bypass CGNAT. The first way I considered was this:
+--------+ +--------+ +--------+ | | | | | | | =========== =========== | | ====wg0==== ====wg1==== | | | | | | | +--------+ +--------+ +--------+ PC VPS MT
PC creates a tunnel to VPS. VPS routes packets destinated to MT into tunnel wg1. This should work, but packets are not encrypted when they are routed between wg0 and wg1.
What if I don’t trust the VPS? The next diagram shows what I ended up with.
+--------+ +--------+ +--------+ | | =================== | | ============================ | | ====wg0===================== | | | ============wg1==== | +--------+ +--------+ +--------+ PC VPS MT
Here wg1 is used to expose another WG endpoint (on another port) of MT. When PC creates wg0, VPS doesn’t get to see the end of the tunnel, it can only see encrypted WG traffic.
To implement this, starting from MT, we have to declare:
- two WG interfaces;
- one peer for wg1;
- one peer for each road warrior device, for wg0.
[admin@MikroTik_hEX] > /interface/wireguard/print Flags: X - disabled; R - running 0 R name="wg0" mtu=1420 listen-port=60000 private-key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" public-key="njg+gAB5Zt/NJl2U8HUA6+vTJvjOIdSLhpbw7SxpADA=" 1 R name="wg1" mtu=1420 listen-port=60001 private-key="YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" public-key="XKm72iBBljwo3l2YAIvzO4RnbR5HitAh9UoelWjBQ3U=" [admin@MikroTik_hEX] > /interface/wireguard/peers/print Columns: INTERFACE, PUBLIC-KEY, ENDPOINT-ADDRESS, ENDPOINT-PORT, ALLOWED-ADDRESS, PERSISTENT-KEEPALIVE # INTERFACE PUBLIC-KEY ENDPOINT-ADDRESS ENDPOINT-PORT ALLOWED-ADDRESS PERSISTENT-KEEPALIVE 0 wg1 q9ii3/DoyZgqYkzEiTdPM9FfraDtUJdKP1KItmh6c1U= X.X.X.X 60100 172.16.1.0/24 25s ;;; RW1 1 wg0 TvfgJvb9Zn37tJNxWU9Lyf+/TWQQy3rFZq4xBILCRwI= 0 172.16.0.1/32 ;;; RW2 2 wg0 AFSycdpkMPYm9Fj32MB4fjiOpP306omN06WMtzo0s3g= 0 172.16.0.2/32 [admin@MikroTik_hEX] > /ip/address/print where interface=wg1 Columns: ADDRESS, NETWORK, INTERFACE # ADDRESS NETWORK INTERFACE 1 172.16.1.254/24 172.16.1.0 wg1
I redacted portions of the config, but it’s quite clear.
X.X.X.X is the public
IP of VPS. MT initiates the tunnel to
X.X.X.X:60100. Note that
persistent-keepalive=25s is useful to persist the state that lets us traverse
CGNAT. Otherwise, after some time of inactivity, firewalls on the path would
drop the state, and the tunnel could not be recreated trying to send a packet
from VPS to MT.
On the VPS: Alpine Linux, wireguard-tools, ifupdown-ng-wireguard.
# cat /etc/wireguard/wg1.conf [Interface] ListenPort = 60100 PrivateKey = ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ # MT [Peer] PublicKey = XKm72iBBljwo3l2YAIvzO4RnbR5HitAh9UoelWjBQ3U= AllowedIPs = 172.16.1.254/32
Note that there’s no
wg0.conf since VPS is not aware of wg0.
With this config in place and the interface activated, MT should initiate the tunnel almost instantly, but for road warriors to be able to reach wg0 we have to forward packets into wg1.
On VPS, right after interface
wg1 goes up, we tell Linux to forward packets.
We also define a few iptables(8) dstnat rules to let the world
access wg0 port.
# cat /etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp hostname <redacted> iface eth0 inet6 static address <redacted> gateway fe80::1 pre-up echo 0 > /proc/sys/net/ipv6/conf/eth0/accept_ra auto wg1 iface wg1 inet static address 172.16.1.100/24 requires eth0 use wireguard post-up echo 1 > /proc/sys/net/ipv4/ip_forward post-up echo 1 > /proc/sys/net/ipv4/conf/all/proxy_arp post-up iptables -t nat -A PREROUTING -p udp -i eth0 --dport 60000 -j DNAT --to-destination 172.16.1.254 post-up iptables -t nat -A POSTROUTING -p udp -d 172.16.1.254 --dport 60000 -j MASQUERADE post-down iptables -t nat -D PREROUTING -p udp -i eth0 --dport 60000 -j DNAT --to-destination 1172.16.1.254 post-down iptables -t nat -D POSTROUTING -p udp -d 172.16.1.254 --dport 60000 -j MASQUERADE post-down echo 0 > /proc/sys/net/ipv4/ip_forward post-down echo 0 > /proc/sys/net/ipv4/conf/all/proxy_arp
Now from PC our favourite WG client might be able to complete the tunnel wg0.