Disclaimer

This is not a tutorial, just brief description of a working configuration. You should be at least vaguely familiar with wireguard before attempting to do something like this. That being said, I am a networking novice, so please do not blindly copy and paste these configs.

Setup

I wish to access the resources of a local network remotely. For this, I have 3\geq 3 peers: a central server SS publicly accessible, a gateway Server GG behind NAT (in the local network), and multiple clients C1,C2,...C_1, C_2, .... All clients CiC_i and GG connect to SS.

IPName
192.168.10.0/24The private network for the VPN
192.168.10.1The public server S
192.168.10.2The Gateway device G
192.168.10.3-nThe other clients C
100.100.20.0/24G’s local network (This has to be different to the network above)

Routing

By setting AllowedIPs = 0.0.0.0/0, ::0/0 for GG on SS, all traffic will by default be routed through GG. But this also means, that all of SS’s traffic will be routed through GG, making SS inaccessible from outside GG. To avoid this, we configure a separate routing table.

IP forwarding

For this to work, S and G need ip forwarding to be enabled.

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1

Or alternatively, use the PreUp command specified below

Configuration

The configuration files below should be put into /etc/wireguard/wg0.conf and the connection can be automatically started by systemctl enable --now wg-quick@wg0.service.

Gateway config

[Interface]
Address = 192.168.10.3/32
PrivateKey = <...>

# IP forwarding (see section above)
PreUp = sysctl -w net.ipv4.ip_forward=1

# IP masquerading
PreUp = iptables -t mangle -I PREROUTING -i wg0 -j MARK --set-mark 0x30
PreUp = iptables -t nat -I POSTROUTING ! -o wg0 -m mark --mark 0x30 -j MASQUERADE
PostDown = iptables -t mangle -D PREROUTING -i wg0 -j MARK --set-mark 0x30
PostDown = iptables -t nat -D POSTROUTING ! -o wg0 -m mark --mark 0x30 -j MASQUERADE

# Server S
[Peer]
PublicKey = <...>
Endpoint = domain.of.server.s.example.com:51820
AllowedIPs = 192.168.10.0/24
PersistentKeepalive = 20

Server config

[Interface]
Address = 192.168.10.1/24
ListenPort = 51820
PrivateKey = <...>

# Do not pass the server's traffic through G, only other wg0 traffic
Table = 123
PreUp = ip rule add iif wg0 table 123 priority 456
PostDown = ip rule del iif wg0 table 123 priority 456
PreUp = ip -6 rule add iif wg0 table 123 priority 456
PostDown = ip -6 rule del iif wg0 table 123 priority 456

# Gateway
[Peer]
PublicKey = <...>
AllowedIPs = 0.0.0.0/0, ::0/0

# Client C_1
[Peer]
PublicKey = <...>
AllowedIPs = 192.168.10.3/32

# Client C_2
[Peer]
PublicKey = <...>
AllowedIPs = 192.168.10.4/32

...

Client C_1 config

[Interface]
PrivateKey = <...>
Address = 192.168.10.3/32

[Peer]
PublicKey = <...>
Endpoint = domain.of.server.s.example.com:51820
AllowedIPs = 0.0.0.0/0, ::0/0
PersistentKeepalive = 20