Here is my procedure for establishing a reverse ssh tunnel that maintains a persistent ssh connection (using autossh) through a firewall/router with no configuration needed on the target (host) machine. I use this to maintain an Ubuntu/Edubuntu LTSP server for a small school several hours from home. As long as the school’s internet connection is working, the target server is impervious to changes in network settings, even though neither the school nor the server’s IP addresses are static.
On boot-up, the target machine establishes an ssh tunnel to the remote computer (called ‘Home’ hereafter) from which I will be working. For additional security, this tunnel allows no execution on Home from the target computer. On Home, I can then connect via ssh to localhost on the specified port using the command at the end of the procedure below. Although I use this primarily for console work, this also works well using NoMachine NX (or FreeNX), an amazing remote desktop app that runs on ssh. These instructions are Ubuntu/Debian specific, but they should work with minor modifications on any Linux distro. This is designed to be cut-and-paste in case you are uncomfortable with a lot of command-line input. Don’t forget to take advantage of the auto-complete feature of the shell (tabbing completes commands or file paths; double-tab shows options where your input is ambiguous).
As always, very little of this is original with me. Credit goes to others who have posted the technical details in many forums and blogs. I’m only compiling them in a format that worked for me.
On the target/host machine:
ssh-keygen -t dsa
Output: (accept defaults, do NOT enter a passphrase):
Generating public/private dsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_dsa.
Your public key has been saved in /home/user/.ssh/id_dsa.pub.
The key fingerprint is:
The key's randomart image is:
+--[ DSA 1024]----+
|.. . |
|... . S |
| .o. o .. . E |
| ... . .o.. |
List the contents of your .ssh directory to be sure everything is as expected.
ls -l ./.ssh/
Output should be similar to this. Note the permissions on id_dsa.
-rw------- 1 user user 673 2010-08-20 15:43 authorized_keys2
-rw------- 1 user user 668 2010-08-26 07:06 id_dsa
-rw-r--r-- 1 user user 604 2010-08-26 07:06 id_dsa.pub
Copy the id_dsa.pub (public key) to the Home computer
Output (enter your password on home when prompted):
Now try logging into the machine, with
and check the authorized_keys file to make sure we haven’t added extra keys that you weren’t expecting.
Install autossh on remote:
sudo apt-get install autossh
Set up the script:
sudo mkdir /etc/tunnel
sudo touch /etc/tunnel/tunnel.sh
sudo cp .ssh/id_dsa /etc/tunnel/
Make sure permissions are tight:
sudo chmod -R 700 /etc/tunnel
sudo chmod 600 /etc/tunnel/id_dsa
Use the text editor of your choice; if you use Gnome/Gedit, run from Alt+F2:
gksu gedit /etc/tunnel/tunnel.sh
If you use KDE/Kate:
kdesudo kate /etc/tunnel/tunnel.sh
I use vim:
sudo vim /etc/tunnel/tunnel.sh
Now insert this script into tunnel.sh, obviously changing ‘Home’ to the name of the computer you will be using to work on the target machine ‘user’ to your user name. I ran this on a LAN before deploying the server (Did you know you can access a linux machine on your LAN using [hostname].local?):
SSH_OPTIONS=" -i /etc/tunnel/id_dsa"
# Always assume initial connection will be successful
# Disable echo service, relying on SSH exiting itself
#to test, use (check out man ssh for explanation of options:
autossh -vv -- $SSH_OPTIONS -o 'ControlPath none' -R 10101:localhost:22 user@Home.local -N > /var/user_sshlog.out 2> /var/user_ssh_error.out &
#once proven, use (and rem out previous command):
#autossh -f -- $SSH_OPTIONS -o 'ControlPath none' -R 10101:localhost:22 user@Home.local -N 2> /var/user_ssh_error.out
NOTE: the logs are a Big Deal! With autossh or ssh set to verbose output (-vv), you can glean an awful lot of info from the log.
Assign script to run on start-up in /etc/rc.local:
sudo vim /etc/rc.local
Append this at the end of your rc.local:
#Delay scipt in case the network needs more time to start running. Adjust as necessary.
#Start autossh script, output to log
/etc/tunnel/tunnel.sh > /var/user_tunnel.log 2>&1
echo autossh to Home.local started
[Note(!): Make sure you have logged in to Home from Target as root before trying to run script (to get past the authentication question):]
$$ ssh user@Home.local
The authenticity of host 'Home.local (192.168.1.103)' can't be established.
RSA key fingerprint is f1:e9:5a:ea:58:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:6a.
Are you sure you want to continue connecting (yes/no)?
To test functionality without time-consuming reboots use:
sudo /etc/init.d/rc.local start
If you want to try again, make sure you kill all running autossh processes:
sudo killall autossh
Sometimes you may have to use:
sudo killall -9 autossh
Check your logs if you have problems:
Now for the acid test. Login from Home
ssh -p 10101 user@localhost
If it works, you're done (unless you need to change the address in your tunnel.sh when you move the target from your LAN to the off-site location).
For additional testing of the script from a user file before committing it to /etc/rc.local, try this:
cat /etc/tunnel/tunnel.sh >> ~/tunnel.sh
chmod 700 tunnel.sh
and modify the script, commands, and log paths as appropriate.
If your Home is behind a NAT, set port forwarding appropriately.
To use NX, configure your session similarly to this:
If you see typos or errors in strategy here, I'd be obliged if you would leave comments accordingly.