Aug 19

How to use a Reverse SSH tunnel to reach a server behind a NAT

If you’re reading this, it means my server is online and running. You pointed your browser at my website and connected to my servers IP on port 80. That is totally possible because this is a server with a publicly available IP address. But what if you want to connect to a server that does not have a public IP address? Read on to see how a Reverse SSH Tunnel saves the day!

I had that issue today, and I thought it would be fun to show you a neat but not at all unique solution.

Problem: I need to backup my server via SSH/rsync to my home Linux server which is behind a NAT, and has a private IP address. Port forwarding is out, because my home public IP can change at any time. What if I could connect my home server directly to the web server I want to backup, and have it connect back to the home server?

The Reverse SSH Tunnel

That’s what an Reverse SSH Tunnel does. Look at the example below. It is to be run from the server with the private IP address (behind the NAT).

ssh -fN -R 5000:localhost:22 root@my.public.server.com -p 22

There are two options passed there. From the SSH 'man' page:
 -N Do not execute a remote command. This is useful for just forwarding ports (protocol version 2 only).
 -f Requests ssh to go to background just before command execution.

This lets you run the command and be returned to a command prompt. The “-R 5000:localhost:22” means “set up a Reverse Proxy from port 5000 on the remote machine to forward to port 22 locally”, and then the rest of the command contains the SSH username, hostname, and port of the public server we want to tunnel to.

What’s the effect? Once the command has been run, you can go to the remote public server, and ssh back to the home server.

[root@remote.server ~]ssh user.on.home.server@localhost -p 5000

You’ll be prompted for a password unless you set up passwordless authentication with an SSH key pair. I chose not to, as the Virtualmin backup configuration prompts for a password. At least a 16 character random password is used.

Making it Persistent

I’m using this on a regular basis, and if my home server is rebooted for any reason I would like the Reverse SSH tunnel to automagically start itself. First, I created a file in /root/ called reverse_tunnel.sh and put my Reverse SSH command in it. Then, I made it executable, and added it to /etc/rc.d/rc.local, which also needs to be made executable:

chmod 700 /root/reverse_tunnel.sh
chmod 700 /etc/rc.d/rc.local
echo /root/reverse_tunnel.sh >> /etc/rc.d/rc.local

The file /etc/rc.d/rc.local is unused in SystemD, but this activates it and it runs on bootup, executing any command put in it.

A Problem

Everything I did worked, but the SSH connection randomly dropped, even after disabling SSH’s timeout. The solution? Autossh! Yes, there’s a program just for this. Install AutoSSH with

yum -y install autossh

and modify reverse_tunnel.sh with the following command:

autossh -M 5122 -N -R 5000:localhost:22 root@my.public.server.com -p 22 -f

Now the connection stays put. AutoSSH uses ports 5122 and 5123 (in this configuration) to monitor the SSH session and restart it if needed. Problem solved!

Did you find this info useful? There’s another great source for tips like this that I think you’ll enjoy: