Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
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 (Monsignor) 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 taking refuge in the Monastery: (11)
As of 2014-10-22 16:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (119 votes), past polls