An SSH tunnel via multiple hops

You basically have three possibilities:

  1. Tunnel from localhost to host1:

    ssh -L 9999:host2:1234 -N host1
    

    As noted above, the connection from host1 to host2 will not be secured.

  2. Tunnel from localhost to host1 and from host1 to host2:

    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    

    This will open a tunnel from localhost to host1 and another tunnel from host1 to host2. However the port 9999 to host2:1234 can be used by anyone on host1. This may or may not be a problem.

  3. Tunnel from localhost to host1 and from localhost to host2:

    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    

    This will open a tunnel from localhost to host1 through which the SSH service on host2 can be used. Then a second tunnel is opened from localhost to host2 through the first tunnel.

Normally, I'd go with option 1. If the connection from host1 to host2 needs to be secured, go with option 2. Option 3 is mainly useful to access a service on host2 that is only reachable from host2 itself.


There is an excellent answer explaining the use of the ProxyCommand configuration directive for SSH:

Add this to your ~/.ssh/config (see man 5 ssh_config for details):

Host host2
  ProxyCommand ssh host1 -W %h:%p

Then ssh host2 will automatically tunnel through host1 (also works with X11 forwarding etc.).

This also works for an entire class of hosts e.g. identified by domain:

Host *.mycompany.com
  ProxyCommand ssh gateway.mycompany.com -W %h:%p

Update

OpenSSH 7.3 introduces a ProxyJump directive, simplifying the first example to

Host host2
  ProxyJump host1

OpenSSH v7.3 onward supports a -J switch and a ProxyJump option, which allow one or more comma-separated jump hosts, so, you can simply do this now:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host

We have one ssh gateway into our private network. If I'm outside and want a remote shell on a machine inside the private network, I would have to ssh into the gateway and from there to the private machine.

To automate this procedure, I use the following script:

#!/bin/bash
ssh -f -L some_port:private_machine:22 user@gateway "sleep 10" && ssh -p some_port private_user@localhost

What is happening:

  1. Establish a tunnel for the ssh protocol (port 22) to the private machine.
  2. Only if this is successful, ssh into the private machine using the tunnel. (the && operater ensures this).
  3. After closing the private ssh session, I want the ssh tunnel to close, too. This is done via the "sleep 10" trick. Usually, the first ssh command would close after 10 seconds, but during this time, the second ssh command will have established a connection using the tunnel. As a result, the first ssh command keeps the tunnel open until the following two conditions are satisfied: sleep 10 is finished and the tunnel is no longer used.

After reading the above and glueing everything together, I've created the following Perl script (save it as mssh in /usr/bin and make it executable):

#!/usr/bin/perl

$iport = 13021;
$first = 1;

foreach (@ARGV) {
  if (/^-/) {
    $args .= " $_";
  }
  elsif (/^((.+)@)?([^:]+):?(\d+)?$/) {
    $user = $1;
    $host = $3;
    $port = $4 || 22;
    if ($first) {
      $cmd = "ssh ${user}${host} -p $port -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $first = 0;
    }
    else {
      $cmd .= " -L $iport:$host:$port";
      push @cmds, "$cmd -f sleep 10 $args";
      $cmd = "ssh ${user}localhost -p $iport -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $iport ++;
    }
  }
}
push @cmds, "$cmd $args";

foreach (@cmds) {
  print "$_\n";
  system($_);
}

Usage:

To access HOSTC via HOSTA and HOSTB (same user):

mssh HOSTA HOSTB HOSTC

To access HOSTC via HOSTA and HOSTB and use non-default SSH-portnumbers and different users:

mssh user1@HOSTA:1234 user2@HOSTB:1222 user3@HOSTC:78231

To access HOSTC via HOSTA and HOSTB and use X-forwarding:

mssh HOSTA HOSTB HOSTC -X

To access port 8080 on HOSTC via HOSTA and HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080