patris has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to complete a script that goes through all the nodes/hosts on a list, and runs a ssh command on them, if this is _not_ the write way to do this, please let me know. anyhow, backticks or system both won't work... here is the broken script.. can you suggest anything to fix it or a different method?
#!/usr/bin/perl open (INFILE, "</root/automate/system.list") || die "could not open sy +stem.list: $!"; @system_list = <INFILE>; close INFILE; foreach $system (@system_list){ `ssh $system date`; #date could be substitute with any unix command like pwd,ls,etc. }

edited: Tue Jun 24 14:47:08 2003 by jeffa - code tags

Replies are listed 'Best First'.
Re: simple iterate and run cmd script
by KPeter0314 (Deacon) on Jun 24, 2003 at 14:57 UTC
    I use Net::SSH like this:

    use Net::SSH qw(sshopen3); use strict; use warnings; # Watch for children and reap them. This is to avoid zombies left by +the ssh. $SIG{CHLD} = 'IGNORE'; my $command = "/usr/bin/date"; foreach $system (@system_list){ sshopen3( $system, *WRITER, *READER, *ERROR, $command ) || die "ssh +: $!"; while (<READER>) { chomp(); $date{$system} = $_; # Or do other stuff } close(READER); close(WRITER); close(ERROR); }
    I feel it is easier to deal with the output this way.

    Lots of stuff in Super Search where I learned this. There are other SSH modules, but this is the one I like.

    -Kurt

Re: simple iterate and run cmd script
by l2kashe (Deacon) on Jun 24, 2003 at 15:45 UTC
    From personal experiance, here it what I can say. There are a few modules that make life easier in regards to ssh'ing around. Namely Net::SSH, and Net::SSH::Perl. The first is nice, but you *have* to use SSH Key exchange to authenticate against the remote host. This can lead to either security policy or administration nightmares. The second I have gotten to compile on FreeBSD, OpenBSD, and Linux as well as run cleanly without issues. I have had issues with it under Solaris 2.6->2.9. Net::SSH::Perl also has a huge prerequisite list of modules which kind of makes it not so nice for roll out in your enterprise network. Net::SSH::Perl has alot of whizbang functionality that just can't be beat for anything you need to do remotely including inline SCP through the SSH tunnel established.

    Now aside from the modules you can use SSH the way you want to. My first guess would be that you are being prompted to provide a password to authenticate against the remote system. But with both system and backticks this should come to STDERR (Im pretty sure its not STDOUT), and read from STDIN. So if you are calling this script from another script you might be missing that prompt. Also the SSH process communicates directy with the tty assigned to you, not the normal handles which makes working with it tricky.

    I had to resort to using IPC::Open2 in my attempts to control the spawned process. Ultimately I failed in my attempts to provide a password at run time due to, I think, the fact that I was passing a clear text password to the process, which I think should have been encrypted first, then passed.

    So the way around this is again to use SSH Keys, but like I said before make sure that is ok from a security policy standpoint on your network. (Picture someone comprimising the host your code resides on, finding your script, and then making use of the SSH Key exchange mechanism to wander through the hosts in question, all without raising alarms).

    One last suggestion would be to try running system("ssh -v $host date"); This will provide a bunch of usefull information and quite possibly could point to what is failing. I.e possible TCP wrappers issues, DNS lookups, etc..

    HTH..

    MMMMM... Chocolaty Perl Goodness.....
Re: simple iterate and run cmd script
by kutsu (Priest) on Jun 24, 2003 at 14:50 UTC

    I'm not sure, as you might want to put in <code> tags, but: do you need/have root permissions to go into /root directory

    Might read: How do I post a question effectively? for some ideas on posting

    Updated: code tags allowed better read so removed last suggestion and changed link per PodMaster, thanks Pod

    "Pain is weakness leaving the body, I find myself in pain everyday" -me

Re: simple iterate and run cmd script
by Tomte (Priest) on Jun 24, 2003 at 14:51 UTC

    A few remarks and another solution you might find useful:

    • run such scripts with -Tw during development
    • use absolute paths in backticks or system calls
    • check the lines you read against a positive-list of allowed characters/a hostname/ip-address matching regexp
    • Do you really have setup ssh pubkey authentication for the user root? I recommend to run such scripts as a halfway secure user, and use a key with a passphrase and ssh-agent along with keychain or somesuch.

    I have written a similar script, maybe you can adept it to your needs, if our fellow monks don't think its to insecure

    Note that -T is absent, because it runs with it enabled, and I can spare a few CPU-cycles this way, and that this script won't runout of the box for you, due to the absence of util.pm, but it should illustrate a IMVHO sane solution.

    Edit: inserted untainting version :)

    regards,
    tomte


    Hlade's Law:

    If you have a difficult task, give it to a lazy person --
    they will find an easier way to do it.

Re: simple iterate and run cmd script
by Bilbo (Pilgrim) on Jun 24, 2003 at 14:46 UTC

    So in what way does it not work? Do you have any error messages? Is ssh failing because it is waiting for a password?

Re: simple iterate and run cmd script
by zentara (Cardinal) on Jun 24, 2003 at 18:23 UTC
    Here is another method, where you use passwords. You could put the %hostdata hash in a config file if you wanted.
    #!/usr/bin/perl use strict; use warnings; use Net::SSH::Perl; my %hostdata = ( 'localhost' => { user => "zz", password => "ztest", cmdtorun => "ls -la", misc_data => [], }, 'zentara.zentara.net' => { user => "zz", password => "ztest", cmdtorun => "/usr/bin/uptime", misc_data => [], }, ); foreach my $host (keys %hostdata) { my $ssh = Net::SSH::Perl->new($host, port => 22); #, debug => 1 +); $ssh->login($hostdata{$host}{user},$hostdata{$host}{password} ); my ($out) = $ssh->cmd($hostdata{$host}{cmdtorun}); print "$out\n"; }