3

I have a WireGuard server as the edge router. Forwarding all http traffic to my web server. Everything works fine but there is a problem. The web server cannot access itself through the WireGuard public IP address. On the web server computer, I cannot use the web browser to access my website.

I found that daddr based nat can resolve this issue but I would like to know if there any better method because IP address may vary but iif is fixed. My Netgear WiFi router can port forwarding without this kind of problem. But I cannot check its internal rule, also I don't think it uses daddr based nat.

Here are the config of the WireGuard server.

wg0

interface: wg0 Address = 10.0.0.1/24 public key: (hidden) private key: (hidden) listening port: 51820 peer: (hidden) endpoint: (hidden):51820 allowed ips: 10.0.0.2/32 latest handshake: 56 seconds ago transfer: 20.69 MiB received, 115.85 MiB sent 

nftables

table ip firewall { chain input { type filter hook input priority filter; policy drop; ct state established,related accept udp dport {51820} accept tcp dport {22} accept ip saddr 10.0.0.0/24 accept } chain prerouting { type nat hook prerouting priority dstnat; iif eth0 tcp dport {80,443} dnat to 10.0.0.2 } chain postrouting { type nat hook postrouting priority srcnat; ip saddr 10.0.0.0/24 masquerade } chain forward { type filter hook forward priority filter; policy drop; ct state established,related accept ct status dnat accept ip saddr 10.0.0.0/24 accept } } 

Here are the config of the Web server.

wg0

interface: wg0 Address = 10.0.0.2/24 public key: (hidden) private key: (hidden) listening port: 51820 fwmark: 0xca6c peer: (hidden) endpoint: (hidden):51820 allowed ips: 0.0.0.0/0 latest handshake: 25 seconds ago transfer: 114.69 MiB received, 3.56 MiB sent persistent keepalive: every 25 seconds 

1 Answer 1

2

This is a case of NAT loopback handling.

Currently the WireGuard server ("WGS") redirects only from eth0, so nothing will happen with traffic received from wg0.

On should redirect on WGS incoming traffic to ports 80,443 intended for its own public IP address. But there's a catch: how to guess at a pre-routing step that the packet will be classified as local without knowing in the rule this local destination IP address , while this classification happens at the routing decision step which didn't yet happen? (See this schematic for a summary of these various steps in the life of a packet).

One can ask the kernel, dynamically in the packet path, how it would route a packet by using nftables' fib expression (requires kernel >= 4.10):

FIB EXPRESSIONS

fib {saddr | daddr | mark | iif | oif} [. ...] {oif | oifname | type} 

A fib expression queries the fib (forwarding information base) to obtain information such as the output interface index a particular address would use. The input is a tuple of elements that is used as input to the fib lookup functions.

This can be performed before the routing step and used to do the redirection only if the packet is tentatively classified as a local destination: to an address belonging to WGS. As this is done in prerouting, once the actual routing decision will happen, it won't be classified as a local packet anymore but as a routed packet (back to sender).

To redirect traffic received from wg0 (thus from the Web Server ("WS")) sent to any address belonging to WGS:

nft add rule ip firewall prerouting iif wg0 tcp dport '{ 80, 443 }' fib daddr type local dnat to 10.0.0.2 

If needed (eg: to still be able to access a private management interface listening on WGS' wg0 address only) it can be further filtered so only a destination local to WGS but not being wg0's 10.0.0.1 will match by using this instead:

nft add rule ip firewall prerouting iif wg0 daddr != 10.0.0.1 tcp dport '{ 80, 443 }' fib daddr type local dnat to 10.0.0.2 

As the nat/postrouting chain already masquerades anything in the 10.0.0.0/24 source range, there's no additional step needed: WS's source address will be replaced by WGS's address on wg0. NAT loopback requires the source to be changed (WS can't accept a packet coming from its own IP address).

Optionally a dedicated rule could be inserted before this masquerade rule to choose an other IP address to snat to instead. Any address would do (instead of the resulting source 10.0.0.1), be it local to WGS or not as long as on WS this address is routed through WGS. As WS's and WGS' routing table weren't provided, the example below might be wrong. Pick the non-existing 10.0.1.2 address to replace WS's own 10.0.0.2, still allowing WS's own logs to easily identify requests coming from itself:

nft insert ip firewall postrouting ip saddr 10.0.0.2 ip daddr 10.0.0.2 snat to 10.0.1.2 

or to be overly conservative:

nft insert ip firewall postrouting ip saddr 10.0.0.2 ip daddr 10.0.0.2 tcp dport '{ 80, 443 }' ct status dnat oif wg0 snat to 10.0.1.2 
3
  • Many thanks for your answer. It is very detailed. Regarding to the SNAT, there is one more thing that I don't understand. If I use the Netgear router port forwarding, my web server logged the client is coming from the public IP address of my router. If I use my nftables port forwarding, the client is coming from the private IP address 10.0.0.1. Although I think coming from 10.0.0.1 make more sense, I would like to know how could the Netgear router do that? Commented Mar 5, 2022 at 4:42
  • Can't tell, but if the Netgear updates its forwarding rules whenever its public IP changes (eg: DHCP event) it can easily use its public IP address in SNAT rules (assuming it runs Linux/iptables etc). Or it could also use a proxy port forwarding and then update routes with the public source hinted for its LAN interface. I understood this question was asking to not know the public address. Btw, you can get 10.0.1.2 instead of 10.0.0.1 as source with the additional rule to distinguish WS from WGS as client. Commented Mar 5, 2022 at 8:31
  • Thanks for the very detailed explanation. I was having trouble with a server running a few containers that needed to talk with each other via DNS / public IP at serverfault.com/questions/1144741/… and your answer allowed me to fix the issue :) Commented Oct 1, 2023 at 9:55

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.