By default, a linux machine will drop traffic that’s not destined for an IP bound locally. If you’re planning on receiving traffic not ultimately destined for you (ie: you’re behaving as a router along the path to the ultimate destination), then you’ll need to flip a couple flags in /etc/sysctl.conf to allow traffic forwarding:
net.ipv4.ip_forward = 1 net.ipv4.conf.default.rp_filter = 1
Reload sysctl:
sysctl -p
Add your NAT rule to the iptables prerouting chain in the nat table. For example, maybe you’re plugged into both a public and private network, and you want to forward traffic to your public IP on tcp/80 to the web server on your private network. Note that this particular step is only necessary if the public network needs to be able to reach the webserver on our private network. You can skip the PREROUTING chain altogether if you only want to allow egress traffic from the servers on your private network, and block all ingress.
root@dev[~]# iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.12:80 root@dev[~]#
When the webserver on 10.0.0.12 tries to respond to the public address, that’s going to be problematic. Since the client originally connected to us and not the webserver, we can’t have the webserver being visible in the response – this would cause a TCP RESET. The return traffic will definitely hit us though, ’cause we’re that webserver’s default gateway. As such, we can correct this by masquerading as the source of this routed traffic.
root@dev[~]# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE root@dev[~]#
Make sure we allow the relevant traffic as well. Since we’re routing port 80 to the webserver, we’ll want port 80 open on the INPUT chain. Since the webserver sends all its traffic through us, we need to allow all of it out via our FORWARD chain – unless we’re doing egress filtering here, in which case you’d glorify your FORWARD chain.
root@dev[~]# iptables -A FORWARD -j ACCEPT root@dev[~]# iptables -A INPUT -p tcp --dport 80 -j ACCEPT root@dev[~]#
Oh and one more thing. If you want to NAT traffic that’s originating from this router server in the first place, then you need to add your rules to the OUTPUT chain in the nat table. For example, say we’ve setup a SOCKS proxy on port 8080 and we want to force all our outgoing HTTP traffic to use that SOCKS proxy. Here’s how you’d do that:
root@dev[~]# iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8080 root@dev[~]#
This can be really useful in cases where an application is trying to connect to a remote resource, but that resource has changed IPs and it’s going to take you 3 weeks to correct every single instance of the old IP within your code. Instead of being offline for those three weeks, throw up a NAT rule so attempts to reach the old IP are NAT’d to the new IP instead:
root@dev[~]# iptables -t nat -A OUTPUT -d 10.0.0.50 -j DNAT --to-destination 192.168.0.50 root@dev[~]#
You won’t see your iptables NAT rules in the default table. You need to specify “-t nat” to see these rules:
root@dev[~]# iptables -nL Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination root@dev[~]# root@dev[~]# iptables -nL -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:10.0.0.12:80 Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination root@dev[~]#
Oh, and just in case we’re not that backend server’s default gateway, make it so:
route add default gw 10.0.0.1
