Score:0

iptables: How to forward UDP and TCP port to server behind wireguard VPN connection

ca flag
Tom

So I have VPS (KVM) server running Debian bullseye with a public IP. That server is also running a wireguard server. At home, I have another server (behind NAT) with a wireguard client that is connected to the my VPS. The tunnel is up and running, meaning I can communicate properly over the wireguard VPN.

+----------------------------------+             +-------------------------------
| Homeserver (behind NAT)          |             | VPS (KVM) with public IP     |
| 192.168.1.10                     |             | 1.2.3.4                      |
|         +------------------------|             |--------------------+         |
|         | wg-homeserver          | - - - - - - | wg-vps             |         |
|         | 192.168.200.2          |             | 192.168.200.1      |         |
|         +------------------------|             |--------------------+         |
|                                  |             |                              |
+----------------------------------+             +------------------------------+

My homeserver runs a number of docker containers that listen on ports (on all IP addresses), meaning these ports are accessible from 192.168.1.0/24. I now need to forward one TCP and one UDP port on my VPS's public IP address and forward the traffic over the wireguard link to the same ports on my home server:

1.2.3.4:10000/tcp -> 192.168.200.2:10000/tcp
1.2.3.4:10001/udp -> 192.168.200.2:10001/udp

IP forwarding is enabled on both machines, meaning cat /proc/sys/net/ipv4/ip_forward shows 1.

To forward the TCP port I have these rules:

iptables -A FORWARD -d 192.168.200.2/32 -i wg-vps -p tcp --dport 10000 -j ACCEPT
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 10000 -j DNAT --to-destination 192.168.200.2:10000

When I try to ssh -p 10000 1.2.3.4 I don't see any packets being logged by wireguard -i wg-vps. I tried numerous other things but haven't been able to figure out what I am doing wrong or missing.

Anyway, as for the UDP port I created this rule, with slightly more success:

iptables -t nat -A PREROUTING -d 1.2.3.4/32 -i eth0 -p udp --dport 10001 -j DNAT --to-destination 192.168.200.2:10001

This is how I send my udp test packet:

echo -n test | nc -4 -u 1.2.3.4 10001

When I do this, I can see tcpdump -i wg-vps on my VPS log this (4.5.6.7 being my public ip address):

11:35:47.497622 IP 4.5.6.7.43357 > 192.168.200.2.10001: UDP, length 4

Which seems like it's trying to do the right thing and take the packet, update the destination to 192.168.200.2, and then put it onto the wireguard interface wg-vps. But tcpdump -i wg-homeserver on my homeserver never shows this packet arriving.

In fact, I noticed that when I watch ip -s link show wg-homeserver on my Homeserver that every time I sent a udp packet to that port, the RX errors increment by one. So something makes it all the way there, but whatever it is, it's not showing up with tcpdump and appears to be dropped somewhere below. Which is weird because both UDP and TCP over the wireguard link works just fine in both diretions.

As far as I understand tcpdump shows packets prior to iptables, so my guess is that even though the packet seems to be written to the wireguard interface, it somewhere gets dropped.

UPDATE: Ok so I found a way to enable wireguard logging:

echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

And when tailing the logs with dmesg --follow it looks like wireguard is dropping the packet with this reason:

wireguard: wg-homeserver: Packet has unallowed src IP (4.5.6.7) from peer 20 (xxxxxx)

So I think this means that it doesn't like the fact that the source address is the original from wherever the request came from. I don't want to do SNAT so I think I'll just need to teach it somehow that it should allow any source address.

Which turns out to be the AllowedIPs setting in the [Peer] section of my wireguard configuration. This solves the issue:

AllowedIPs = 0.0.0.0/0, ::0/0

Before this I had put the interface masks similar to what is in the [Interface] section, not realizing that this would limit the source addresses of packets.

However, this hijacks all traffic and attempts to route it over the wireguard link as wg-quick looks at AllowedIPs and thinks it should add a route and redirect all traffic over the VPN. This is obviously not desired, but luckily there's a solution: Add Table = off to the [Interface] section will prevent this.

Score:0
br flag

See your rule table to understand how the outgoing packets are routed at the home server. wg-quick will add some rules.

ip rule show

For me, I had to remove some rules generated by wg-quick (that makes it forward all the packets to wg) and then add rules that forward the required ports. You can see the wg table# from above command.

ip -4 rule add sport 80 table XXXXX
ip -4 rule add sport 443 table XXXXX

The above worked for a while. But systemd-networkd will remove custom rules when dhcp lease expires or lan cable is disconnected and reconnected. So I completely removed wg-quick and use systemd-networkd to configure the wg interface and the Routing Rules.

Tom avatar
ca flag
Tom
As for the routing of outgoing packets, I instead set `Table = off` in the `[Interface]` section, which does stop it from routing everything over the wireguard connection, except of course the subnet(s) specified in the `[Interface]`'s `Address` setting.
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.