I am writing a script to communicate with a remote server. I understand how to send a basic shell command such as "ls" or "find / |grep foo". But I am executing an interactive application at the other end.
If I call $ssh->shell, I get the prompt from the remote server so I know that SSH is receiving this prompt. But then I can't do anything because the script is blocked in a shell.
I installed two handlers. If I use the script to connect to a basic ssh host, and execute a shell command, the response is delivered to the handlers, so I know they are ok. So I expected the prompt from the application I am executing to be sent to these but it isn't.
#!/usr/bin/perl -w
use strict;
use Net::SSH::Perl;
my $host = 'rt.olsendata.com';
my $user = 'rtdemo';
my $pass = 'rtdemo';
my %params = ('debug' => 1,'protocol' => 2);
my $ssh = Net::SSH::Perl->new($host, %params);
$ssh->register_handler("stderr", sub{
my ($ssh, $packet) = #_;
receiveerrors($packet);
});
$ssh->register_handler("stdout", sub{
my ($ssh, $packet) = #_;
receivedata($packet);
});
my ($output, $errors, $exit) = $ssh->login($user, $pass);
At this point I need to respond to the prompt by sending a "1". But this is interpreted by the remote host as a shell command, not a response to the prompt. It returns the error "h: 1: No such file or directory".
If you want to try it you can use the demo ssh account at the top of the code. It is publicly available.
Edit: I realise that Expect solves this issue, but I could not find any equivalent to register_handler() in Expect. Am I right in this?
I use expect to automate ssh/sftp sessions.
Net::SSH::Perl provides shell() method for "interactive" sessions. Otherwise, this one, like its brethren, assumes that you're executing a single command on a remote server.
The simplest solution by far is Net::SSH::Expect. Below is the code. Immediately upon login I need to send a "1" in response to the prompt ("1" is the choice I want). Then I get another prompt and since I want the default value I just send \n. Then I want to read the input line by line forever. It works like a charm.
my $ssh = Net::SSH::Expect->new (
host => $host,
user => $user,
password=> $pass,
raw_pty => 1
);
my $login_output = $ssh->login();
my $prompt = $ssh->exec("1");
my $line;
$ssh->send("\n");
while ( defined ($line = $ssh->read_line()) ) {
print "$line\n";
}
Related
I am bit stuck here I want to ssh in to a machine and then run about 3 commands which are basically setup commands and then i want to return back to my machine with env variables of that machine
like
setup1
setup2
setup3
env > envtext.txt.
return back
All this i have to do in perl
i tried commands like
system("ssh #machine command1 && command 2") doesnt work
is there something like?
system("ssh #machine command1 -cmd command 2 -cmd command 3")
if not than what is the best way to do it
like making a shell script then calling it or i can do it in perl itself without any shell scripts?
code
#!/usr/bin/perl -w
use Net::SSH::Perl;
my $host = "address";
my $user = "name";
my $password = "password";
-- set up a new connection
my $ssh = Net::SSH::Perl->new($host,
debug=>0,
identity_files => ['path to key'],
options=> ["StrictHostKeyChecking no"]
#interactive => yes,
);
-- authenticate
$ssh->login($user,$password);
-- execute the command
my($stdout, $stderr, $exit) = $ssh->cmd("env");
print $stdout;`
error it gives is Permission denied at ssh.pl line 25
Thank you
I think your question is about SSHing to a single remote host and running multiple commands there. If that's true, then you need to pack your multiple commands up into a single command line that the remote shell can execute. The easiest way to do this is to use the list form of system, and pass the command line as a single parameter:
system "ssh", "machine", "setup1; setup2; setup3";
On to the second part of your question: You want to get data back from the remote side. For that, you'll want your program to read SSH's output rather than using system. For this, you can use:
open my $FH, "-|", "ssh", "machine", "setup1; setup2; setup3; env";
my #lines_from_ssh = <$FH>;
close $FH;
If you also need to send input to the remote side, look into IPC::Open2. If you need to capture both stdout and stderr, see IPC::Open3.
What you can do :
system("ssh $_ command1 -cmd command 2 -cmd command 3") for #machines;
Another Pure Perl solution is to use Net::OpenSSH
I am working on putting together a perl script. I have captured it below:
#!/usr/bin/perl
use Tie::File;
use Net::SSH::Expect;
use utf8;
use warnings;
use diagnostics;
# Grab password from hidden file
$pw=`cat .password`;
chomp $pw;
#Read list of 9200's from hosts.list file into an array
tie #hosts, 'Tie::File', "hosts.list" or die;
#Loop through hosts, connect via ssh, run commands, and write out log files.
foreach (#hosts) {
#Create ssh session handle
my $ssh = Net::SSH::Expect->new (
host => $_,
password => $pw,
user => 'user',
raw_pty => 1
);
my $login_output = $ssh->login();
if ($login_output !~ /.*sbc.*>/) {
die "Login failed. Login output was $login_output";
}
$ssh->send("show sip errors");
my $line;
while ( defined ($line = $ssh->read_line()) ){
print $line . "\n";
}
$ssh->close();
}
First, I'm not a programmer, so style is probably very ugly. Sorry about that :) The goal is to run several commands on a remote appliance, capture the results in separate files, which will then be consumed by a 3rd party parsing engine (splunk).
The current implemented functionality is able to log in to remote hosts, run the command, and then print out to stdout. Not quite there, but still shows a good proof of concept.
The script runs fine for the first 3 hosts in the hosts.list file. However as soon as it gets to the fourth host, I receive this exception:
Uncaught exception from user code:
SSHAuthenticationError Login timed out. The input stream currently has the contents bellow: user#myhost.mydomain's password: at /System/Library/Perl/Extras/5.12/Expect.pm line 828
at /Library/Perl/5.12/Net/SSH/Expect.pm line 209
Net::SSH::Expect::__ANON__('ARRAY(0x7fd718a03008)') called at /System/Library/Perl/Extras/5.12/Expect.pm line 828
Expect::_multi_expect(1, 'ARRAY(0x7fd7189fbce8)', 'ARRAY(0x7fd7189f7460)') called at /System/Library/Perl/Extras/5.12/Expect.pm line 565
Expect::expect('Expect=GLOB(0x7fd7189f1878)', 1, 'ARRAY(0x7fd718a01530)', 'ARRAY(0x7fd7189f15a8)', 'ARRAY(0x7fd71890a3d0)', 'ARRAY(0x7fd718a07470)', 'ARRAY(0x7fd7189d8b18)') called at /Library/Perl/5.12/Net/SSH/Expect.pm line 580
Net::SSH::Expect::_sec_expect('Net::SSH::Expect=HASH(0x7fd718a29828)', 1, 'ARRAY(0x7fd718a01530)', 'ARRAY(0x7fd7189f15a8)', 'ARRAY(0x7fd71890a3d0)', 'ARRAY(0x7fd718a07470)', 'ARRAY(0x7fd7189d8b18)') called at /Library/Perl/5.12/Net/SSH/Expect.pm line 213
Net::SSH::Expect::login('Net::SSH::Expect=HASH(0x7fd718a29828)') called at ./pcscfFetch.pl line 26
Any ideas on what the problem could be? I am able to log in to the host with no issue manually via ssh. The script works fine for our other hosts, it's just this one outlier that I can't seem to figure out. Any advice would be appreciated. Thanks!
I did end up resolving this. In the constructor for $ssh I set the timeout to 10 seconds, instead of the default 1. The script runs significantly slower, but I don't appear to have the issues I was running into before. Appreciate the feedback!
Net::SSH::Expect is not reliable.
Use Net::OpenSSH instead, or if you want to run the same set of commands in several hosts Net::OpenSSH::Parallel.
How can I set the remote server shell to bash through the perl telnet?
My code is below:
$telnet = Net::Telnet->new(Timeout=>90,Errmode=>'die');
$telnet->open($ipAddress);
$telnet->login($username,$password);
$telnet->waitfor('/$/');
$telnet->print("exec bash");
print "after bash";
print $telnet->cmd("ls -lrt");
print $telnet->cmd("cd $homePath");
In the above code, after the exec bash statement, none of the commands are getting executed. I need to set the remote shell as bash because some of the processes I need to run after this lines require env settings.
Please let me know how can I do the same.
Your regex to wait for the command prompt is wrong
$telnet->waitfor('/$/');
Try
$telnet->waitfor('/\$ $/');
Even better, see the first example in the Net::Telnet 3.04 doc:
my $host = 'your_destination_host_here';
my $user = 'your_username_here';
my $passwd = 'your_password_here';
my ($t, #output);
## Create a Net::Telnet object.
use Net::Telnet ();
$t = new Net::Telnet (Timeout => 10);
## Connect and login.
$t->open($host);
$t->waitfor('/login: ?$/i');
$t->print($user);
$t->waitfor('/password: ?$/i');
$t->print($passwd);
## Switch to a known shell, using a known prompt.
$t->prompt('/<xPROMPTx> $/');
$t->errmode("return");
$t->cmd("exec /usr/bin/env 'PS1=<xPROMPTx> ' /bin/sh -i")
or die "login failed to remote host $host";
$t->errmode("die");
## Now you can do cmd() to your heart's content.
#output = $t->cmd("uname -a");
print #output;
Have a perl script to connect with a Java service running on localhost, passes encrypted cookie, and returns decrypted data. When I run script from command line, it works fine. Even gave apache user a shell, and ran from command line as that user, which also works fine. If the script is run as CGI from apache, the socket new returns undef and $! is set with "permission denied". ???
Running CentOS 6.3 on this server, and IPtables are disabled.
#!/usr/bin/perl
use strict;
use CGI;
use IO::Socket;
use JSON;
my $cgi = CGI->new();
my $cookie = $cgi->cookie('attESSec') || shift (#ARGV) || undef;
my $data = JSON::false;
if($cookie){
my $socket = IO::Socket::INET->new(
'PeerHost' => '127.0.0.1',
'PeerPort' => '1500',
'Proto' => 'tcp'
);
if($socket){
$socket->send($cookie . "\r\n");
$socket->recv(my $auth,1024);
$socket->close();
chomp($auth);
if($auth){
$data = (split(/\|/,$auth))[5];
}
}
else{
$data = $!;
}
}
print($cgi->header('application/javascript'));
print(JSON->new()->allow_nonref()->utf8()->encode($data));
exit();
I found the answer. The problem was SElinux. By default it doesn't let the httpd process (or anything that spawns from it, such as CGI scripts) establish network sockets. So just had to enable that particular feature with command "setsebool -P httpd_can_network_connect 1". Now it works perfectly.
I don't know if I managed to install Net::SSH::Perl module successfully but I can't seem to be able to run the following code:
my $ssh = Net::SSH::Perl->new($remote_host);
$ssh->login($username, $password);
print "login done", "\n";
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
I am able to login but cannot print the $out. I keep getting this error:
Use of uninitialized value $out in string at test_ssh.pl line 28.
Line 28 refers to print "$out", "\n";.
I am running this code on Cygwin. What should I do now?
EDIT:
I got the following error msg when I ran my $ssh = Net::SSH::Perl->new($remote_host, options => ["Debug yes"]);:
Use of uninitialized value $out in string at test_ssh.pl line 29 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you the
name of the variable (if any) that was undefined. In some cases it cannot
do this, so it also tells you what operation you used the undefined value
in. Note, however, that perl optimizes your program and the operation
displayed in the warning may not necessarily appear literally in your
program. For example, "that $foo" is usually optimized into "that "
. $foo, and the warning will refer to the concatenation (.) operator,
even though there is no . in your program.
EDIT2:
Here's my full code
use strict;
use warnings;
use Net::SSH::Perl;
my $remote_host = '<host ip address>';
my $password = 'pass';
my $username = 'user';
my $cmd = 'copy run tftp:<my own ip address>';
warn "Starting SSH Services:...";
my $ssh = Net::SSH::Perl->new($remote_host, debug => 1);
print "done", "\n";
warn "Starting Login:...";
$ssh->login($username, $password);
print "login done", "\n";
warn "Starting command:...";
#$ssh->cmd($cmd);
#my($stdout, $stderr, $exit) = $ssh->cmd($cmd);
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
The error message on "print "$out","\n";" line:
<Computername>: channel 1: new [client-session]
<Computername>: Requesting channel_open for channel 1.
<Computername>: Entering interactive session.
<Computername>: Channel open failure: 1: reason 4:
Use of uninitialized value $out in string at test_ssh.pl line 29.
LAST EDIT: I decided to use Net::Appliance::Session to login via SSH to the network devices instead. it's a lot easier to use than Net::SSH::Perl.
Please show more of your code. What is the value of $cmd?
Note that the login method doesn't perform a login: it merely stores the username and password to be used when the connection is set up for each cmd call.
The correct way of enabling debugging messages is
my $ssh = Net::SSH::Perl->new($remote_host, debug => 1);
This will give you trace information from the cmd method which should say, amongst other things
Sending command: xxx
Entering interactive session.
and should give you some clues about what is going wrong.
Your debug output shows the problem. Looking at SSH2.h, open failure reason 4 is SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED. Your username and password are incorrect.
Net::SSH::Perl does support login via username/password, I have a working example, I just got this to work. I used the code from above and took out the Double Quotes (" ") and used single quotes (' ') instead. And "debug => 1" works for debugging the code when having issues. It will display info to you when you try to login if the debug option is set.
I am connecting to a Win32-OpenSSH SSHD server based on Windows Powershell very similar to BSDLinux SSHD server with SFTP support. Supports same Linux style based connection.
I have been trying all other SSH modules all day. Hopefully someone can use this code to just run a command and get the output if required.
You can install Net::SSH::Perl with "cpan -i module-name"
use Net::SSH::Perl;
my $host = 'testmachine.acme.local'; #Or just IP Address
my $user = 'domain\username'; #Or just username
my $pass = 'Password#123';
my $cmd = 'dir C:\\Windows\\';
use Net::SSH::Perl;
my $ssh = Net::SSH::Perl->new($host, debug => 1);
$ssh->login($user, $pass);
my ($out, $err, $exit) = $ssh->cmd($cmd);
print "$out", "\n";
Net::SSH::Perl does not support login via username/password only via interactive password entry or public key. See this post for more information.
http://www.perlmonks.org/?node_id=590452