Search code examples
perlsshcygwinrsa

SSH login through Perl


I am using Net:Appliance::Session to login to a remote Unix server, but am not able to connect. Below is my code and the debug output:

my $s = Net::Appliance::Session->new({
     personality => 'Bash',
     transport => 'SSH',
     host => $host,
});
$s->set_global_log_at('debug');
try {
     print "Trying to connect\n";
     $s->connect({ username => $user, password => $pass });
     print "Executing command\n";
     print $s->cmd($cmd);   
}
catch {
     warn "failed to execute command: $_";
}
finally {
     $s->close;
};

And the output is:

Trying to connect
[   0.019420] pr finding prompt
[   0.028553] tr creating Net::Telnet wrapper for ssh
[   0.031377] tr connecting with:  ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o CheckHostIP=no -l user ...
[   3.151205] du   SEEN:
Warning: Permanently added '...' (RSA) to the list of known hosts.
[   3.183935] pr failed: [Can't call method "isa" on an undefined value at /usr/lib/perl5/site_perl/5.14/Net/CLI/Interact/Phrasebook.pm line 247.
], sending WAKE_UP and trying again
[   3.184943] pr finding prompt
[   4.898408] du   SEEN:
Warning: Permanently added '...' (RSA) to the list of known hosts.
Password:
[   4.920447] pr failed to find prompt! wrong phrasebook?
failed to execute command: Warning: Permanently added '...' (RSA) to the list of known hosts.
Password:
        ...propagated at /usr/lib/perl5/site_perl/5.14/Net/CLI/Interact/Role/Prompt.pm line 127.

When I login through Putty, I get the following response and can login successfully:

login as: user
Using keyboard-interactive authentication.
Password:

I cannot figure out what I am doing wrong. Any help is appreciated.

EDIT: I think I should mention that I am using Cygwin for this. I have manually logged in to the remote server and the keys in my .ssh/known_hosts file are also set, but still get the RSA error when running this program in Cygwin. I saw this question in SO: "Warning: Permanently added to the list of known hosts” message from Git and added the line UserKnownHostsFile ~/.ssh/known_hosts to my config file, but the error refuses to go away.

EDIT2: When I use the -vvv option in the above program, I get the following output:

Trying to connect
[   0.020327] pr finding prompt
[   0.062541] tr creating Net::Telnet wrapper for ssh
[   0.063709] tr connecting with:  ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o CheckHostIP=no -vvv -l user 1.1.1.1
[   0.731041] du   SEEN:
OpenSSH_6.2p2
[   0.851829] pr failed: [Can't call method "isa" on an undefined value at /usr/lib/perl5/site_perl/5.14/Net/CLI/Interact/Phrasebook.pm line 247.
], sending WAKE_UP and trying again
[   0.852459] pr finding prompt
[   0.852748] du   SEEN:
OpenSSH_6.2p2, OpenSSL 1.0.1e 11 Feb 2013
[   0.863739] pr failed to find prompt! wrong phrasebook?
failed to execute command: OpenSSH_6.2p2, OpenSSL 1.0.1e 11 Feb 2013
        ...propagated at /usr/lib/perl5/site_perl/5.14/Net/CLI/Interact/Role/Prompt.pm line 127.

Solution

  • The Net::Appliance::Session module is using is set of matching patterns called "Phrasebook" to guess password query output, command ending prompt, ...

    In your case, there are 2 major issue and one minor/cosmetic one:

    1. Net::Appliance::Session rely on connection profile. The correct one is named "bash" and not "Bash"
    2. The bash default phrasebook (located in "~site_perl/Net/CLI/Interact/phrasebook/unix/bash/pb") is targeting ssh/bash based appliance and is not matching your everyday unix server behavior:

      prompt user  
          match /[Uu]sername: $/  
      
      prompt pass
          match /password(?: for \w+)?: $/
      
      prompt generic
          match /\w+@.+\$ $/
      
      prompt privileged
          match /^root@.+# $/
      
      macro begin_privileged
          send sudo su -
          match pass or privileged
      
      macro end_privileged
          send exit  
          match generic  
      
      macro disconnect  
          send logout  
      

      As you can see, both "generic" and "pass" prompt does not match your usual linux password and prompt. You will need to adjust it to your needs:

      • create a library structure by creating some nested directory: "mylib\mybash\"
      • make a copy of the "bash" phrasebook to that nested directory and edit it to match your unix server behaviour.
    3. There is also the ssh warning output:

    Warning: Permanently added '...' (RSA) to the list of known hosts.

    You just need to set ssh warnings to off using either the "-q" or "-o LogLevel=quiet" options to the ssh calling options.

    So, in the end, your code would look like that:

    my $s = Net::Appliance::Session->new
      ({ add_library     => 'mylib',
         personality     => 'mybash',
         transport       => 'SSH',
         host            => $host,
         connect_options => { opts => [ '-q', ], },
       });
    $s->set_global_log_at('debug');
    try {
        print "Trying to connect\n";
        $s->connect({ username => $user, password => $pass });
        print "Executing command\n";
        print $s->cmd($cmd);
    }
    catch {
        warn "failed to execute command: $_";
    }
    finally {
        $s->close;
    };
    

    With a phrasebook like this one (quickly tuned to my freebsd server):

    prompt user
        match /[Uu]sername: $/
    
    prompt pass
        match /[Pp]assword:\s*$/
    
    prompt generic
        match /\w+@.+[\$>] $/
    
    prompt privileged
        match /^root@.+# $/
    
    macro begin_privileged
        send sudo su -
        match pass or privileged
    
    macro end_privileged
        send exit
        match generic
    
    macro disconnect
        send logout
    
    macro paging
        send terminal length %s
    

    NOTE:
    About "Net::Appliance::Session" vs "Net::OpenSSH":

    • both modules are handling ssh connectionx fine
    • "Net::Appliance::Session" is more "cisco/whatever-appliance" oriented, but should permit easily to connect to a server, execute 1 command, get its result, then go root and execute another command (very handy if you don't have direct root access from ssh)
    • "Net::OpenSSH" is handling command execution though ssh on 1 command only basis, that is it execute a command, get its result and exit. No direct way to set an environment, go root and execute the command (you need to use a wrapper like Net::Telnet on it to do that)
    • "Net::OpenSSH" requires a fairly recent version of openssh client and does not work on Windows, not even under Cygwin (see "Net::OpenSSH" manual).