Bypassing the NAT – Reverse SSH tunnel with port forwarding!

This was a really confusing and difficult article to write! But yes, I finally figured it out and wanted to share it with you guys. Hope the article helps someone!


I had recently moved to China, where I discovered that the carriers used CGNAT to combat the growing problem of shrinking IPv4 assignments. No surprises there, since China is literally booming with people. However, this posed a big problem for my home server and network, since in Korea I had my very own public IP address handed out to me.

Yup, that terrible drawing

But there is always a solution – and this time, the solution came in the form of SSH. Using SSH, I could forward literally any ports I wanted, from my local network, to a remote server that did have a public facing IP address.

In this tutorial, I’ll go over how to get your own remote server set up, and tunnel your very own home server to a remote server so that you could access it from anywhere.

1. DigitalOcean

You need a remote server to get started. I chose a VPS, because it is simple and easy to just spin up an instance.

Hop onto DigitalOcean, make an account, link a credit card, and spin up an instance. Size doesn’t really matter – I chose the smallest instance on there, which is $5 a month. Pretty cheap, right?

Note down the IP address of your instance. From now on, I will be expressing this IP as XXX.XXX.XXX.XXX. Pretty simple.

2. Server setup

Connect to your server using ssh. Do any server-maintenance stuff you need to do. I recommend this quick start article from DigitalOcean to add a sudo user to your server. After you’ve mucked around plenty on the server and secured it enough, proceed to the next step. I won’t be going into detail on server hardening, because that’s a topic for another day.

Then, you want to modify /etc/ssh/sshd_config, assuming you chose Ubuntu when you set up your VPS instance. Inside there, find the line named #GatewayPorts no and change it to GatewayPorts yes(remove the hash in the front and change no to yes.)

Save, exit, and type sudo service sshd restart. In theory you shouldn’t get kicked off from the server, but if you do, just reconnect. Finally, type restart to clear up the environment.

3. Client setup

This is probably the most confusing and difficult part. DO NOT SKIM THIS PART. READ IT FULLY.

Let’s say you have a port you want to forward on localhost, port 9000. You want to make it so that when you connect to the remote server, you could access it on port 80.

Client –> Remote server XXX.XXX.XXX.XXX (port 80) –> Home Server –> Web instance (port 9000)

Not a game of snake

To do that, on the home server type this SSH command.

ssh -nNT -R 80:localhost:9000 [email protected]

What just happened here? Let’s dissect this command. ssh is the program name itself, -nNT instructs SSH to not create a TTY instance (so you will NOT get a Terminal prompt), -R tells SSH we want to make a remote port forward, 80is the port the remote server will be listening on, localhost is the IP the home server will forward requests to (in this case, it’ll be itself), 9000 would be the port that the home server would listen on, and the rest ([email protected]) is just the typical SSH details to connect to a server.

Let’s try this one more time. I have a instance with an IP address of on my local network, such as a Raspberry Pi. It is listening on port 12984 and is hosting a web server. I want to see my website pop up when I connect to the remote server, so we would have to use port 80 or even port 443. To access this through a reverse tunnel, I would have to use this command on my home server:

ssh -nNT -R 80: [email protected] # HTTP
ssh -nNT -R 443: [email protected] # HTTPS
ssh -nNT -R 80: -R 443: [email protected] # or all in one

And this would be the diagram for how it would work:

4. AutoSSH

Sure, you could type these commands out every time you want a bridge! But personally, I’m lazy, and I want a script that does it for me every boot.

That’s where AutoSSH comes in.

sudo apt install autossh

Change to fit your distribution. In this case, I’m using Ubuntu Server. Then, we need to make a systemd configuration file.

touch and nano a file in /etc/systemd/system/, named autossh-whatever-you-want-to-put-in-here.service. I’ll go with autossh-ssh.service for today. So the full path would be /etc/systemd/system/autossh-tunnel.service.

Paste the following in, making sure to adapt it for your uses:

Description=AutoSSH tunnel service

ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o "ExitOnForwardFailure yes" -N -R 80: -R 443: [email protected] -p 22 -i /home/example/.ssh/private-key


Then tell systemd to look for new configuration, and then enable the configuration:

sudo systemctl daemon-reload
sudo systemctl start autossh-tunnel.service
sudo systemctl enable autossh-tunnel.service

If you want to configure how SSH connects to the server, you could utilize ~/.ssh/config:

Host example-tunnel
    User example
    Port 22
    IdentityFile ~/.ssh/private-key
    RemoteForward 80
    RemoteForward 443
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes

You don’t really need to specify RemoteForward here since AutoSSH will take care of that for you anyway, but I just put it there for peace of mind.

For more information, you could check out this great guide over here! I learnt a lot from that post, so thanks to the authors who wrote that!

Wrapping up

Hopefully, this was enough to get you started on making your own SSH tunnels.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.