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!