3

I set up port forwarding for port 80 with iptables:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 10080  

Additionally, I have ip forwarding enabled to route connections from a different subnet.

I am running my own standard socket server on port 10080 on the same machine and when I receive an incoming connection to port 80 it is forwarded to my server as expected.

My problem is that when I print out the socket information using getsockname and getpeername, the source address is always the server's. If I connect to the server directly through a port that is not affected by the iptables rule I see the client address as expected. Why is port forwarding affecting the source address?

Andrew Schulman
  • 8,811
  • 21
  • 32
  • 47
user1247196
  • 41
  • 1
  • 3

2 Answers2

3

I don't think the redirect target is the right one to use in this case, the dynamic NAT target would be more appropriate.

An excerpt from https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Security_Guide/s1-firewall-ipt-fwd.html:

If you have a server on your internal network that you want make available externally, you can use the -j DNAT target of the PREROUTING chain in NAT to specify a destination IP address and port where incoming packets requesting a connection to your internal service can be forwarded. For example, if you wanted to forward incoming HTTP requests to your dedicated Apache HTTP Server server system at 172.31.0.23, run the following command:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT \
  --to 172.31.0.23:80
Joe
  • 166
  • 4
  • I've tried DNAT but I get the same exact thing. I only see the server address and not the client address in my server – user1247196 Dec 27 '14 at 02:18
  • In my test config, I added this rule, the logs for the test webserver included the public IP of the client. `iptables -A PREROUTING -d /32 -p tcp -m tcp --dport 80 -j DNAT --to-destination ` – Joe Dec 27 '14 at 02:42
  • I tried this: iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.31.52.55:10080 in my case, my server is running on the same machine as iptables. I am forwarding anything incoming on port 80 to port 10080. y server is listening on port 10080. The forwarding works, however in the server I see this when I print out the socket information: local:172.31.52.55:10080 peer:172.31.52.55:44007 172.31.52.55 is the server's address and the peer appears to as the same – user1247196 Dec 27 '14 at 03:02
  • Ah, I misunderstood. If the firewall is the same machine as the server, the redirect target would be appropriate, but in the output chain. `iptables -t nat -A OUTPUT -p tcp -d 172.31.52.55 --dport 80 -j REDIRECT --to-port 10080` – Joe Dec 27 '14 at 03:09
3

My problem is that when I print out the socket information using getsockname and getpeername, the source address is always the server's. If I connect to the server directly through a port that is not affected by the iptables rule I see the client address as expected. Why is port forwarding affecting the source address?

I'll explain, but be warned that this is going to be a long explanation.

Let's first understand what is a TCP Connection: It is a stateful connection-oriented data interchange between an 'initiator tuple' (IP,Port) and a 'terminator tuple' (IP,port).

In other words, the TCP Connection is defined by a four-ple of (Client_IP,Client_Port,Server_IP,Server_Port).

This means, all information transfer for that TCP Connection needs to fulfill the four-ple definition.

Now, let's assume that your server is located at 1.1.1.1, and the client at 2.2.2.2. The client opens the connection to your server's port 80. Assuming the client's ephemeral port is 34567, we have the four-ple (2.2.2.2,34567,1.1.1.1,80).

On your server, the packet gets redirected to 1.1.1.1:10080. But this results in a different four-ple of connection! E.g., (2.2.2.2,34567,1.1.1.1,10080).

What will the actual listener do? Why it would respond directly to 2.2.2.2:34567, resulting in the packet to be dropped by the client, because the client has the four-ple of (2.2.2.2,34567,1.1.1.1,80) and not (2.2.2.2,34567,1.1.1.1,10080).

So, in this case, the REDIRECT target have to create a temporary four-ple mapping:

(2.2.2.2,34567,1.1.1.1,80) --> (1.1.1.1,x,1.1.1.1,10080)

(x is a temporary, ephemeral port to allow TCP Connection to be built).

With this temporary mapping, the server at 10080 responds back to netfilter, and a reverse mapping happens:

(1.1.1.1,x,1.1.1.1,10080) --> (2.2.2.2,34567,1.1.1.1,80)

netfilter sends the packet which has been reverse-mapped to the Client, and all is well.

So, if you need to know the client's IP address, you can NOT use the REDIRECT target. Rather, put your actual server in a different subnet, and use DNAT target, with your Linux server acting as a full router. Assuming your actual server has a local LAN address of 192.168.1.51, the mapping will be:

(2.2.2.2,34567,1.1.1.1,80) --> (2.2.2.2,34567,192.168.1.51,80)

The server at 192.168.1.51 will reply to 2.2.2.2, and the router will reverse map:

(2.2.2.2,34567,192.168.1.51,80) --> (2.2.2.2,34567,1.1.1.1,80)

And all's well.

pepoluan
  • 5,038
  • 4
  • 47
  • 72