Port forwarding

From Finninday
Jump to: navigation, search

background

This should be so easy, but I've made several attempts and each have failed, so I need to start keeping track of what I have tried so I don't repeat myself.

The goal is to configure my iptables firewall to pass traffic from the internet through my server to a NATted box on my internal network where I'm running a service of some kind.

References:

  1. http://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#IPHEADERS
  2. http://www.ridinglinux.org/2008/05/21/simple-port-forwarding-with-iptables-in-linux/
  3. http://www.centos.org/docs/4/html/rhel-sg-en-4/s1-firewall-ipt-fwd.html
  4. http://lartc.org/howto/lartc.cookbook.fullnat.intro.html

This diagram from lartc should help:

        +------------+                +---------+               +-------------+
Packet -| PREROUTING |--- routing-----| FORWARD |-------+-------| POSTROUTING |- Packets
input   +------------+    decision    +---------+       |       +-------------+    out
                             |                          |
                        +-------+                    +--------+   
                        | INPUT |---- Local process -| OUTPUT |
                        +-------+                    +--------+

Create the FORWARD rule

[0:0] -A FORWARD -p tcp -d 10.0.0.45 --dport 7777 -j ACCEPT
[0:0] -A FORWARD -p udp -d 10.0.0.45 --dport 7777 -j ACCEPT

Create the NAT rule

[0:0] -A PREROUTING -p tcp --dport 7777 -j DNAT --to-destination 10.0.0.45:7777

Create the MASQUERADE rule.

[0:0] -A POSTROUTING -o eth0 -j MASQUERADE

Testing with netcat and tcpdump show the packets arriving at the firewall, but no response. Looking at the verbose iptables I can see that the rule has matched three packets:

root@weasel:/etc/default# iptables -v -L -t nat
Chain PREROUTING (policy ACCEPT 165 packets, 18154 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    3   180 DNAT       tcp  --  any    any     anywhere             anywhere             tcp dpt:7777 to:10.0.0.45:7777

However, the FORWARD rule hasn't matched anything yet:

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination        
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            10.0.0.45            tcp dpt:7777
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            10.0.0.45            udp dpt:7777

I'm not entirely sure that the forward rule needs to match if the redirect has already taken place... But tcpdump tells me that the packets don't appear on any network interfaces after they arrive on eth1.

That makes me think that I have a DROP rule somewhere that is eating my packets. However, I've checked the packet totals and don't see any increase in any of my DROP rules.


verify the first leg of the trip (no port forwarding)

  • create rule to allow input on port 7777
  • disable any NAT rules about port 7777
  • verify that netcat can talk between remote machine and my firewall

Remote machine opens connection to port 7777

[root@bash01 ~]$ nc 216.99.216.99 7777
test
awesome
^C
[root@bash01 ~]$ 

Netcat listens on the firewall

root@weasel:~# nc -l 7777
test
awesome
root@weasel:~# 

Sucess. I don't have to worry about packets being blocked before they reach the firewall.

re-enable port forwarding nat rules and test

  • two rules need to be created together for this to work, the PREROUTING and FORWARD.
[0:0] -A PREROUTING -p tcp --dport 7777 -j DNAT --to-destination 10.0.0.6:7777
[0:0] -A PREROUTING -p udp --dport 7777 -j DNAT --to-destination 10.0.0.6:7777

[0:0] -A FORWARD -p tcp -d 10.0.0.6 --dport 7777 -j ACCEPT
[0:0] -A FORWARD -p udp -d 10.0.0.6 --dport 7777 -j ACCEPT

Failure: netcat listening on 10.0.0.6:7777 never gets any messages. tcpdump on firewall reports that syn packets arrive from the remote machine to port 7777 No syn-acks go back.

root@weasel:/etc/default# tcpdump -i any port 7777
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
10:24:14.973584 IP 70.103.8.101.50313 > 216-99-216-99.static.dsl.spiritone.com.7777: Flags [S], seq 2406398579, win 14600, options [mss 1380,sackOK,TS val 2157661614 ecr 0,nop,wscale 7], length 0
10:24:15.973575 IP 70.103.8.101.50313 > 216-99-216-99.static.dsl.spiritone.com.7777: Flags [S], seq 2406398579, win 14600, options [mss 1380,sackOK,TS val 2157662614 ecr 0,nop,wscale 7], length 0
10:24:17.973557 IP 70.103.8.101.50313 > 216-99-216-99.static.dsl.spiritone.com.7777: Flags [S], seq 2406398579, win 14600, options [mss 1380,sackOK,TS val 2157664614 ecr 0,nop,wscale 7], length 0
10:24:21.973500 IP 70.103.8.101.50313 > 216-99-216-99.static.dsl.spiritone.com.7777: Flags [S], seq 2406398579, win 14600, options [mss 1380,sackOK,TS val 2157668614 ecr 0,nop,wscale 7], length 0

The SYN requests should show up as about 4 packets coming in on eth0 and going out on eth1. The SYNACKs should show up as coming in on eth1 and going out on eth0.

Count the packets matched by filter rules

My first try of counting packets going through rules by diffing iptables output wasn't helpful enough. It didn't capture enough context to tell me what rules were actually triggered. So I added more context and the grepped out the context that I don't care about, like this:

rm /tmp/iptables-diff*
iptables -t filter -L -v -n > /tmp/iptables-diff1
sleep 5
iptables -t filter -L -v -n > /tmp/iptables-diff2
echo " pkts bytes target     prot opt in     out     source               destination "
diff -U100 /tmp/iptables-diff1 /tmp/iptables-diff2 | egrep "Chain|^[-+]"


Count the packets matched by nat rules

Start again

Disable all iptable rules for port 7777

  • testing from the server itself is not valid as it evidently bypasses iptables rules
root@weasel:/etc/default# nc -l 7777
root@weasel:~# nc 216.99.216.99 7777

That works even when port 7777 traffic is not allowed.

  • testing from a remote host with 7777 disable
root@weasel:/etc/default# nc -l 7777
$ nc 216.99.216.99 7777

Silently fails. That is more like what I expect.

Allow port 7777 on public interface

Still silently fails. That's wrong.

Bah, I'm putting this off again as it is simpler in this case to install haproxy and have it deliver the packets between the firewall and the server. Setting up haproxy only took about five minutes and just worked.