DEV Community

Ivo
Ivo

Posted on • Originally published at ivoberger.com on

How to Set up Port Forwarding with iptables

If you have a server on a private network and need to access it from the outside (but can't simply give it an external IP) you can use port forwarding on an externally accessible server to get around it. Once set up it simply sends all incoming packets that meet certain criteria to a new IP. That way you can connect to a public server in order to communicate with the private server.

Setup

All following steps need the to be done on the externally accessible machine and need to be run as with root privilege's. I'll assume a scenario where you want to access an internal server with an internal IP 10.0.0.1 on port 80 through a server with an external IP 32.0.0.1 on port 8080 using TCP.

First we need to allow forwarding on the kernel level as this is usually disabled by default. Open /etc/sysctl.conf with your favorite editor (and root priviliges) and uncomment the line net.ipv4.ip_forward=1. Now run

sudo sysctl -p
sudo sysctl --system
Enter fullscreen mode Exit fullscreen mode

to apply the setting.

The forwarding rule itself can be added as follows:

iptables -t nat -A PREROUTING -p tcp -d 32.0.0.1 --dport 8080 -j DNAT --to-destination 10.0.0.1:80
Enter fullscreen mode Exit fullscreen mode

Let's break that down. -t nat tells iptables that we want to work on the Network Address Translation (NAT) table. We add our rule to the PREROUTING chain as we want to re-route packets and select them based on protocol (-p tcp), destination (-d 32.0.0.1) and port (--dport 8080). We specify that we want to apply Destination NAT (DNAT) to the selected packets (-j DNAT) and of course the target IP and port with --to-destination 10.0.0.1:80.

If you only care about the port because the public server has multiple IPs and you want the rule to work for all of them simply omit -d 32.0.0.1.

Now that all incoming traffic will be re-routed we just need to tell iptables to change the source address in the re-routed packages. If we don't the target server will think they came from our local machine directly and try to respond directly which doesn't sit well with TCP. To do that we need to run the following:

iptables -t nat -A POSTROUTING ! -s 127.0.0.1 -j MASQUERADE
Enter fullscreen mode Exit fullscreen mode

Now iptables will rewrite the origin of the re-rerouted packages so the target server will answer to the correct machine. I added ! -s 127.0.0.1 to exclude packets originating in [localhost](http://localhost) as without it the rule broke DNS resolution to point that sudo didn't work properly anymore because it couldn't resolve its own hostname.

Making it permanent

Iptables doesn't persist rules through restarts on its own. There are packages to take care of that like iptables-persistent but that doesn't seem to be available on Ubuntu 18.04 so here's how to do it manually.

The ruleset can be easily saved by running iptables-save > /etc/iptables.rules and restored with iptables-restore < /etc/iptables.rules. Where you save your rules it up to you.

Previous Ubuntu versions used ifupdown for networking which provides simple pre-up and pre-down hooks which are a good place to run the save and restore commands:

# open the interface definitions
sudo nano /etc/network/interfaces
# find your interface and add the following
pre-up iptables-restore < /etc/iptables.rules
pre-down iptables-save > /etc/iptables.rules
Enter fullscreen mode Exit fullscreen mode

If your /etc/network/interfaces contains

# ifupdown has been replaced by netplan(5) on this system.  See
# /etc/netplan for current configuration.
# To re-enable ifupdown on this system, you can run:
#    sudo apt install ifupdown
Enter fullscreen mode Exit fullscreen mode

you'll need to use networkd-dispatcher as netplan doesn't support hooks:

echo 'iptables-restore < /etc/iptables.rules' | sudo tee /etc/networkd-dispatcher/routable.d/50-iptables-restore
echo 'iptables-save > /etc/iptables.rules' | sudo tee /etc/networkd-dispatcher/off.d/50-iptables-save
Enter fullscreen mode Exit fullscreen mode

The save on pre-down and in off.d hooks is optional, it's actually safer to leave it out in case you mess something up. That way you can just restart and have the old (working) ruleset running again.

And that's it, know you have a port-forwarding setup that is persistent through reboots and doesn't break DNS resolution!

Top comments (1)

Collapse
 
adikwok profile image
adikwok

i tried that setup in huawei router hg8245h still failed to connect.

my public ipchicken:
202...*

wan ip from isp
10.8..

lan ip dchp
192.168.*

i had forwarded port 80. for ddns using no-ip
using internal connection is working ok.

but when i tried from my smartphone isp to connect to my laptop
connection timed out.

please help what to do, and thank you.