In situations when no VPN is either not available or you just think it’s an overkill to configure an ssh tunnel can be a pretty good alternative. An SSH tunnel works by setting up an ssh connetion between two hosts and use that connection to transport normal network traffic. On one side of the tunnel, the OpenSSH software takes anything that is sent to a specific port and sends it over to the other side of the connection. When the packets arrive on the target side, the OpenSSH software forwards them to the correct local port. Natrually the traffic is encrypted on it’s way, thereby creating a miniature VPN.
Setting up an ssh tunnel is quite straigt forward, for example at the terminal:
ssh -NL 8080:127.0.0.1:80 firstname.lastname@example.org
Let’s break that down:
- -N means that SSH should just create the connection and then do nothing.
- -L means that the local side is the listening side. If you want it the other way around, use -R instead.
- 8080:127.0.0.1:80 tells ssh that the listening side should listen on port 8080 of it’s localhost interface and the other side should forward it to port 80. So, on the machine that runs this command, anything sent to 127.0.0.1:8080 is forwarded to port 80 on the other side
- email@example.com tells ssh to connect to 192.168.10.10 with username root
There’s really only one problem with this. Whenever the ssh connection breaks the tunnel will break and you have to rerun that command again. Depending on your situation this might be a problem and that’s why autossh exists.
Autossh is a tool that sets up a tunnel and then checks on it every 10 seconds. If the tunnel stopped working autossh will simply restart it again. So instead of running the command above you could run:
autossh -NL 8080:127.0.0.1:80 firstname.lastname@example.org
Note: starting an ssh tunnel with autossh assumes that you have set up public key authentication between the client and server since there’s no way for autossh to ask you for the password. Especially if you want to follow the rest of this article and have the tunnels start automatically. Please read more here for details on how to setup public key authentication.
Having autossh monitor your tunnels is a great advantage, but it still requires a command to be run whenever autossh itself would die, such as after a reboot. If you want your ssh tunnels to persist over a reboot you would need to create a startup script. Since Ubuntu 9.10 (Karmic) Ubuntu have used Upstart as its main mechanism to handle startup scripts so the rest of this article will discuss how such a script can be created.
I wanted to achieve three things with my startup script. First, I’d like my tunnel to come up when the computer boots. Second, I’d like to be able to keep configuration for multiple tunnels in one file. And lastly, I’d like to be able to control (start/stop) my tunnels without having to kill individual ssh processes.
Three files are created:
description "autossh tunnel" author "Erik Torsner " start on (local-filesystems and net-device-up IFACE=eth1 and net-device-up IFACE=wlan1) stop on runlevel  pre-start script NUMHOSTS=$(egrep -v '^[[:space:]]*$' /etc/autossh.hosts | wc -l) for i in `seq 1 $NUMHOSTS` do start autossh_host N=$i done end script
stop on stopping autossh respawn instance $N export HOST=$N script ARGS=$(head -$N /etc/autossh.hosts | tail -1) exec autossh $ARGS end script
And lastly, the config file /etc/autossh.hosts
-NL 8080:127.0.0.1:80 email@example.com -NL 8080:127.0.0.1:80 firstname.lastname@example.org
Rationale for each file
This is the main startup script. The first parts of the file sets the conditions that triggers this script to start or stop. I’ve told my script to start as soon as the local filesystem is ready and when mu network interfaces are up. Chances are that you want to modify this to suit your computer, at least the name of the network interfaces.
The actual script part is a simple bash script that checks the contents of a config file to determine how many tunnels that needs to be started. It then starts a numer of autossh_host jobs with an individual id that happens to correspond a the line number. It would have been nice to just start the autossh processes directly from this script, but autossh is a blocking command and only the first line would be executed on startup. Dropping the process to the background using a & character at the end would work but the resulting autossh processes would be out of our controll.
This is the job where the interesting part actually happens. Note that this script doesn’t have any “start on” stanza at all, it’s only ever started when the main job tells it to start. The interesting part is its “stop on” stanza that simply says that it should stop whenever the parent job is stopped. Using this mechanism all child jobs can be controlled via the parent job.
The script part uses a very naive method of picking out the correct line from /etc/autossh.hosts and use that line as the argument for autossh. Note that with this approach, the config file /etc/autossh.hosts can’t contain any empty lines or comments at all. Room for improvement.
This is just a config file with one row per ssh tunnel to create. Each line consists of the entire parameter line sent to autossh. Again, please note that the parsing of this file is very naive and won’t allow for any blank lines or comments at all.
Starting the tunnels
If everything is correctly set up, you should now be able to start your tunnels using:
sudo start autossh
and stopping them with
sudo stop autossh
If you have problems, check out the logfiles under /var/log/upstart. There should be one logfile for each script/instance, so expect to find autossh.log as well as many autossh_host-N.log.
Questions? Did I miss something? Is my solution entierly wrong? Let me know in the comments.