====== Ubuntu - iptables - Forward Ports ====== ===== Sample Network Infrastructure ===== Web server network details: * Public IP Address: 5.42.134.2 * Public Interface: eth0 * Private IP Address: 192.168.1.2 * Private Interface: eth1 Firewall network details: * Public IP Address: 5.42.134.35 * Public Interface: eth0 * Private IP Address: 192.168.1.35 * Private Interface: eth1 ---- ===== Sample WebServer ===== The web-server is NginX listening on the private networking address. server { listen 192.168.1.2:80 default_server; . . . } ---- ===== Verify Connectivity to the WebServer ===== From the firewall server, issue: curl --connect-timeout 5 192.168.1.2 returns: The webpage successfully. No problems there. Welcome to nginx!

Welcome to nginx!

. . .
Now try to use the public interface to connect to the web-server. curl --connect-timeout 5 5.42.134.2 returns: curl: (7) Failed to connect to 5.42.134.2 port 80: Connection refused ---- ===== Configuring the Firewall to Forward Port 80 ===== ==== Enable Forwarding in the Kernel ==== Enable traffic forwarding at the kernel level. By default, most systems have forwarding turned off. To turn port forwarding on for this session only, type: echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward To turn port forwarding on permanently, you will have to edit the **/etc/sysctl.conf** file. Open the file with sudo privileges by typing: sudo vi /etc/sysctl.conf Inside, find and uncomment the line that looks like this: net.ipv4.ip_forward=1 Save and close the file when you are finished. You apply the settings in this file by typing: sudo sysctl -p sudo sysctl --system ---- ===== Adding the Forwarding Rules to the Firewall ===== We want to configure our firewall so that traffic flowing into our public interface (eth0) on port 80 will be forwarded to our private interface (eth1). Our basic firewall has a our **FORWARD** chain set to **DROP** traffic by default. We need to add rules that will allow us to forward connections to our web server. For security's sake, we will lock this down fairly tightly so that only the connections we wish to forward are allowed. In the **FORWARD** chain, we will accept new connections destined for port 80 that are coming from our public interface and travelling to our private interface. New connections are identified by the **conntrack** extension and will specifically be represented by a TCP SYN packet: sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT This will let the first packet, meant to establish a connection, through the firewall. We also need to allow any subsequent traffic in both directions that results from that connection. To allow **ESTABLISHED** and **RELATED** traffic between our public and private interfaces, type: iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT We can double check that our policy on the **FORWARD** chain is set to **DROP** by typing: sudo iptables -P FORWARD DROP At this point, we have allowed certain traffic between our public and private interfaces to proceed through our firewall. However, we haven't yet configured the rules that will actually tell iptables how to translate and direct the traffic. ---- ===== Adding the NAT Rules to Direct Packets Correctly ===== Next, we'll add the rules that will tell iptables how to route our traffic. We need to perform two separate operations in order for iptables to correctly alter the packets so that clients can communicate with the web server. The first operation, called **DNAT**, will take place in the **PREROUTING** chain of the **nat** table. **DNAT** is an operation which alters a packet's destination address in order to enable it to be correctly routed as it passes between networks. The clients on the public network will be connecting to our firewall server and will have no knowledge of our private network topology. We need to alter the destination address of each packet so that when it is sent out on our private network, it knows how to correctly reach our web server. Since we are only configuring port forwarding and not performing NAT on every packet that hits our firewall, we'll want to match port 80 on our rule. We will match packets aimed at port 80 to our web server's private IP address (192.168.1.2 in our example): sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.2 This takes care of half of the picture. The packet should get routed correctly to our 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, we also need to modify the packet's source address as it leaves the firewall en route to the web server. We need to modify the source address to our firewall server's private IP address (192.168.1.35 in our 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, we'll add a rule to the **POSTROUTING** chain of the **nat** table, which is evaluated right before packets are sent out on the network. We'll match the packets destined for our web server by IP address and port: sudo iptables -t nat -A POSTROUTING -o eth1 -p tcp --dport 80 -d 192.168.1.2 -j SNAT --to-source 192.168.1.35 Once this rule is in place, our web server should be accessible by pointing our web browser at our firewall machine's public address: curl 5.42.134.35 Returns the website successfully. Welcome to nginx!

Welcome to nginx!

. . .
Our port forwarding setup is complete. ---- ===== Adjusting the Permanent Rule Set ===== Now that we have set up port forwarding, we can save this to our permanent rule set. If you do not care about losing the comments that are in your current rule set, just use the **iptables-persistent** service to save your rules: sudo service iptables-persistent save If you would like to keep the comments in your file, open it up and edit manually: sudo vi /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. For our example, it would look something like this: *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: 5.42.134.2 # * Private IP Address: 192.168.1.2 # * Public Interface: eth0 # * Private Interface: eth1 # # Firewall network details: # # * Public IP Address: 5.42.134.35 # * Private IP Address: 192.168.1.35 # * 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: 5.42.134.2 # * Private IP Address: 192.168.1.2 # * Public Interface: eth0 # * Private Interface: eth1 # # Firewall network details: # # * Public IP Address: 5.42.134.35 # * Private IP Address: 192.158.1.35 # * Public Interface: eth0 # * Private Interface: eth1 -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.2 -A POSTROUTING -d 192.168.1.2 -o eth1 -p tcp --dport 80 -j SNAT --to-source 192.168.1.35 # 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 have added the above and adjusted the values to reflect your own network environment. Test the syntax of your rules file by typeing: sudo iptables-restore -t < /etc/iptables/rules.v4 If no errors are detected, load the rule set: sudo service iptables-persistent reload Test that your web server is still accessible through your firewall's public IP address: curl 5.42.134.35 This should work just as it did before. ---- ===== Conclusion ===== By now, you should be comfortable with forwarding ports on a Linux server with iptables. The process involves permitting forwarding at the kernel level, setting up access to allow forwarding of the specific port's traffic between two interfaces on the firewall system, and configuring the NAT rules so that the packets can be routed correctly. This may seem like an unwieldy process, but it also demonstrates the flexibility of the netfilter packet filtering framework and the iptables firewall. This can be used to disguise your private networks topology while permitting service traffic to flow freely through your gateway firewall machine. ---- ===== References ===== https://www.digitalocean.com/community/tutorials/how-to-forward-ports-through-a-linux-gateway-with-iptables