Chaining Wireguard Tunnels

Published: | Last Edited:
Category: tech | Tags: #vpn #wireguard

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

PrivateKey = XYZ123456ABC=                  # PrivateKey will be different              
Address =,fc00:bbbb:bbbb:bb01::5:ac80/128
DNS =                          # LAN address of the home server
FwMark = 51820                              # FwMark is important in this setup.

PublicKey = F+80gbmHVlOrU+es13S18oMEX2g=    # PublicKey will be different
AllowedIPs =,::0/0
Endpoint =

Start the tunnel and verify we are connected:

$ wg-quick up vpn-client

$ curl
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

Edit the /etc/wireguard/wg0.conf to include new forwarding rules:

$ cat /etc/wireguard/wg0.conf

Address =
PrivateKey = <redacted>
ListenPort = 51820
FwMark = 51820 # Make sure this value is the same as defined in vpn-client.conf

# replace enp2s0 with your actual network interface, e.g. eth0
# replace with your LAN address subnet

# Forwarding...
PostUp  = iptables -A FORWARD -o enp2s0 ! -d -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 -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


The rules and explanations are copied from this comment4:

  1. The first rule is about blocking the use of $INTERFACE (enp2s0) for anything but your LAN’s local machines (“kill-switch”).
  2. The second rule is about forwarding your client’s packets (unless rejected by the first rule before).
  3. 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).
  4. 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
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 or the curl command above. Also check whether local services are still accessible.


Next Post: 2023 San Jose Half Marathon Recap
Previous Post: On Vy's Birthday