Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Tunneling data over ssh is pretty straight-forward:

ssh -D9999 username@foreignhost.com

sets up port 9999 on your localhost as a tunnel to foreignhost.com, but I have more specific need:

  • I am working locally on localhost
  • host1 is accessible to localhost
  • host2 only accepts connections from host1
  • I need to create a tunnel from localhost to host2

Effectively I want to create a "multi-hop" ssh tunnel. How can I do this? Ideally, I'd like to do this without needing to be superuser on any of the machines.

share|improve this question
    
What did you use it for? I want to use it for socks proxy. Will it work? –  prongs Feb 29 '12 at 13:57
2  
Yes, you should be able to use the tunneled connection as a SOCKS proxy, unless host2 denies forwarding –  Mala May 3 '12 at 6:48
    
I was thinking about creating a wrapper over SSH that would set up that using multiple use of ProxyCommand. –  Pavel Šimerda Oct 19 '13 at 20:52

9 Answers 9

up vote 82 down vote accepted

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.

share|improve this answer
2  
option 3 was what i was looking for, thanks! –  Mala Jan 25 '10 at 1:38
    
I want to do browsing this way. Which one's best? I tried first one but it didn't work. I set a socks proxy in my browser localhost:1234 but no luck. :( please help.. –  prongs Feb 29 '12 at 13:54
1  
@prongs try option 3 –  Mala May 3 '12 at 6:49
    
is there a way to forward my public key from localhost, through the tunnel of host 1, on to host2? –  Noli Dec 19 '12 at 8:00
2  
@Noli If you use ssh-agent (which you should), you can forward it through the connections using the -A option to ssh. –  Mika Fischer Dec 19 '12 at 11:36

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
share|improve this answer
1  
Is there a way to do this conditionally? I only want to do this sometimes. Also, this is specifically for commands, but I'm looking for something for all of port 22 (ssh, sftp, etc). –  Stephane Sep 11 '13 at 15:08
1  
@Stephane what do you mean by specifically for commands? Your SSH config is used by anything using ssh, including git, sftp etc. afaik. –  kynan Sep 11 '13 at 20:58
1  
@Stephane I'm not aware of a way of enabling this conditionally (e.g. only when you're outside the network of the target host). I set this option for all hosts in question in a configuration block and then (un)comment the line as needed. Not perfect, but it works. –  kynan Sep 11 '13 at 20:58
1  
@Stephane sure: ssh -F /path/to/altconfig. Beware this will ignore the system wide /etc/ssh/ssh_config. –  kynan Sep 12 '13 at 11:39
8  
An easy way to make settings "conditional" is to define two different hosts in .ssh/config, that have the same HostName. Connect to host2-tunnel when you want the tunnel, and host2 when you don't. –  Steve Bennett Sep 23 '13 at 6:55

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.
share|improve this answer
1  
Very clever!!! LOVE IT! –  Hendy Irawan May 6 '12 at 6:37

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
share|improve this answer
    
this is awesome –  Mala Mar 25 '12 at 1:20
1  
I seriously cannot thank you enough, this script makes my life easier on a daily basis. The only thing I changed was to add int(rand(1000)) to iport, to allow multiple instances to run at the same time. I definitely owe you a beer. –  Mala May 3 '12 at 7:20
    
This works really well. A further improvement would be to resolve HOSTB, HOSTC etc using localhost's /etc/hosts and ~/.ssh/config –  Steve Bennett Sep 23 '13 at 7:03
    
Also I second Mala's comment. Without the randomized port, if you then try to mssh HOSTA HOSTD you'll actually end up at HOSTB (and maybe won't realise..) –  Steve Bennett Sep 24 '13 at 2:39

Really, I think you should have a look here :

http://sshmenu.sourceforge.net/articles/transparent-mulithop.html

share|improve this answer

This answer is similar to kynan, as it involves the use of ProxyCommand. But it's more convenient to use IMO.

If you have netcat installed in your hop machines you can add this snippet to your ~/.ssh/config:

Host *+*
    ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') nc $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')

Then

ssh -D9999 host1+host2 -l username

will do what you asked.

I came here looking for the original place where I read this trick. I'll post a link when I find it.

share|improve this answer
1  
I believe this is the origination of the trick: wiki.gentoo.org/wiki/SSH_jump_host –  slm Mar 28 at 21:00
1  
@slm yes, that's it! Thanks! –  silviot Apr 3 at 14:01

you should be able to use port forwarding to access a service on host2 from localhost. A good guide is located here. Excerpt:

There are two kinds of port forwarding: local and remote forwarding. They are also called outgoing and incoming tunnels, respectively. Local port forwarding forwards traffic coming to a local port to a specified remote port.

For example, if you issue the command

ssh2 -L 1234:localhost:23 username@host

all traffic coming to port 1234 on the client will be forwarded to port 23 on the server (host). Note that localhost will be resolved by the sshdserver after the connection is established. In this case localhost therefore refers to the server (host) itself.

Remote port forwarding does the opposite: it forwards traffic coming to a remote port to a specified local port.

For example, if you issue the command

ssh2 -R 1234:localhost:23 username@host

all traffic which comes to port 1234 on the server (host) will be forwarded to port 23 on the client (localhost).

In your cast, replace localhost in the example with host2 and host with host1.

share|improve this answer
    
according to that article, the connection will only be secured until the middle machine (host1). Is there a way to make sure the whole thing stays secure? –  Mala Jan 16 '10 at 7:21
    
I've never tried this, but if host1 and host2 are both ssh servers, you might be able to set up a tunnel from host1 to host2, then set up a tunnel from localhost to host1 for the same service (getting your local and remote ports right). I don't know if that's possible in one command from localhost. –  fideli Jan 16 '10 at 16:13
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999:host2:80

Means bind to localhost:9999 and any packet sent to localhost:9999 forward it to host2:80

-R 9999:localhost:9999

Means any packet received by host1:9999 forward it back to localhost:9999

share|improve this answer

If you can SSH into both machines, take a look at ssh's ProxyCommand directive. This will let you go straight from localhost into host2 (in one easy command if you use public keys!!). Then you can do whatever you want with host2.

http://www.statusq.org/archives/2008/07/03/1916/

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.