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.
## NAME: TunnelMaker
## PURPOSE: Creates multi-hop SSH tunnels for forwarding data+connections
## AUTHOR: Matt Reid
## DATE: 2012-05-30
## VERSION: 1.0.2-jf
# echo "ssh -v -L $localport:localhost:$remotehost1port $remotehost1 \
# ssh -v -L $remotehost1port:localhost:$remotehost2port -N $remotehost2 ... repeat"
echo "-->version: 1.0.2-jf"
## Get sequence value
echo -n "How many hops are we making [#remote servers]: "
## Check user, set initial port
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 -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 "
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.
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