Score:1

Wireguard use one client as gateway of another

cn flag

I have a Wireguard VPN setup that basically looks like this:

P1 ---- S ---- P2 --- Internet

IP addreses: P1 = 10.200.1.5 S = 10.200.1.1 P2 = 10.200.1.3

I am redirecting all traffic of P1 to S by specifying allowedIps = 0.0.0.0/0 in P1's client config.

Now I want that S routes that traffic to P2. I tried the following on S:

 ip rule add from 10.200.1.5 lookup 200
 ip route add default via 10.200.1.3 dev wg0 table 200
 sysctl -w net.ipv4.ip_forward=1

However, when I run tcpdump on P2 I cannot see any traffic coming in. Also P1 is not experiencing any internet connectivity.

Edit: Testing the custom routing table on S via

ip route get 8.8.8.8 from 10.200.1.5 iif wg0

gives the following response

8.8.8.8 from 10.200.1.5 via 10.200.1.3 dev wg0 table 200
    cache iif wg0

which seems fine, however

tcpdump -nn -i wg0

on S shows unreachable as below

09:58:22.207251 IP 10.200.1.5.9134 > 8.8.8.8.53: 36555+ A? play.googleapis.com. (37)
09:58:22.207270 IP 10.200.1.1 > 10.200.1.5: ICMP host 8.8.8.8 unreachable, length 73
djdomi avatar
za flag
why dont you let p1 connect directly?
cn flag
p1 is in an internal network and cannot access the internet directly
djdomi avatar
za flag
your network is unclear please let us more about
cn flag
What you have so far looks good -- make sure you also set `AllowedIPs = 0.0.0.0/0` in S's `[Peer]` configuration for P2; and that you include `10.200.1.5` in the `AllowedIPs` of P2's `[Peer]` configuration for S.
cn flag
When I put 0.0.0.0/0 into P2's config on S, I cannot SSH into S anymore since (I believe) it hoovers up all the traffic and sends it to P2, no?
gliepins avatar
gs flag
Did you ever get this sorted? I am looking to establish exactly this! Also if there is another way of achievin
Score:1
sy flag

If you want

P1 <--> S <--> P2 <--> Internet

where S is a "Wireguard server", P1 and P2 are Wireguard peers (assumed to be behind NAT with no port forwarding configured) "connecting" to S and P2 acts as internet gateway for P1, you need first of all the following basic settings:

P1 Wireguard config

[Interface]
PrivateKey = ...
Address = 10.200.1.5/32

[Peer] # S
PublicKey = ...
Endpoint = ...:51820
AllowedIPs = 0.0.0.0/0

S Wireguard config

[Interface]
ListenPort = 51820
PrivateKey = ...
Address = 10.200.1.1/32

[Peer] # P1
PublicKey = ...
AllowedIPs = 10.200.1.5

[Peer] # P2
PublicKey = ...
AllowedIPs = 0.0.0.0/0

P2 Wireguard config

[Interface]
PrivateKey = ...
Address = 10.200.1.3/32

[Peer] # S
PublicKey = ...
Endpoint = ...:51820
PersistentKeepalive = 25    # allows S to reach P2
AllowedIPs = 10.200.1.0/24

Also, make sure that IP forwarding is enabled on S as well as P2 and that P2 performs the necessary NAT/masquerading when the forwarded packets from S1 leave P2 on its internet-facing network interface.

This should already work in that P1's internet traffic is forwarded all the way to P2. However, now the internet access of S is also via P2. This might be undesirable. For example, you will have problems SSH-ing into S over the internet because S would try to respond via P2 (asymmetric routing with NAT along the way). If you don't want S itself to use P2 as gateway you can configure policy-based routing on S manually like this:

S config with custom policy-based routing

[Interface]
ListenPort = 51820
PrivateKey = ...
Address = 10.200.1.1/32
Table = 123  # <-- AllowedIPs-based routes end up here
PostUp = ip rule add from 10.200.1.0/24 table 123
PreDown = ip rule del from 10.200.1.0/24 table 123

[Peer] # P1
PublicKey = ...
AllowedIPs = 10.200.1.5

[Peer] # P2
PublicKey = ...
AllowedIPs = 0.0.0.0/0

This makes wg-quick add the AllowedIPs-based routes to a custom routing table (table 123) and conditionally "enables" this routing table based on the source IP addresses using ip rule.

You also might want to add some firewall rules on S, to, for example, make sure traffic coming in over Wireguard is not escaping anywhere and is just forwarded to the same device. You could do so by adding

PostUp = iptables -I FORWARD -i %i ! -o %i -j REJECT
PreDown = iptables -D FORWARD -i %i ! -o %i -j REJECT

to S' config.

sellibitze avatar
sy flag
@A.B: Are you referring to the suggested `AllowedIPs` settings for the server S? If so, there is no issue. Crypto-key routing will select the most specific route pretty much like routing is normally done.
A.B avatar
cl flag
A.B
it won't. You should verify what you state before thinking it's what will happen. Of course it would be easier, but that's not the case: that's not the *routing stack*.
sellibitze avatar
sy flag
@A.B I did verify this and it works. I can have two peers, one with `AllowedIps=10.200.0.5` and the second peer with `AllowedIPs=0.0.0.0/0` where traffic adderssed to 10.200.0.5 is sent to the correct peer and everything else is sent to the other one. I am happy to provide the scripts that demonstrate this. Please point out paragraph and sentence where this kind of overlap with differing CIDRs is ruled out to work reliably because I didn't see it.
A.B avatar
cl flag
A.B
Ok, I admit I'm wrong. Either I've always been wrong, or this changed since (I couldn't get this result a few years ago). But testing it's as you wrote. Sorry (and +1)
sellibitze avatar
sy flag
@A.B I appreciate your response and +1. Thank you very much. You had me worried a bit that I just got lucky. But I used such overlapping allowed-ips a couple times already and it always worked as expected. I tried to verify that the wireguard trie data structure really supports this (see allowedips.c) but it's not so easy to make sense of it or isolate the code for testing.
A.B avatar
cl flag
A.B
I've been looking for a commit that would explain why I remember that when adding an overlapping allowed address it would be removed from the previous peer but couldn't find it.
sellibitze avatar
sy flag
To add another data point: I've been using similar `AllowedIPs` settings for at least two years without problems.
A.B avatar
cl flag
A.B
I've been using WG since 2017. If there was a change (can't tell) it was probably in the branch now named wireguard-linux-compat
A.B avatar
cl flag
A.B
Never mind. I'll have to revisit a few of my answers about this, thanks for pointing this.
Score:1
cl flag
A.B

WireGuard is a layer 3 interface, as such stating via 10.200.1.3 has no effect, since it would be used for the link layer protocol (typically ARP) to resolve the layer 2 address which doesn't exist here.

So

ip route add default via 10.200.1.3 dev wg0 table 200

can be rewritten:

ip route add default dev wg0 table 200

This helps keep in mind that this part isn't the part selecting a packet to go to P1 or to P2: WireGuard too has its own internal routing: cryptokey-routing, which is done by setting correctly AllowedIPs in each peer's configuration. There's one important limitation: contrary to standard routing, AllowedIPs doesn't support any overlapping address. If this is attempted (like setting on server S AllowedIPs = 0.0.0.0/0 for Peer P2) this will automatically erase the conflicting address(es) on (the) other peer(s) (like erasing AllowedIPs = 10.200.1.5 from Peer P1, because 0.0.0.0/0 overlaps anything else) and connectivity will suffer (S doesn't crypto-route anything to P1: no connectivity anymore).

There are two ways to solve this:

  • use two different WireGuard interfaces

    The previous limitation is per WireGuard interface. Using a second interface avoids such clashes, but will make routing more complex. Probably multiple entries are now needed in the routing table 200 and/or main table: one for the left side interface and one (default) for the right side interface.

  • do a set substraction of the conflicting ranges

    There might be tools actually able to compute the difference between the set 0.0.0.0/0 and the set 10.200.1.5 but I don't know them. There's still a handy tool called netmask (homepage: https://github.com/tlby/netmask) that will help by converting ranges to the smallest set of netmasks:

    $ netmask 0.0.0.0:9.255.255.255 10.200.1.3 11.0.0.0:255.255.255.255
            0.0.0.0/5
            8.0.0.0/7
         10.200.1.3/32
           11.0.0.0/8
           12.0.0.0/6
           16.0.0.0/4
           32.0.0.0/3
           64.0.0.0/2
          128.0.0.0/1
    

    These are the values (to separate with commas) that should be used on server S for Peer P2's AllowedIPs so the crypto-key routing will route anything there, except 10.0.0.0/8 of which only 10.200.1.3 will be defined on P2's side, leaving intact the already defined 10.200.1.5 on P1's side: no overlap anymore. Packets sent by P1 to Internet should now proceed to P2 for further routing.

sellibitze avatar
sy flag
Splitting `0.0.0.0/0` into smaller subranges just so that there is not *any* kind of overlap in the `AllowedIPs` is not necessary (for Linux + `wg-quick`). And unless you use the `Table =` option to change the default behaviour, this would make `wg-quick` add all these routes into the main table which would create a routing loop.
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.