3

I am attempting a chained/double-hop VPN setup where all client traffic passes through 2 servers before reaching the internet:

Client → Server1 → Server2 → Public Internet

All peers are on these private address blocks: 10.103.213.0/24 (IPv4) and fd6f:9403:2887:9cd6:10:103:213:0/112 (IPv6). Below are the configurations of the peers and the IPTables rules in place. (Please also note the names of the configuration files, in case it matters.)


Server2 Configuration

File: /etc/wireguard/wg0.conf

# Server2

[Interface]
PrivateKey = SERVER2_PRIVATE_KEY
Address = 10.103.213.2/24, fd6f:9403:2887:9cd6:10:103:213:2/112
ListenPort = 53701
SaveConfig = false

# CLIENTS

[Peer] # Server1
PublicKey = SERVER1_PUBLIC_KEY
PresharedKey = SERVER1_PRESHARED_KEY
# ↓ to allow traffic from client (10.103.213.11/32) via Server1 (10.103.213.1/32), allow both
AllowedIPs = 10.103.213.0/24, fd6f:9403:2887:9cd6:10:103:213:0/112

Firewall config. commands:

ufw allow 53701/udp comment 'WireGuard VPN'

iptables -A FORWARD -i wg0 -j ACCEPT &&
iptables -A FORWARD -o wg0 -j ACCEPT &&
ip6tables -A FORWARD -i wg0 -j ACCEPT &&
ip6tables -A FORWARD -o wg0 -j ACCEPT

iptables -t nat -A POSTROUTING -s 10.103.213.0/24 -o enp8s0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -s fd6f:9403:2887:9cd6:10:103:213:0/112 -o enp8s0 -j MASQUERADE

Server1 Configuration

File: /etc/wireguard/wg0.conf

# Server1

[Interface]
PrivateKey = SERVER1_PRIVATE_KEY
Address = 10.103.213.1/24, fd6f:9403:2887:9cd6:10:103:213:1/112
ListenPort = 53701
SaveConfig = false

# CLIENTS

[Peer] # Server2
PublicKey = SERVER2_PUBLIC_KEY
PresharedKey = SERVER1_PRESHARED_KEY
Endpoint = SERVER2_PUBLIC_IP:53701
AllowedIPs = 10.103.213.2/32, fd6f:9403:2887:9cd6:10:103:213:2/128
#PersistentKeepalive = 25

[Peer] # PC
PublicKey = CLIENT_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
AllowedIPs = 10.103.213.11/32, fd6f:9403:2887:9cd6:10:103:213:11/128

Firewall config. commands:

ufw allow 53701/udp comment 'WireGuard VPN'

iptables -A FORWARD -i wg0 -j ACCEPT &&
iptables -A FORWARD -o wg0 -j ACCEPT &&
ip6tables -A FORWARD -i wg0 -j ACCEPT &&
ip6tables -A FORWARD -o wg0 -j ACCEPT

Client Configuration

# CLIENT: PC

[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.103.213.11/24, fd6f:9403:2887:9cd6:10:103:213:11/112
DNS = 10.103.213.1, fd6f:9403:2887:9cd6:10:103:213:1

[Peer] # Server1
PublicKey = SERVER1_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
Endpoint = SERVER1_PUBLIC_IP:53701
AllowedIPs = 0.0.0.0/0, ::0/0
#PersistentKeepalive = 25

Now, like I said, I am able to ping Server1 (10.103.213.2) and Server2 (10.103.213.1) from the Client (10.103.213.11) on the private (WireGuard) network when the VPN is enabled on all peers, but I cannot access the internet.

I know I'm missing some much needed IP routes or IPTables rules, but despite trying to find a solution that I can understand for days, I've had no success. I see that there's little hope for me without actually reading a book on Linux networking/firewall.

For now, I am hoping someone can help me with a solution along with an explanation as to what we are doing and why, so I can understand better and take notes.

Thank you very much!


NOTES:

  • If I'm missing any useful information, please ask and I'll be happy to get it.

  • Client runs macOS. Server1 and Server2 run Debian 11 "Bullseye". Just an FYI, in case it's relevant.

  • Server1 and Server2 have Unbound installed and set up for local DNS resolution. That's why you see DNS = 10.103.213.1, fd6f:9403:2887:9cd6:10:103:213:1 in Client config. If not for that, I'd be using either CloudFlare or Google's IPs there.

  • Someone on #wireguard IRC suggested that I try adding Table = 123 under [Interface] in Server1's WireGuard config. and then running the command ip rule add iif wg0 table 123. That didn't work and I couldn't understand what it's supposed to do either (I couldn't make sense of the man pages or the technical details).

  • From my reading I came to the conclusion that iptables and ufw can be used together—except one has to be careful when using iptables-persistent. You need to run netfilter-persistent save even after running ufw commands for the firewall rules to be persistent across reboots even if ufw status says they are in place. If for some reason you reboot before saving, delete the UFW rules and add them again and then run netfilter-persistent save.

2
  • Mixing UFW and iptables doesn't look like a good idea to me.
    – iBug
    Oct 21, 2021 at 4:57
  • @iBug IDK, I did read up about it and pretty much came to the conclusion that they can be used together—except you have to be careful when using iptables-persistent (netfilter-persistent save even after running ufw commands). No others issues whatsoever.
    – its_me
    Oct 21, 2021 at 11:30

3 Answers 3

1

The kind folks over at #wireguard IRC channel on Libera.Chat helped me out!

The issues with my config. were as follows:

  • Server1's config has Server2 with AllowedIPs of just Server2's IP addresses. That wont permit "The Internet". It needs to be 0.0.0.0/0, ::0/0.

  • Server1's WireGuard interface must be configured to add routes (for all entries in AllowedIPs) to a custom IP routing table (let's call it wireguard2x) instead of the main table. Then add an IP policy rule that says that traffic with the input interface (iif) wg0 go to the custom table—which also means other kinds of traffic take the default route, per usual.


Corrected Configuration

Server2 Configuration

File: /etc/wireguard/wg0.conf

# Server2

[Interface]
PrivateKey = SERVER2_PRIVATE_KEY
Address = 10.103.213.2/24, fd6f:9403:2887:9cd6:10:103:213:2/112
ListenPort = 53701
SaveConfig = false

# CLIENTS

[Peer] # Server1
PublicKey = SERVER1_PUBLIC_KEY
PresharedKey = SERVER1_PRESHARED_KEY
# ↓ to allow traffic from client (10.103.213.11/32) via Server1 (10.103.213.1/32), allow both
AllowedIPs = 10.103.213.0/24, fd6f:9403:2887:9cd6:10:103:213:0/112

Firewall config. commands:

ufw allow 53701/udp comment 'WireGuard VPN'

iptables -A FORWARD -i wg0 -j ACCEPT &&
iptables -A FORWARD -o wg0 -j ACCEPT &&
ip6tables -A FORWARD -i wg0 -j ACCEPT &&
ip6tables -A FORWARD -o wg0 -j ACCEPT

iptables -t nat -A POSTROUTING -s 10.103.213.0/24 -o enp7s0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -s fd6f:9403:2887:9cd6:10:103:213:0/112 -o enp7s0 -j MASQUERADE

Server1 Configuration

File: /etc/wireguard/wg0.conf

# Server1

[Interface]
PrivateKey = SERVER1_PRIVATE_KEY
Address = 10.103.213.1/32, fd6f:9403:2887:9cd6:10:103:213:1/128
ListenPort = 53701
Table = wireguard2x
# ↓ should only be set if resolvconf or openresolv is installed on the system, otherwise let the system use defaults
# ↓ is unncessary if local DNS resolution is set up
#DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001
DNS = 10.103.213.1, fd6f:9403:2887:9cd6:10:103:213:1
SaveConfig = false

# CLIENTS

[Peer] # Server2
PublicKey = SERVER2_PUBLIC_KEY
PresharedKey = SERVER1_PRESHARED_KEY
Endpoint = SERVER2_PUBLIC_IP:53701
AllowedIPs = 0.0.0.0/0, ::0/0
#PersistentKeepalive = 25

[Peer] # PC
PublicKey = CLIENT_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
AllowedIPs = 10.103.213.11/32, fd6f:9403:2887:9cd6:10:103:213:11/128

Firewall config. commands:

ufw allow 53701/udp comment 'WireGuard VPN'

iptables -A FORWARD -i wg0 -j ACCEPT &&
iptables -A FORWARD -o wg0 -j ACCEPT &&
ip6tables -A FORWARD -i wg0 -j ACCEPT &&
ip6tables -A FORWARD -o wg0 -j ACCEPT

echo 123 wireguard2x >> /etc/iproute2/rt_tables
ip rule add iif wg0 table wireguard2x

Client Configuration

# CLIENT: PC

[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.103.213.11/32, fd6f:9403:2887:9cd6:10:103:213:11/128
DNS = 10.103.213.1, fd6f:9403:2887:9cd6:10:103:213:1

[Peer] # Server1
PublicKey = SERVER1_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
Endpoint = SERVER1_PUBLIC_IP:53701
AllowedIPs = 0.0.0.0/0, ::0/0
#PersistentKeepalive = 25

NOTES:

  • You can see all the policy routing rules that are currently in effect using this command: ip rule list or ip rule

  • View the routing tables with, for e.g., ip route show table wireguard2x or ip route list table wireguard2x.

  • To flush the route cache: ip route flush cache

  • Further reading: Linux Advanced Routing & Traffic Control (LARTC) HOWTO

  • You can monitor Client's network traffic flow—if on Linux, using sudo iptraf-ng; on macOS using sudo iftop. (To be run on the client.)

    • I found running that hard to parse due to too many live changes. So I was suggested trying route get 192.0.2.0 (which shows the interface being used, e.g., interface: utun2) and then running ifconfig <interface> (e.g., ifconfig utun2) on macOS. The latter should show the Client's private (WireGuard peer) IP address, e.g., 10.103.213.11, confirming that the traffic is being routed through WG interface. It's not much, but it's a start.

    • UDPATE: traceroute is brilliant for this! For e.g., traceroute 8.8.8.8. (Hat tip to Chrispus Kamau.)

  • curl ipinfo.io to check your IP address with the VPN enabled. (To be run on the client.)

RELATED:

1
  • except that AllowedIPs = 10.103.213.11/32 on one peer will clash with AllowedIPs = 0.0.0.0/0 on the other peer. Contrary to routes, there must be no overlap. Compute 0.0.0.0/0 minus 10.103.213.11/32 (eg with the help the netmask command) or use two different WG interfaces. The reason for this is explained there: wireguard.com/#cryptokey-routing . You can't have 10.103.213.11/32 matching two different peers (because it also matches 0.0.0.0/0)
    – A.B
    Feb 18, 2023 at 22:47
0

its_me's answer is working great for me. Below are some changes I made to automate the creation/removal of the route table, iptables and ip rules. The automation happens as the wireguard interface is brought up and down. Please note I am not using ipv6 or ufw in this configuration:

Server1:

# Add a route table for this interface
PreUp = echo 1 wireguard2x >> /etc/iproute2/rt_tables
# Add ip rule to point this interface at the new route table
PreUp = ip rule add iif %i table wireguard2x
# Setup iptables
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT

# Remove the iptables
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
# Find and remove the ip rule
PostDown = ip rule | grep "from all iif %i" | cut -d: -f1 | xargs -L1 ip rule del prio
# Remove the ip table
PostDown = sed -i '/wireguard2x/d' /etc/iproute2/rt_tables

Server2:

PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
2
  • Yep, I am aware of PostUp and PostDown in WG config. I just like all my firewall rules (including system & other app rules) to be in one place and therefore prefer adding them manually.
    – its_me
    Oct 21, 2021 at 10:36
  • @computerguy1 I tried your configuration and it can connect to Server-II but eventually lost SSH access to Server-I. Moreover, I can't connect to Server-I using any WireGuard client.
    – Aman Juman
    May 2, 2022 at 9:26
0

(Based on Chrispus Kamau's excellent Wireguard VPN tutorials for Typical Setup and Chained Setup, it appears I may have a solution (untested!) in a format that shows how somewhat unusual/complex setups could be handled—or at least give you an idea. Many thanks CK!)

Via: https://github.com/iamckn/chained-wireguard-ansible


How it works:

  • Client (vpn0) → 10.200.200.0/24 → (wg0) → Middleman (gate0) → 10.100.100.0/24 → (wg0) Gate (wg0) → Public Internet

  • Assumes that Unbound is set up on both the Middleman and Gate for local DNS resolution.

Gate Configuration

Configure the gate's VPN interface (wg0).

File: /etc/wireguard/wg0.conf

# SERVER

[Interface] # Gate
PrivateKey = GATE_PRIVATE_KEY
Address = 10.100.100.1/24
ListenPort = 53701
SaveConfig = false

# CLIENTS

[Peer] # Middleman
PublicKey = MIDDLEMAN_PUBLIC_KEY
PresharedKey = MIDDLEMAN_PRESHARED_KEY
AllowedIPs = 10.0.0.0/8

Firewall config. commands:

# Track VPN connection
## Track input chain
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
## Track forward chain
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Allow incoming WireGuard connections/VPN traffic on the listening port
iptables -A INPUT -p udp -m udp --dport 53701 -m conntrack --ctstate NEW -j ACCEPT

# Allow both TCP and UDP recursive DNS traffic
iptables -A INPUT -s 10.100.100.0/24 -p tcp -m tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -s 10.100.100.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT

# Allow forwarding of packets that stay in the VPN tunnel
iptables -A FORWARD -i wg0 -o wg0 -m conntrack --ctstate NEW -j ACCEPT

# Set up NAT
iptables -t nat -A POSTROUTING -s 10.100.100.0/24 -o enp7s0 -j MASQUERADE

Bring up the gate's VPN interface, and enable the WireGuard service to automatically restart on boot:

wg-quick up wg0
systemctl enable wg-quick@wg0

Middleman Configuration

Configure the middleman's gate-facing interface (gate0). Here middleman acts as the client.

File: /etc/wireguard/gate0.conf

[Interface] # Middleman
PrivateKey = MIDDLEMAN_PRIVATE_KEY
Address = 10.100.100.2/32
DNS = 10.100.100.1
SaveConfig = false

# PEERS

[Peer] # Gate
PublicKey = GATE_PUBLIC_KEY
PresharedKey = MIDDLEMAN_PRESHARED_KEY
Endpoint = GATE_PUBLIC_IP:53701
AllowedIPs = 0.0.0.0/0
#PersistentKeepalive = 21

Configure the middleman's client-facing interface (wg0). Here middleman acts as the server.

File: /etc/wireguard/wg0.conf

# SERVER

[Interface] # Middleman
PrivateKey = MIDDLEMAN_PRIVATE_KEY
Address = 10.200.200.1/24
ListenPort = 53701
SaveConfig = false

# CLIENTS

[Peer] # PC
PublicKey = CLIENT_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
AllowedIPs = 10.200.200.2/32

Firewall config. commands:

iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

iptables -A INPUT -p udp -m udp --dport 53701 -m conntrack --ctstate NEW -j ACCEPT

iptables -A INPUT -s 10.200.200.0/24 -p tcp -m tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -s 10.200.200.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT

iptables -A FORWARD -i wg0 -o wg0 -m conntrack --ctstate NEW -j ACCEPT

iptables -t nat -A POSTROUTING -s 10.200.200.0/24 -o enp41s0 -j MASQUERADE

# Set up VPN chain NAT
iptables -t nat -A POSTROUTING -s 10.200.200.0/24 -j SNAT --to-source 10.100.100.2

Configure policy routing on the middleman to route traffic from the client to the gate.

echo "1 middleman" >> /etc/iproute2/rt_tables

# Forward all traffic to the gate
ip route add 0.0.0.0/0 dev gate0 table middleman

# OR only forward traffic to 4.2.2.2 (for e.g.), to the gate
#ip route add 4.2.2.2/32 dev gate0 table middleman

ip rule add from 10.200.200.0/24 lookup middleman

Bring up the middleman's WireGuard interfaces, and enable the WireGuard service to automatically restart on boot:

wg-quick up gate0
systemctl enable wg-quick@gate0

wg-quick up wg0
systemctl enable wg-quick@wg0

Client Configuration

File: /etc/wireguard/vpn0.conf

[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.200.200.2/32
DNS = 10.200.200.1

# PEERS

[Peer] # Middleman
PublicKey = MIDDLEMAN_PUBLIC_KEY
PresharedKey = CLIENT_PRESHARED_KEY
Endpoint = MIDDLEMAN_PUBLIC_IP:53701
AllowedIPs = 0.0.0.0/0
#PersistentKeepalive = 21

Finally activate WireGuard on the client.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .