So, we have a Linux router with an iptables firewall, where one interface faces the internet and the other faces the internal network. For example, we have few public IPs (in the worst case, only one), and we need to forward traffic on a specific port of the public IP to a server located inside the network.
That is, like this:
packet -> public_ip:80 --> private_ip:80
Or, to put it in concrete terms:
- public address: 11.22.33.44
- address of the server inside the network: 10.0.0.5
- internal network: 10.0.0.0/24
- router's IP on the internal-network interface: 10.0.0.1
- everything hitting 11.22.33.44 on port 80 should be forwarded to 10.0.0.5 (also port 80)
This is a trivial task and is solved with this one line:
iptables -t nat -A PREROUTING -p tcp -d 11.22.33.44 --dport 80 -j DNAT --to-destination 10.0.0.5:80
But here's a trickier task. We can see the web server from the outside. But from inside the network it's still not visible. What do we do?
There are 2 simple options:
1) Inside the network, if there's an internal DNS server - for example, from Active Directory - redefine the necessary DNS zones and say that www.myhost.ru should go not to 11.22.33.44, but to 10.0.0.5. The downsides are obvious: you need an internal DNS server, and you need to edit and add zones on it in time. And it's a bit of a hassle.
2) Set up iptables so that it does DNAT not only from outside, but also from inside the network. That way, when we hit 11.22.33.44:80 from inside the network, we'll get redirected to 10.0.0.5:80, just as if we were doing it from outside.
If you look at the PREROUTING rule described above, you'll see that it doesn't specify where exactly the packet needs to come from to get thrown to 10.0.0.5. So why doesn't it work?
The answer is this: when the client computer sends a packet to 11.22.33.44, the router does in fact successfully D-NAT the packet to 10.0.0.5. That is, the packet does reach the server. But when the server sends the reply, it sees that the traffic came from inside the network, so it sends the reply:
a) From its own internal-network address (10.0.0.5);
b) Directly, without going through the router, since the client is inside its own subnet;
But the client doesn't expect such a packet: it isn't ready for the reply to come from 10.0.0.5 instead of 11.22.33.44. Moreover, even if you pull the servers out into a separate subnet, you most likely still won't succeed, because the client will still be waiting for a reply from 11.22.33.44, not from the internal IP, even though the traffic does pass through the router - S-NAT simply won't happen.
Solution - masquerade the traffic between the internal network and the servers facing outward.
To do this, leaving the rule above unchanged, let's add one more, in the POSTROUTING section:
iptables -t nat -A POSTROUTING -d 10.0.0.5 -s 10.0.0.0/24 -j SNAT --to-source 10.0.0.1
This way we perform S-NAT for all the traffic that, in fact, is headed to our server 10.0.0.5. And the reply will already be post-masquerading - from 11.22.33.44, exactly as the client computer expects.
Comments