I spent the last weekend attempting to chain two Wireguard tunnels: one that I use to connect to my home network and the other to forward my home network traffic to an external VPN of choice. This setup allows me to access local network and has all the benefits of using an external VPN while we are on the go.
I am already using Wireguard so the first tunnel has been set. However, setting up the second tunnel is a challenging feat. There are some articles1,2,3 that provide valuable information for this setup. The notes below are my adaptation or workaround based on them.
Set up external VPN tunnel
Download the generated Wireguard config by your VPN service of choice. I’m currently testing with Mullvad. Also modify the
DNS and add
FwMark like below:
$ cat /etc/wireguard/vpn-client.conf [Interface] PrivateKey = XYZ123456ABC= # PrivateKey will be different Address = 10.68.172.129/32,fc00:bbbb:bbbb:bb01::5:ac80/128 DNS = 192.168.1.10 # LAN address of the home server FwMark = 51820 # FwMark is important in this setup. [Peer] PublicKey = F+80gbmHVlOrU+es13S18oMEX2g= # PublicKey will be different AllowedIPs = 0.0.0.0/0,::0/0 Endpoint = 188.8.131.52:51820
Start the tunnel and verify we are connected:
$ wg-quick up vpn-client $ curl https://am.i.mullvad.net/connected You are connected to Mullvad ...
Set up home network tunnel
This tunnel named
wg0 should already exist by following this guide.
$ sudo wg ... interface: wg0 public key: <redacted> private key: (hidden) listening port: 51820 fwmark: 0xca6c ...
The home tunnel came with a generated
/etc/systemd/system/wg-iptables.service that interfered with this setup. We will need to remove it.
$ systemctl stop wg-iptables.service $ systemctl disable wg-iptables.service
/etc/wireguard/wg0.conf to include new forwarding rules:
$ cat /etc/wireguard/wg0.conf ... [Interface] Address = 10.7.0.1/24 PrivateKey = <redacted> ListenPort = 51820 FwMark = 51820 # Make sure this value is the same as defined in vpn-client.conf # IMPORTANT: # replace enp2s0 with your actual network interface, e.g. eth0 # replace 192.168.1.0/24 with your LAN address subnet # Forwarding... PostUp = iptables -A FORWARD -o enp2s0 ! -d 192.168.1.0/24 -j REJECT PostUp = iptables -A FORWARD -i %i -j ACCEPT PostUp = iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT PostUp = iptables -A FORWARD -j REJECT PreDown = iptables -D FORWARD -o enp2s0 ! -d 192.168.1.0/24 -j REJECT PreDown = iptables -D FORWARD -i %i -j ACCEPT PreDown = iptables -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT PreDown = iptables -D FORWARD -j REJECT # NAT... PostUp = iptables -t nat -A POSTROUTING -o enp2s0 -j MASQUERADE PostUp = iptables -t nat -A POSTROUTING -o vpn-client -j MASQUERADE PreDown = iptables -t nat -D POSTROUTING -o enp2s0 -j MASQUERADE PreDown = iptables -t nat -D POSTROUTING -o vpn-client -j MASQUERADE # BEGIN_PEER [Peer] ...
The rules and explanations are copied from this comment4:
- The first rule is about blocking the use of $INTERFACE (enp2s0) for anything but your LAN’s local machines (“kill-switch”).
- The second rule is about forwarding your client’s packets (unless rejected by the first rule before).
- The third rule is about allowing traffic that is related to or belongs to already established connections (e.g. responses that clients are interested in).
- The last rule blocks everything else which also prevents your VPN provider from accessing your clients or LAN’s local machines and also blocks your LAN’s local machines from initiating connections to your client(s).
Connect the dots
Spin up both tunnels if not already done so. Verify we are still connected to VPN.
$ wg-quick up vpn-client $ wg-quick up wg0 $ curl https://am.i.mullvad.net/connected You are connected to Mullvad ...
From client, connect to the home net work tunnel using the existing client conf and verify network traffic is routed through our VPN service. This can be done by checking for the IP on https://whatismyipaddress.com or the curl command above. Also check whether local services are still accessible.