4

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
Coxer
  • 197
  • 2
  • 14

2 Answers2

3

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.

A.B
  • 13,968
1

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.