Port Forwarding with iptables is not working

I´m using an Ubuntu Server Box (10.04) to route my network to internet. This box has 2 ethernet cards (eth0 for internet connection, eth1 for lan - 192.168.1.1) and I would like to forward port 80 to my server (192.168.1.254). So, I setup the following:

iptables -F
iptables -t nat -F
iptables -X
iptables -t nat -X
iptables -t mangle -F

# default route:
ip route add default via 200.160.111.67
# www tunnel:
iptables -I FORWARD -p tcp -d 192.168.1.254 --dport 80 -i eth0 -j ACCEPT
# this line locks up internet access for all users:
#iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.254:80

# ssh tunnel
iptables -I FORWARD -p tcp -d 192.168.1.254 --dport 22 -i eth0 -j ACCEPT
# this line uncommented locks all my accesses to external ssh servers:
#iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination 192.168.1.254:22

# NAT
modprobe iptable_nat
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

NAT for connections inside my network is running, but both port forwarding (ssh and www) are not working, and I don´t know what I´m doing wrong. Could you help me?


Your DNAT rules need to be a bit more specific in order to work correctly. One way to proceed is to add a --destination (or -d) condition to each of them, e.g.

iptables -t nat -A PREROUTING -d $EXT_IP -p tcp --dport 80 -j DNAT \
    --to-destination 192.168.1.254

iptables -t nat -A PREROUTING -d $EXT_IP -p tcp --dport 22 -j DNAT \
    --to-destination 192.168.1.254

where $EXT_IP is your router's external (globally routable) IP address.

Caveat: If internal clients attempt to connect to the www or ssh services on 192.168.1.254 via the external (globally routable) IP address, then their connection attempts will fail (while connections directly to 192.168.1.254 would have succeeded).

To solve that, it is best to tell your internal clients to access the server via its internal IP address. Alternatively, you can configure a "hairpin NAT" by adding a couple more iptables rules, as follows:

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.1.254 -p tcp \
    --dport 80 -j SNAT --to-source $INT_IP

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.1.254 -p tcp \
    --dport 22 -j SNAT --to-source $INT_IP

where $INT_IP is the router's internal IP address (e.g. 192.168.1.1). With these rules, connections from internal clients to the external IP address will flow through the router. It is best to avoid doing hairpin NAT whenever possible, since it is a rather inefficient use of network and router system resources.