SSH tunnels provide a very effective means to access remote services and applications. Not only does it provide encryption of data between hosts, but it allows you to route connections between a sequence of servers, thus chaining connections. A common use of this method is to provide encrypted connections to MySQL servers so that user accounts can be limited to only “localhost” privileges, yet accessed from remote workstations without having to run MySQL+SSL.
The concept is simple, for example let’s say you have three servers: localhost (your workstation in America), a server in Europe, and a server in Japan. You want to access Apache running on port 80 on the Japan server but because of firewall restrictions you cannot access port 80 remotely, and to make things more difficult the Japan server only allows SSH connections from the Europe server’s IP. We can solve this by creating a SSH tunnel that forwards localhost port 8080 (arbitrary port number) over an SSH connection to Europe, and then through another SSH tunnel to the Japan server’s port 80 to talk to Apache. Then we simply load http://localhost:8080 in our web browser and we’ll have access to Apache running on the server in Japan. You can think of the chain like this: localhost:8080->Europe:8080->Japan:80. The SSH command to create this chain is as follows: ssh -v -L 8080:localhost:8080 europe ssh -v -L 8080:localhost:80 -N japan
Since you are not limited to chaining between one or two hosts with this method, and you can choose different ports and services to forward, the opportunities are nearly endless. Here’s a script that makes building the tunnel commands easier.
#!/bin/bash ## NAME: TunnelMaker ## PURPOSE: Creates multi-hop SSH tunnels for forwarding data+connections ## AUTHOR: Matt Reid ## DATE: 2012-05-30 ## VERSION: 1.0.2-jf ## SAMPLE: # echo "ssh -v -L $localport:localhost:$remotehost1port $remotehost1 \ # ssh -v -L $remotehost1port:localhost:$remotehost2port -N $remotehost2 ... repeat" echo "------------------->" echo "-->Tunnel-->Maker-->" echo "-->version: 1.0.2-jf" echo "-->themattreid.com" echo "------------------->" ## Get sequence value echo -n "How many hops are we making [#remote servers]: " read hops ## Check user, set initial port user=`whoami` notice=' [>1024]' if [ "$user" = "root" ]; then notice=''; fi ## Initialize some vars c=1 #session value counter n='' #session value for notice p='' #session value for port final='' #end result string for tunnel command localport='' #session value holder let hops=hops+1 #increment hops for iteration in loop ## Build connection strings while [ $c -lt $hops ]; do echo "" echo -n "Host[or IP] for hop#[$c]: "; read host echo -n "Localhost port for $host$notice: "; read localport echo -n "Destination port for $host$notice: "; read destport if [ "$localport" = '' ]; then echo "no port selected. exiting." exit 1; fi if [ "$destport" = '' ]; then echo "no port selected. exiting." exit 1; fi if [ $c -gt 1 ]; then n="-N "; fi #added to suppress remote login string="ssh -v -L $localport:localhost:$destport $n$host " final="$final $string" let c=c+1 p=$localport done echo "You can initiate your tunnels via command: \"$final\"" echo "Shall I start the tunnel now? [y,N]: "; read choice if [ "$choice" = "Y" ] || [ "$choice" = "y" ]; then `$final`; else exit 0; fi
Here’s the sample output from our example with Europe and Japan.
> ./tunnelmaker -------------------> -->Tunnel-->Maker--> -->version: 1.0.2-jf -->themattreid.com -------------------> How many hops are we making [#remote servers]: 2 Host[or IP] for hop#: europe Localhost port for europe [>1024]: 8080 Destination port for europe [>1024]: 8080 Host[or IP] for hop#: japan Localhost port for japan [>1024]: 8080 Destination port for japan [>1024]: 80 You can initiate your tunnels via command: " ssh -v -L 8080:localhost:8080 europe ssh -v -L 8080:localhost:80 -N japan " Shall I start the tunnel now? [y,N]: N