0

I have a server lanserver running in my private LAN that is connected to a public server publicserver using Wireguard. publicserver forwards TCP connections to certain ports to lanserver through the Wireguard connection using an iptables DNAT rule.

On lanserver, Wireguard is set up as a NetworkManager connection. It is routing all internet traffic through Wireguard using AllowedIPs = 0.0.0.0/0, ::/0. This causes the following ip rules to be set up on lanserver:

[root@lanserver ~]# ip rule show
0:      from all lookup local
31100:  from all lookup main suppress_prefixlength 0
31101:  not from all fwmark 0xcb2e lookup 52014
32766:  from all lookup main
32767:  from all lookup default
[root@lanserver ~]# ip route show table 52014
default dev wg0 proto static scope link metric 50

This setup works properly. An incoming TCP connection to publicserver gets forwarded to lanserver using the iptables rule. The response is sent back through publicserver because the ip rule 31101 matches it.

There is one exception: When I try to open a TCP connection to publicserver using IPv6 from my personal computer pc, which is also inside my private LAN, it does not work. The problem seems to be that both pc and lanserver have an IPv6 address in the same public subnet. publicserver forwards the connection to lanserver successfully, but the response is not routed back through the Wireguard connection but directly to pc due to ip rule 31100.

How can I make sure that all responses for connections coming in through the Wireguard interface on lanserver are also sent back through the Wireguard interface, regardless of whether their source IP is in a local subnet?

I can think of solutions in the following directions:

  • Disable IPv6 on lanserver, causing it to not be on the same subnet as pc. Not a very good solution.
  • Use SNAT on publicserver for the port forward. Not an acceptable solution, since some of the services behind the forwarded ports need to know the true source IP.
  • Explicitly mention the local IPv6 subnet in the AllowedIPs of the Wireguard peer. This does not work because the IPv6 subnet changes every 24 hours.
  • Add a custom IP rule that somehow matches all connections coming in through wg0 and uses the routing table 52014 for them. I'm not sure how exactly to specify such a rule. Also, the problem is that the number of the routing table changes every time the Wireguard connection is restarted. The right place to create such a rule would probably be the PostUp script, but NetworkManager does not seem to allow specifying one.
cdauth
  • 861
  • 9
  • 18

1 Answers1

0

After experimenting with different options, I decided to stop using NetworkManager and instead use wg-quick directly. On Fedora Server, this was pretty easy and I only had to remove the NetworkManager connection using nmcli con del wg0 and enable the wg-quick service using systemctl enable --now wg-quick@wg0 (which reads the config from /etc/wireguard/wg0.conf). Switching to wg-quick has the following advantages:

  • I can specify custom PostUp commands to configure a more complex routing setup
  • The number of the Wireguard routing table seems to be constant at 51820, even though I could not find any documentation about this. This makes it easier to set up custom ip rules.

I then used the PostUp command in wg0.conf to set up additional ip rules that would make sure that any Wireguard traffic would also respond through Wireguard. There are two options to achieve this:

Option 1: source IP matching

The ip rules match the Wireguard packages based on their source IP addresses (which is automatically set to the IP address on which the request originally came in):

[Interface]
Address = 10.139.192.4/24
Address = fd52:30a4:f9e7:647a::4/64
PostUp  = ip -4 rule add from 10.139.192.4 lookup 51820
PreDown = ip -4 rule del from 10.139.192.4 lookup 51820
PostUp  = ip -6 rule add from fd52:30a4:f9e7:647a::4 lookup 51820
PreDown = ip -6 rule del from fd52:30a4:f9e7:647a::4 lookup 51820

Option 2: fwmark

Some iptables rules are configured to set a mark on packages coming in through wg0:

iptables  -t mangle -A INPUT -j CONNMARK -i wg0 --set-mark 1
ip6tables -t mangle -A INPUT -j CONNMARK -i wg0 --set-mark 1
iptables  -t mangle -A OUTPUT -j CONNMARK -m connmark --mark 1 --restore-mark
ip6tables -t mangle -A OUTPUT -j CONNMARK -m connmark --mark 1 --restore-mark

Then this mark is matched by the IP rule:

PostUp  = ip -4 rule add fwmark 1 lookup 51820
PreDown = ip -4 rule del fwmark 1 lookup 51820
PostUp  = ip -6 rule add fwmark 1 lookup 51820
PreDown = ip -6 rule del fwmark 1 lookup 51820

More details about this solution can be found here.

cdauth
  • 861
  • 9
  • 18