Useful iptables Port Forwarding Patterns

I make use of netfilter/iptables quite frequently — most system admins probably do. Over time, I have come to use a few patterns that go beyond the simple “allow this” or “block everything but”. These generally involve NAT and Port Forwarding, and use not the filter table, but the nat table. I find myself going back to my notes and code snippets somewhat frequently to jog my memory on the format of these patterns, and felt it might be helpful to share them.

For the purposes of my examples, let’s say we have the following setup:

Host: Asimov
Centos6
eth0 – 10.0.0.10
eth1 – 192.168.0.1
Apache httpd

Host: Heinlein
Centos6
eth0 – 192.168.0.5
Apache Tomcat

Simple Port Forwarding

This can be helpful if you want a particular service available on different ports internally and externally for the same host. For example, you may want Apache httpd available on your internal network at port 80, but externally on some obscure port for a host that is directly connected to both networks. Instead of having Apache listen on the obscure port and creating a customized virtual host, I often just use iptables to redirect traffic from the obscure port to the standard port.

iptables -t nat -A PREROUTING -p tcp --dport <incoming-port> -j REDIRECT --to-port <destination-port>

Example:
root@asimov:~# iptables -t nat -A PREROUTING -p tcp --dport 1234 -j REDIRECT --to-port 80

Complex Port Forwarding

In more complex situations, you might want to have an incoming port on a host directly connected to both an internal and external network forwarded to a separate internal network host. The cases for this are rare… but I’ve found it convenient from time to time for temporary setups such as for testing or demonstrations.  For example, you may want to forward some obscure port on an external facing host to Apache Tomcat on an internal host.

iptables -t nat -A PREROUTING -p tcp --dport <incoming-port> -j DNAT --to-destination <destination-ip>:<destination-port>

Example:
root@asimov:~# iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.0.5:8080

While you can technically achieve the same redirection behavior with the DNAT extension as the REDIRECT extension, it is generally preferable to stick to using the simple REDIRECT unless you need to involve a new destination IP address.

In order to NAT to other IP addresses (forwarding), you need to be sure that the kernel is set to allow forwarding:

echo 1 > /proc/sys/net/ipv4/ip_forward

Note: This should enable forwarding immediately, but may vary by distro. This also does not persist the setting across reboots. Check your distro’s method of doing this. e.g. Centos6: sysctl -w net.ipv4.ip_forward=1

Additionally, you need to setup the SNAT or MASQUERADE extension so that packets which are forwarded to the new destination IP can be properly routed back to the origin of the request:

iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to <source-address>
or
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

Example:
root@asimov:~# iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 192.168.0.1
or
root@asimov:~# iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

Generally, the MASQUERADE extension should be reserved for hosts that have a dynamic address (such as from DHCP) for which hard-coding a SNAT rule may be problematic. Otherwise, both forms achieve the same effect.  See the iptables man pages for full details.

Note: Don’t forget to persist your iptables rules! It varies by distro, but generally you need to use iptables-save and write the output to the iptables rules file. Check your distro’s method of doing this. e.g. Centos6: /etc/init.d/iptables save

General iptables references: