Aloha monks! I was recently stuck with trying to solve a problem for a friend; he was using rsync, specifically, File::Rsync, to rsync files over to and from a remote host; however, he couldn't perform any authentication because he was not able to use the command line, due to some restriction. So I spoke with him and we came up with the idea that what he could do would be to simply just key the remote host with his public key, and this is the script I wrote to take care of that:
#!/usr/bin/env perl use strict; use warnings; use Net::SSH::Perl; my ($username, $hostname, $password) = @ARGV; my $die = 0; if(!defined $username) { print "It seems you forgot to provide a username.\n"; $die = 1; } if(!defined $hostname) { print "It seems you forgot to provide a hostname.\n"; $die= 1; } if(!defined $password) { print "It seems that you forgot to provide a password.\n"; $die = 1; } if($die) { print "Usage: key <username> <hostname> <password>\n"; exit; } my $filename; if(-f $ENV{"HOME"}."/.ssh/id_rsa.pub") { $filename = "id_rsa.pub"; } elsif(-f $ENV{"HOME"}."/.ssh/id_dsa.pub") { $filename = "id_dsa.pub"; } else { print "I couldn't find an SSH public key generated. Try running + ssh-keygen then run me again."; exit } my $key = &slurp($ENV{"HOME"}."/.ssh/$filename"); my $ssh = Net::SSH::Perl->new($hostname); $ssh->login($username,$password); $ssh->cmd("mkdir ~/.ssh"); my ($out,$err,$exit) = $ssh->cmd("cat ~/.ssh/authorized_keys"); my $suc; $suc = has_key($key,$out); if($suc) { print "$username\@$hostname is already keyed with your key.\n" +; exit; } $ssh->cmd("cat >> ~/.ssh/authorized_keys",$key); ($out,$err,$exit) = $ssh->cmd("cat ~/.ssh/authorized_keys"); if(has_key($key,$out)) { print "Your public key has been successfully added; try runnin +g ssh $username\@$hostname to see if it works.\n"; } else { print "Your key could not be added.\n"; print "Try connecting manually, then adding the contents of ". +$ENV{"HOME"}."/.ssh/$filename manually to /home/$username/.ssh/author +ized_keys\n"; exit; } print "\n"; sub slurp { my $fname = shift; open my $FILE, '<', $fname; local $/; my $key = <$FILE>; close $FILE; return $key; } sub has_key { my ($key, $keys) = @_; chomp($key); my $suc = 0; my @keys = split /\n/,$keys; my $i = 1; foreach my $k (@keys) { chomp($k); if($key eq $k) { $suc = 1; last; } else { $suc = 0; } $i++; } return $suc; }
Hopefully someone else will find this as interesting as I did.

Replies are listed 'Best First'.
Re: SSH Keyer for passwordless logins.
by salva (Canon) on May 26, 2011 at 19:44 UTC
    use Net::OpenSSH; my $ssh = Net::OpenSSH->new($hostname, user => $username, password => +$password); $ssh->error and die "unable to connect: " . $ssh->error; $ssh->rsync_put(@local_files, $remote_dir);

      Thanks for sharing that, I evidently didn't even look into the possibility of Net::OpenSSH. Mostly because I had never actually seen it before. I'll be sure to check it more, see if it'll work for what's going on.

Re: SSH Keyer for passwordless logins.
by jwkrahn (Abbot) on May 26, 2011 at 19:01 UTC
    sub slurp { my $fname = shift; open my $FILE, '<', $fname; local $/; my $key = <$FILE>; close $FILE; return $key; }

    You should verify that the file opened correctly and take appropriate action if it didn't rather than trying to use a possibly invalid filehandle.

    You should chomp the data returned here rather than in another subroutine.

    sub slurp { my $fname = shift; local $/; open my $FILE, '<', $fname or die "Cannot open '$fname' becaus +e: $!"; chomp( my $key = <$FILE> ); return $key; }


    sub has_key { my ($key, $keys) = @_; chomp($key); my $suc = 0; my @keys = split /\n/,$keys; my $i = 1; foreach my $k (@keys) { chomp($k); if($key eq $k) { $suc = 1; last; } else { $suc = 0; } $i++; } return $suc; }

    The $i variable is superfluous in this subroutine.

    You are spliting, and thus removing newlines from @keys and then you are chomping the elements of @keys to remove newlines that aren't there anymore?

    The else clause in the loop is totally superfluous because you exit the loop as soon as you change the value of $suc.

    sub has_key { my ( $key, $keys ) = @_; foreach my $k ( split /\n/, $keys ) { return 1 if $key eq $k; } return 0; }

    Or:

    sub has_key { my ( $key, $keys ) = @_; return scalar grep $key eq $_, split /\n/, $keys; }
      To address a few of the things you said:

      Thanks for reminding me about putting some sort of error handling in. I had to grok this out of some code that it was integrated into, and as such, I forgot to fix a few things with it.


      I definitely can see a lot of superfluous elements in the code, but that's because those elements were all there originally for testing purposes and I forgot to yank them out. Previously, the else clause was there to output the current key, and compare it to the key that was provided. Nonetheless, I needed to fix up some code in it anyways, so, thanks for looking through and letting me know where I tripped up.


      Also, for some reason or another, when I hadn't chopped the data, even in something already split, it still wasn't matching. My theory is that there may have been an extra character in there that I may have bee nforgetting. Nonetheless. I appreciate your input, and I will put that in my code to get everything back to how it should be. :D