Accessing the DNAT'ted webserver from inside the LAN

Solution 1:

I deleted my original answer, because I wasn't fully confident that it was correct. I have since had some time to set up a little virtual network of VMs to simulate the network in question. Here is the set of firewall rules that worked for me (in iptables-save format, for the nat table only):

-A PREROUTING -d 89.179.245.232/32 -p tcp -m multiport --dports 22,25,80,443 -j DNAT --to-destination 192.168.2.10
-A POSTROUTING -s 192.168.2.0/24 -o ppp0 -j MASQUERADE
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.2.10/32 -p tcp -m multiport --dports 22,25,80,443 -j MASQUERADE

The first POSTROUTING rule is a straightforward way of sharing the internet connection with the LAN. I left it there for completeness.

The PREROUTING rule and the second POSTROUTING rule together establish the appropriate NATs, so that connections to the server via the external IP address can happen, regardless of whether the connections originate from outside or from inside the LAN. When clients on the LAN connect to the server via the external IP address, the server sees the connections as coming from the router's internal IP address (192.168.2.1).

Interestingly, it turns out that there are a couple of variations of the second POSTROUTING rule that also work. If the target is changed to -j SNAT --to-source 192.168.2.1, the effect is (not surprisingly) the same as the MASQUERADE: the server sees connections from local LAN clients as originating from the router's internal IP address. On the other hand, if the target is changed to -j SNAT --to-source 89.179.245.232, then the NATs still work, but this time the server sees connections from local LAN clients as originating from the router's external IP address (89.179.245.232).

Finally, note that your original PREROUTING/DNAT rule with -i ppp0 does not work, because the rule never matches packets coming from the LAN clients (since those don't enter the router via the ppp0 interface). It would be possible to make it work by adding a second PREROUTING rule just for the internal LAN clients, but it would be inelegant (IMO) and would still need to refer explicitly to the external IP address.

Now, even after having laid out a "hairpin NAT" (or "NAT loopback", or "NAT reflection", or whatever one prefers to call it) solution in full detail, I still believe that a split-horizon DNS solution---with external clients resolving to the external IP and internal clients resolving to the internal IP---would be the more advisable route to take. Why? Because more people understand how DNS works than understand how NAT works, and a big part of building good systems is choosing to use parts that are maintainable. A DNS setup is more likely to be understood, and thus correctly maintained, than an arcane NAT setup (IMO, of course).

Solution 2:

I am surprised that after almost 8 years, nobody has explained how to do this the correct way using the UCI configuration system used by default in OpenWRT.

Steven Monday's answer is correct, yet it is using iptables commands directly, which is a lower layer than the UCI configuration system, and is best left untouched by most OpenWRT users if possible.

The correct way to access internal servers through their public IP/port combos from another internal host in UCI is by enabling the configuration option reflection under each specific DNAT target in the file /etc/config/firewall. This behavior is documented here.

For example:

config redirect option target 'DNAT' option src 'wan' option dest 'lan' option proto 'tcp' option src_dport '44322' option dest_ip '192.168.5.22' option dest_port '443' option name 'apache HTTPS server' option reflection '1'

Note: According to the indicated OpenWRT documentation, reflection is enabled by default. In my testing, this was not the case.


Solution 3:

A common solution is to point your internal hosts at a local DNS server that returns the correct "internal" address for these hostnames.

Another solution -- and we're using this where I work on our Cisco firewalls -- is to rewrite DNS responses on the firewall that correspond to these addresses. I don't think there are tools for Linux that do this right now.

You should be able to configure the routing on your gateway to do the right thing. You may need to configure the servers to be aware of their externally mappped ip address (e.g., by assigning it to a dummy interface). With this configuration, communication from one internal system to another internal system -- using it's "external" address -- would go through the router.


Solution 4:

What you are asking to do is called NAT Loopback and it requires that you add a SNAT rule so that packets originating from your LAN to your Server will go back through the router:

-A POSTROUTING -p tcp -s 192.168.2.0/24 -d 192.168.2.10 -m multiport --dports 22,25,80,443 -j SNAT --to-source 89.179.245.232