Port forwarding through Linux Gateway using IPtables

ip -4 addr show scope global

The highlighted output shows two interfaces (eth0 and eth1) and the addresses assigned to each (203.0.113.1 and 10.0.0.1 respectively). To find out which of these interfaces is your public interface, run this command:

ip route show | grep default

eg: eth0 as public interface & eth1 as private interface

In the FORWARD chain, you’ll accept new connections destined for port 8902 that are coming from your public interface and traveling to your private interface. New connections are identified by the conntrack extension and will specifically be represented by a TCP SYN packet as in the following:

sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp –syn –dport 8902 -m conntrack –ctstate NEW -j ACCEPT

This will let the first packet, meant to establish a connection, through the firewall. You’ll also need to allow any subsequent traffic in both directions that results from that connection. To allow ESTABLISHED and RELATED traffic between your public and private interfaces, run the following commands. First for your public interface:

sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT

Then for your private interface:

sudo iptables -A FORWARD -i eth1 -o eth0 -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT

Double check that your policy on the FORWARD chain is set to DROP:

sudo iptables -P FORWARD DROP

Adding the NAT Rules to Direct Packets Correctly: Since you’re only configuring port forwarding and not performing NAT on every packet that hits your firewall, you’ll want to match port 8902 on your rule. You will match packets aimed at port 8902 to your web server’s private IP address (192.168.100.55 in the following example):

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp –dport 8902 -j DNAT –to-destination 192.168.100.55

This process takes care of half of the picture. The packet should get routed correctly to your web server. However, right now, the packet will still have the client’s original address as the source address. The server will attempt to send the reply directly to that address, which will make it impossible to establish a legitimate TCP connection.

To configure proper routing, you also need to modify the packet’s source address as it leaves the firewall en route to the web server. You need to modify the source address to your firewall server’s private IP address (10.0.0.2 in the following example). The reply will then be sent back to the firewall, which can then forward it back to the client as expected.

To enable this functionality, add a rule to the POSTROUTING chain of the nat table, which is evaluated right before packets are sent out on the network. You’ll match the packets destined for your web server by IP address and port:

sudo iptables -t nat -A POSTROUTING -o eth1 -p tcp –dport 8902 -d 192.168.100.55 -j SNAT –to-source 192.168.100.55


If you would like to keep the comments in your file, open it up and edit manually:

sudo nano /etc/iptables/rules.v4

You will need to adjust the configuration in the filter table for the FORWARD chain rules that were added. You will also need to adjust the section which configures the nat table so that you can add your PREROUTING and POSTROUTING rules. The contents will resemble the following:

/etc/iptables/rules.v4
*filter

Allow all outgoing, but drop incoming and forwarding packets by default

:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

Custom per-protocol chains

:UDP – [0:0]
:TCP – [0:0]
:ICMP – [0:0]

Acceptable UDP traffic

Acceptable TCP traffic

-A TCP -p tcp –dport 22 -j ACCEPT

Acceptable ICMP traffic

Boilerplate acceptance policy

-A INPUT -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

Drop invalid packets

-A INPUT -m conntrack –ctstate INVALID -j DROP

Pass traffic to protocol-specific chains

Only allow new connections (established and related should already be handled)

For TCP, additionally only allow new SYN packets since that is the only valid

method for establishing a new TCP connection

-A INPUT -p udp -m conntrack –ctstate NEW -j UDP
-A INPUT -p tcp –syn -m conntrack –ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack –ctstate NEW -j ICMP

Reject anything that’s fallen through to this point

Try to be protocol-specific w/ rejection message

-A INPUT -p udp -j REJECT –reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT –reject-with tcp-reset
-A INPUT -j REJECT –reject-with icmp-proto-unreachable

Rules to forward port 80 to our web server

Web server network details:

* Public IP Address: 203.0.113.1

* Private IP Address: 10.0.0.1

* Public Interface: eth0

* Private Interface: eth1

Firewall network details:

* Public IP Address: 203.0.113.2

* Private IP Address: 10.0.0.2

* Public Interface: eth0

* Private Interface: eth1

-A FORWARD -i eth0 -o eth1 -p tcp –syn –dport 80 -m conntrack –ctstate NEW -j ACCEPT
-A FORWARD -i eth0 -o eth1 -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eth1 -o eth0 -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT

End of Forward filtering rules

Commit the changes

COMMIT

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

Rules to translate requests for port 80 of the public interface

so that we can forward correctly to the web server using the

private interface.

Web server network details:

* Public IP Address: 203.0.113.1

* Private IP Address: 10.0.0.1

* Public Interface: eth0

* Private Interface: eth1

Firewall network details:

* Public IP Address: 203.0.113.2

* Private IP Address: 10.0.0.2

* Public Interface: eth0

* Private Interface: eth1

-A PREROUTING -i eth0 -p tcp –dport 80 -j DNAT –to-destination 10.0.0.1
-A POSTROUTING -d 10.0.0.1 -o eth1 -p tcp –dport 80 -j SNAT –to-source 10.0.0.2

End of NAT translations for web server traffic

COMMIT

*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

Save and close the file once you’ve added the content and adjusted the values to reflect your own network environment.

Next, test the syntax of your rules file:

sudo sh -c “iptables-restore -t < /etc/iptables/rules.v4”

If no errors are detected, load the rule set:

sudo service netfilter-persistent reload