Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

ssh chain

by shmem (Canon)
on Sep 26, 2006 at 09:21 UTC ( #574891=CUFP: print w/ replies, xml ) Need Help??

My work involves logging into remote machines via ssh, possibly over multiple hops to do random tasks.

Tired of typing

ssh -p 3334 shmem@gateway -L1025:host.example.com:22

and for the next hop, in another shell,

ssh -p 1025 admin@localhost -L1026:192.168.123.2:975 -R993:localhost:1 +993

then in yet another shell

ssh -p 1026 wrxd15@localhost -L1027:192.168.254.2:22

and so on, I cranked out the following, which lets me say

ssc shmem@gateway:3334 admin@host.example.com wrxd15@192.168.123.2:975 +,-R993:localhost:1993 root@192.168.254.2

which does the chaining. I only have to type in the hops in order, and presto - I have a shell open at the endpoint. Aliasing a chain is much easier this way. Secure Shells are invoked with -f -N except the last.

#!/usr/bin/perl -w # $Id: ssc,v 0.4 2006/09/28 20:31:37 shmem Exp $ use strict; use Getopt::Std; my (%o,@pids); getopts('p:',\%o); $o{'p'} ||= 1025; # default port range start die "ssh chain - usage: ssc [-p] user\@host1[:sshport][,arg,arg,...] \ +\\n" ." user\@host1[:sshport][,arg,arg,...] \ +\\n" ." ...\n" ."where -p is of the forwarded ports range's start (default: $o{p}) +\n" unless @ARGV; $o{'p'}++ while port_busy($o{'p'}); while (@ARGV) { my ($host,@args) = split/,/,shift; my ($port,$nexthop,$silent,$nextport); $port = $nextport = $nexthop = $silent = ''; $host =~ s/:(\d+)// && ($port="-p $1"); if(@ARGV) { my ($c,@args) = split/,/,$ARGV[0]; my ($u,$host) = split /\@/,$c; $host =~ s/:(\d+)// && ($nextport=$1); $nexthop = "-L$o{p}:$host:". ($nextport ? $nextport : 22); $ARGV[0] = "$u\@localhost:$o{p}"; $ARGV[0] .= ','. join(',',@args); $o{'p'}++; $o{'p'}++ while port_busy($o{'p'}); $silent = '-f -N'; } if(@ARGV) { # clean up commandline (my $cmd = "ssh $silent $port $host $nexthop @args") =~ s/\s+/ /g; $cmd =~ s/\s+$//; # maybe I should just 'last' here # - not die - and kill all shells so far system $cmd and die "Can't '$cmd': $!\n"; # since ssh - f does a fork, we must get it's pid via ps # XXX is there a portable way to do this? open(I,"ps ux |") or die "Can't run ps: $!\n"; while(<I>) { /$cmd/ && /\b(\d+)\b/ && push @pids, $1; } } else { print "ssh $silent $port $host $nexthop @args\n"; system "ssh $silent $port $host $nexthop @args"; } } # reap all my shells kill 15,$_ for reverse @pids; # XXX is there a portable way to check for busy ports? sub port_busy { system("lsof -i :$_[0] >/dev/null") ? 0 : 1 }

Comments, critique and improvements welcome.

--shmem

update Added port_busy

update: Added code to reap the chain after endpoint shell exits

_($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                              /\_¯/(q    /
----------------------------  \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

Comment on ssh chain
Select or Download Code
Re: ssh chain
by salva (Abbot) on Sep 26, 2006 at 15:57 UTC
    You don't really need to create TCP tunnels to chain ssh connections.

    You can do it as...

    ssh -t shmem@gateway ssh -t admin@host.example.com \ ssh -t wrxd15@192.168.123.2 ssh -t root@192.168.254.2
    You can also use ProxyCommand entries inside your ~/.ssh/config file to connect to remote servers in custom ways.

    For instance, to connect to foo.net.org via other.server.org via my.server.org...

    # .ssh/config Host foo ProxyCommand ssh me@my.server.org \ ssh me@other.server.org \ ssh root@foo.server.org /usr/sbin/sshd -i
    And then...
    ssh foo
      Ah yes, but...
      ssh -t shmem@gateway ssh -t admin@host.example.com \ ssh -t wrxd15@192.168.123.2 ssh -t root@192.168.254.2

      ... I don't have the password for wrxd15@192.168.123.2, nor is this user allowed to login to 192.168.254.2 as root. The root passwords are generally unknown. It's my public key that is installed on each of these accounts in ~/.ssh/authorized_keys2, and my private key certainly won't leave my machine.

      Furthermore, with a complex network setup, and being forced to use multiple ways to connect to a remote site - that occurs generally after a service down alert has arrived - entries in ~/.ssh/config aren't that useful for me.

      I whipped the above cruft up because I need tunneling and forwarding of arbitrary local/remote ports to/from the remote host with changing requirements.

      <update>

      I need

      host1 host2 host3 +-------+ +-------+ +-------+ ssh 1 ----------\ | | | | | ssh 2 ---------------------\ | | | ssh 3 ================================> | ssh 2 ---------------------/ | | | ssh 1 ----------/ | | | | | +-------+ +-------+ +-------+

      rather than

      host1 host2 host3 +-------+ +-------+ +-------+ | | | | | | | | | | | | ssh 1 ======> ssh 2 ===> ssh 3 =======> | | | | | | | | | | | | | +-------+ +-------+ +-------+

      The most insane thing I do sometimes is tunneling a complete network via ppp through 5+ chained ssh's:

      host1 host2 host3 | | +-------+ +-------+ +-------+ | | ssh 1 ---------\ | | | | proxy | |n| ssh 2 --------------------\ | | arp | |e| ssh 3 --------------------------------\ \| |t| ppp ===== compress ======================^===== | ssh 3 --------------------------------/ | |w| ssh 2 --------------------/ | | | |o| ssh 1 ---------/ | | | | | |r| +-------+ +-------+ +-------+ |k|

      </update>

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
        This would appear to be an absolutely classic case for using agent forwarding.

        The TCP forwarding method is a little more resistant to interception on the intermediate hosts by someone with root privs, but this is pretty marginal in most situations.

        Personally I would (and do, frequently) use a chain of ssh with agent forwarding to get this effect.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://574891]
Approved by Corion
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (12)
As of 2015-07-07 14:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (89 votes), past polls