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.