Score:0

Routing traffic between A and C using A-B wireguard's network

in flag

I'm having some difficulties setting up a routing between 3 nodes. Here's the situation:

  • A, B and R are wireguard (wg0) peers
    • R is a public hub (vpn.example.com) with IP 172.0.0.1
    • A is a peer connected to R with IP 172.0.0.10
    • B is a peer connected to R with IP 172.0.0.20
  • B is also connected to a LAN with IP 192.168.1.20
    • C is a host in the same LAN with IP 192.168.1.30
  • Firewall on R that accepts traffic on wg0 and public network towards wireguard (51820/udp).

I would like to route traffic so that A and C can communicate and 192.168.1.30 was reachable from A.

Naively I thought to set up a routing in this way:

B# sysctl net.ipv4.ip_forward=1
A# ip route add 192.168.1.30/32 via 172.0.0.20 dev wg0
C# ip route add 172.0.0.10/32 via 192.168.1.20 dev eth0

this does not work though: if I tcpdump traffic on A, I can see packages going from A to C:

A# tcpdump -n -l -i wg0 | grep 192
17:27:47.492184 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 10, seq 1, length 64
17:52:36.908342 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 10, seq 2, length 64
17:52:37.921042 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 10, seq 3, length 64
17:52:37.932100 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 10, seq 4, length 64

but there's no answer and it even seems that those packets never reach R, even if apparently the routing is set up correctly:

A# ip route get 192.168.1.30
192.168.1.30 dev wg0 src 172.0.0.20 uid 1000
    cache

So I thought that it might wireguard. There's an AllowedIPs option that might be useful, so I changed the configurations like this:

# wg0.conf on A and B
[Interface]
Address = 172.0.0.10/24
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAA

[Peer]
PublicKey = rrrrrrrrrrrrrrrrrrrrrrrrrr
Endpoint = vpn.example.com:51820
AllowedIPs = 172.0.0.0/24, 192.168.1.30/32 # added second addr

on R I changed it like this:

# wg0.conf on R, the VPN hub
[Interface]
Address = 172.0.0.1/24
PrivateKey = RRRRRRRRRRRRRRRRRRRRRRRRRR

[Peer]
PublicKey = aaaaaaaaaaaaaaaaaaaaaaaaaa
AllowedIPs = 172.0.0.10/32, 192.168.1.30/32 # added second addr

[Peer]
PublicKey = bbbbbbbbbbbbbbbbbbbbbbbbbb
AllowedIPs = 172.0.0.20/32, 192.168.1.30/32 # added second addr

and rebooted wireguard on all hosts.

Now I can see some traffic flowing from A to R, but it doesn't seem to get to B:

A# tcpdump -n -l -i wg0 | grep 192.168
...
18:11:15.421908 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 25, seq 50, length 64
...

R# tcpdump -n -l -i wg0 | grep 192.168
...
18:11:15.421908 IP 172.0.0.10 > 192.168.1.30: ICMP echo request, id 25, seq 50, length 64
...

B# tcpdump -n -l -i wg0 | grep 192.168

The route on B doesn't seem right:

B# ip route get 192.168.1.30
192.168.1.30 dev wg0 src 172.0.0.20 uid 30042
    cache

...in fact, I cannot ping C anymore from B... But that's expected since I changed the route in B's wireguard configuration. So I rolled it back and B can ping C, but packets are still not received.

Now I'm stuck: I don't get what's going on and why packets are not routed to B. Also, I'm not sure how a packet coming to B from wg0 should be routed to eth0 towards C: does B needs some other entries in the routing table or will sysctl net.ipv4.ip_forward=1

Score:1
cl flag
A.B

WireGuard is a layer 3 interface (the lowest layer is IPv4 (or IPv6) on this interface). That means there is no concept of gateway in the Linux routing stack for this interface. Any use of a gateway parameter (which works but is ignored) on such interface will only lead to confusion on its meaning. WireGuard itself uses peers and AllowedIPs to play a related (but not identical) role, of which the routing stack knows nothing about.

So once the interfaces are created, at least this is needed for the routing stack in addition to the "LAN" routes already automatically added with the addresses on the interfaces (rewritten without gateway on wg0):

R# sysctl net.ipv4.conf.wg0.forwarding=1 # will be a one-armed router
B# sysctl net.ipv4.ip_forward=1 # or else as above with eth0 + wg0
A# ip route add 192.168.1.30/32 dev wg0
C# ip route add 172.0.0.10/32 via 192.168.1.20 dev eth0

Optionally C could benefit (eg: for traceroute to display all nodes when rp_filter=1) to have instead the complete route for 172.0.0.0/24:

C# ip route add 172.0.0.0/24 via 192.168.1.20 dev eth0

Now about the AllowedIPs parameters. The explanation is there: WireGuard Cryptokey Routing.

In the server configuration, each peer (a client) will be able to send packets to the network interface with a source IP matching his corresponding list of allowed IPs.

In the server configuration, when the network interface wants to send a packet to a peer (a client), it looks at that packet's destination IP and compares it to each peer's list of allowed IPs to see which peer to send it to.

The same AllowedIPs value acts both as incoming filter (first quote) and outgoing peer selector (second quote). This last role is what I described as related but not identical to a gateway above.

There can be no overlap, since an IP address maps to one peer (or none which gets network error messages such as ENOKEY (Required key not available)), never more. Trying to use the same IP address on multiple peers makes no sense. This will simply silently remove entries from the previous peer and get the overall configuration wrong.

So the configuration for communication between A, R, B (which is not even really needed but can be useful for traceroute) and between A and C via R is:

  • on A:

    [Peer]
    PublicKey = rrrrrrrrrrrrrrrrrrrrrrrrrr
    [...]
    AllowedIPs = 172.0.0.0/24,192.168.1.30/32
    

    A expects any source 172.0.0.0/24 from R (only R's and B's addresses currently exist so they could have been written instead of the whole /24) plus also 192.168.1.30 from R.

  • on B:

    [Peer]
    PublicKey = rrrrrrrrrrrrrrrrrrrrrrrrrr
    [...]
    AllowedIPs = 172.0.0.0/24
    

    B expects any 172.0.0.0/24 from R (only R's and A's addresses currently exist so they could have been written instead of the whole /24). B doesn't use 192.168.1.30 anywhere: it's a source coming from eth0 (so not validated by WireGuard) and also not a destination through wg0: it should not appear in the configuration (though if it did, this wouldn't do anything harmful except confusing the reader).

  • on R:

    [Peer]
    PublicKey = aaaaaaaaaaaaaaaaaaaaaaaaaa
    AllowedIPs = 172.0.0.10/32
    
    [Peer]
    PublicKey = bbbbbbbbbbbbbbbbbbbbbbbbbb
    AllowedIPs = 172.0.0.20/32,192.168.1.30/32
    

    Here the shortcut 172.0.0.0/24 can't be used anymore: each address has to appear on the correct peer. R expects source 192.168.1.30 from B peer, never from A peer: it must be on peer B and must not be on peer A.

ARDVL avatar
in flag
I learned new things. Thank you for such a detailed answer!
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.