Notes on reverse SSH tunnel: Connect to servers behind NATs and firewalls through publicly available middle server

2018/1/7 2:36 AM posted in  Hacker's comments

In some cases, the servers you use in office/school may be behind some NAT or firewall so that you can not access these servers from the external internet. This can be inconvenient sometimes. A simple walkaround of this problem is reverse SSH tunneling. For all Unix like systems(e.g. Linux, MacOS), this just works fine.

How to establish reverse SSH tunnel

The whole connection route will look like this:

Destination Server <- |NAT| <- Public Server <- Your PC

In order to establish a reverse SSH tunnel, first you need to forward an unused port(e.g. 19999) from the destination server to the public server:

ssh -R 19999:localhost:22 user@pubic_server

Then from your PC, SSH to the public server first:

ssh user@public_server

Then from the public server, do the following to SSH to the destination server:

ssh localhost -p 19999

Now you have successfully gain access to the destination server from your PC(from external Internet)!

Make the reverse SSH tunnel stable

This tunnel can be unstable for long terms(some times the port forwarding can fail). In order to make it more reliable, we make use of linux system's cron utility.

First we make a configuration for the public server by adding the following to the "~/.ssh/config" file:

Host public_server
   ServerAliveInterval 60
   ExitOnForwardFailure yes
   TCPKeepAlive no

Then restart sshd service to make the configuration effective:

sudo service sshd restart

Now make a script file like this(make sure you can SSH to public_server without password using ssh keys):

# reverse_ssh_tunnel.sh
#!/bin/sh
COMMAND="ssh -N -f -R 19999:localhost:22 user@public_server"
pgrep -f -x "$COMMAND" > /dev/null 2>&1 || $COMMAND

Then add a new crontab by the following command:

crontab -e

Add the following:

*/1 * * * * /bin/sh [path to your script file]

This will check if the forwarding is alive or not every 1 minute. If it is not,cron will rerun the SSH command.

Using SFTP and etc. through reverse SSH tunnel

You might want to be able to use SFTP or access the jupyter notebook on the destination server as well. This can be achieved by adding a regular tunnel from any unused port on your PC to port 19999 on public_server:

ssh -L 19998:localhost:19999 user@public_server

Now you can access the destination server by using the 19998 port on your PC(localhost). Works like a magic!