Forward a TCP port to another IP or port using NAT with Iptables

Besides using NAT for accessing the internet with multiple machines using a single IP address, there are many other uses of NAT. One of them is to forward all traffic that is sent to a certain TCP port to another host. In practice, this technique can be used to test a service on a new host without adjusting anything on the client. The users or the clients do not need to be pointed to a new machine in order to test it. When the test would be unsuccessful, removing the NAT-rule is all it takes to switch back.

Theoretical explanation

To above scenario is better known as port forwarding and it allows you to forward an incoming packet to another destination. That destination can be another port or IP-address. With the scheme under here, I will try to make things a little more visual and explain how port forwarding works:

nat_beforeafter

The left part of the scheme shows the original situation: the client connects to service A, running on server A (ip: 10.1.1.1). Incoming packets come through service A on server A and the outgoing packets go back to the original source IP, client A. The goal of the NAT-setup is to forward the traffic to service A running on server B (ip:10.2.2.2) without any modifications to client A. Our new setup needs to be transparent for the client.

The right part of the scheme shows our goal: the client still connects to TCP port 9999 to the IP of server A (10.1.1.1) so nothing changed on that side. As soon as a TCP packet with destination port 9999 arrives at server A, it should be forwarded to server B (ip: 10.2.2.2). Before redirecting the TCP-packet to the other machine, the packet needs to be modified so that it get’s sent back to server A before sending back to the original host.

To completely understand this process, we need to take a deeper look into how Iptables works. When a packet passes through Iptables, it passes a set of chains. Decisions made by those chains are called rules and that’s basically how you configure Iptables.

Overview of the chains used by Iptablesiptables_chains

For our setup to work, we need to add a DNAT and SNAT rule (Prerouting and Postrouting). The first one will make sure that the packet gets routed to the other host (ip: 10.2.2.2) and the second one will make sure that the source address of the packet is no longer the original one but the one of the machine who performed the NAT. That way, the packet will be sent back to it’s original source via the host which owns ip 10.1.1.1.

Firewalld

Since CentOS or RHEL 7, iptables has been “replaced” with firewalld. And while you can realize the scenario with firewalld, I will use the classic iptables. To use iptables instead of firewalld on CentOS 7 or RHEL 7, you can find more information in this post.

Preparations

The next steps prepare the system and iptables for NAT. Most commands can be used on CentOS, RHEL and Debian in exactly the same way. If there are some differences, I mention them (so if nothing special is mentioned, the commands are interchangeable).

IP forwarding

NAT uses IP forwarding and by default it’s not enabled in the kernel parameters. First we need to check if IP forwarding is enabled and if it’s not, we need to enable it.

To check if IP forwarding is enabled:

CentOS/RHEL:

Debian:

The above outputs shows that IP forwarding is not enabled.

To enable IP forwarding persistently (survives a reboot):

CentOS/RHEL:

Debian:

To activate the changes immediately:

CentOS/RHEL:

Debian:

Iptables

The next thing to do is to check if Iptables is running on the system. Iptables is running as a kernel module so it can’t be seen as one of the normal processes.

CentOS/RHEL:

Debain:

If there is no output, it means that Iptables isn’t loaded.

To start Iptables:

Now that we are sure that Iptables is active on the system, we can check which rules are active.

For the INPUT, FORWARD and OUTPUT-chains:

For the NAT-related chains:

As you can see, at this moment, no rules are configured and all traffic is allowed to and from the system without doing anything NAT-related.

In case you already have some rules configured, it’s a good idea (in a testing environment) to flush the current rules:

Enable port forwarding

After going trough the above steps, we’re ready to active the port forwarding. As an example, I will forward the TCP port 9999 of host 192.168.202.103 to TCP port 80 on host 192.168.202.105.

First I will check that nothing is actually listening on port 9999 of host 192.168.202.103 by doing a telnet to port 9999 on that machine:

jensd@deb:~$ telnet 192.168.202.103 9999
Trying 192.168.202.103…
telnet: Unable to connect to remote host: Connection refused

To be sure that something is listening at port 80 of host 192.168.202.105, where I want to get forwarded, I can do the same check:

As you can see, there is a webserver running on port 80 on host 192.168.202.105.

Now, to forward port 9999 on host 192.168.202.103 to port 80 on host 192.168.202.105, we need to add the following rules to the iptables configuration of host 192.168.202.103:

To test if my NAT-rule is working, I will repeat the test with telnet:

To permanently save the rules, execute iptables-save

CentOS/RHEL:

Debian:

5 thoughts on “Forward a TCP port to another IP or port using NAT with Iptables

  1. Great article, I followed this article and was able to get all my mysql traffic forwarded to a second server from the 10.1.x.x subnet. However my forwarding server has 2 nics. How to achieve this on a centOS 5.11 server with 2 NICs one with 192.168.1.10 as IP and the other with 10.1.1.10 as IP. and the mysql server at 10.2.1.10? I need to do this because people from out side use putty to establish a connection via 192.168.1.10

    Thanks,

    • I would need to test this to be 100% sure but I assume that you can add a -i eth0 to the rules and adjust the source IP to the correct one matching the interface.

  2. Pingback: VPS: How to forward traffic to devices from public IP | DL-UAT

  3. I need same config, but in your configuration, client IP seen as SERVER 1 IP. That’s is a bit problem. What if i need to on SERVER2 client ip ?

    Because by the following rule,

    nat -A POSTROUTING -p tcp -d 192.168.202.105 –dport 80 -j SNAT –to-source 192.168.202.103

    You overwrite source IP of client. And server won’t be able to detect client IP.

    How we can receive client IP as source on SERVER 2?

    • I didn’t really test this but I think that MASQUERADE is what you’re looking for. SO instead of putting the POSTROUTING rule in your Iptables, you could add something like this:
      iptables -t nat -A POSTROUTING -j MASQUERADE

Leave a Reply

Your email address will not be published. Required fields are marked *