Display Output In Browser Perl CGI SSH - perl

I'm executing remote commands using Net::OpenSSH using a web frontend. My commands return without failure on the command line, but I get nothing in a web browser. I've done a couple hour research to no avail--any ideas?
Here is some code to give you an example (some removed for obvious reasons).
#!/usr/bin/perl -w
use strict;
use CGI ':standard';
use Net::OpenSSH;
# Here in the code is just the header and standard tags
print "1";
print "2"; # both display
my $ssh = Net::OpenSSH->new($host, user => $uname, key_path => $key); # all works
$ssh- error and die "Can't ssh to host" . $ssh->error;
print "3";
$ssh->system("uname -a") or
die "remote command failed: " . $ssh->error;
my #lsa = $ssh->capture("ls -a");
$ssh->error and
die "remote ls command failed: ". $ssh->error;
print "4";
print "5";
print #lsa; # won't display in browser, just terminal/CLI
Cheers!

I maintain CGI.pm. I recommend these additions to your simple script:
Before you print anything else, print the standard HTTP header: print header();
Add this after the use CGI line: use CGI::Carp qw(fatalsToBrowser); ... that will display any run-time problems in the browser. If you don't get any output after these changes, check that the script compiles with perl -cw script.pl

Below is about the minimum Perl code that worked for me on Debian machine. I suggest you go through it and compare it to your actual code.
However, it did not work out-of-the box on my Debian, I had make some decisions most of which probably aren't very safe, but that's more about specific environment:
make home for user that server runs writable (/var/www)
add host to ~/.ssh/known_hosts beforehand
use the strict_mode => 0 to bypass Net::OpenSSH's security checks instead of finding proper
ctl_dir (Net::OpenSSH requires that the folder and all above folders are 0755 or more strict,
so /tmp I used is normally not good enough)
I believe there are much safer practices than that, but as I said, that's specific to environment.
So the code:
#!/usr/bin/perl
use strict;
use warnings;
use Net::OpenSSH;
use File::Temp qw/ tempdir /;
# necessary minimum for CGI
print "Content-type: text/plain\n\n";
# prepare temp dir
my $temp = tempdir("/tmp/sshme.pl-XXXXXXXX", CLEANUP => 1);
# open SSH session
my %opts = (
user => "user",
password => "password",
ctl_dir => $temp,
strict_mode => 0 ## NOT recommended - see my comments
);
my $ssh = Net::OpenSSH->new("host", %opts);
$ssh->error
and die "Couldn't establish SSH connection: ". $ssh->error;
# perform command and print output
my #lines = $ssh->capture("ls")
or die "remote command failed: " . $ssh->error;
print #lines;

Perhaps your errors get directed to standard error, not standard output. In that case, they'll usually end up in the server log, not the browser window. Perhaps you can use POSIX::dup2 to avoid this:
use POSIX;
# Make sure to send HTTP headers before redirecting streams!
POSIX::close(2); # close original stderr stream. No more output to apache logs!!!
POSIX::dup2(1,2); # redirect error stream to standard output, so errors will appear in browser.
Processes launched by perl, like e.g. some ssh binary, will inherit these streams.

Related

Perl Net::SSH2 scp_put returns unknown error

I am using Net:SSH2 to put file on a remote server with scp_put.
It returns unknown error:
-43, LIBSSH2_ERROR_UNKNOWN(-43), SCP failure
It seems that the error comes after some timeout/delay, as it takes several minutes to return.
Connection to sftp-server is working. I can get a directory list from the directory.
I have access rights to that directory as I can put files there with SFTP-client.
I am using Strawberry Perl in Windows environment.
use warnings;
use strict;
use Net::SSH2;
my $dir1 = '.';
my $file = 'D:\\test\\test.txt';
my $ssh2 = Net::SSH2->new();
$ssh2->connect('testserver') or die "Unable to connect Host $# \n";
$ssh2->auth_password('test','test') or die "Unable to login $# \n";
if($ssh2->scp_put($file, $dir1)) {
print "File $file transferred to $dir1\n";
} else {
print "Couldn't transfer file $file to $dir1\n";
print join ', ', $ssh2->error;
print "\n";
}
SCP support in libssh2 is quite rudimentary and buggy.
Better alternatives are Net::SSH::Any which has a proper pure-perl implementation of SCP or Net::SFTP::Foreign for SFTP. Both can work on top of Net::SSH2.

Perl CGI To Write File

I have written a Java Applet as a school project and I need a CGI file to create a file in the cgi-bin directory. The problem is when I run the code from the browser, the code executes but my file is not created with the variable name. Nothing is created. Here is the code
#!/usr/bin/perl -wT
use CGI;
print "content-type: text/plain\n\n";
my $q = CGI->new();
my $name = $q->param('username');
my $pw = $q->param('param');
my $bool = $q->param('bool');
my $rel = $q->param('rel');
my $ext = ".txt";
my $strt = "../cgi-bin/";
my $app = $strt . $name . $ext;
print $app;
open (FILE,'>',$app) or print "Error";
print FILE $pw . "\n";
print FILE $bool . "\n";
print FILE $rel;
close(FILE);
When I run the cgi it prints the $app variable and it is the correct address I want but the file is not created. If I change the line
open (FILE,'>',$app) or print "Error";
to
open (FILE,'>','../cgi-bin/test.txt') or print "Error";
it creates the file where I want it. Any ideas why this would happen when using the variable $app? Either way I never get Error printed to the browser.
SOLUTION:
Thanks guys for the help. When using:
use CGI::Carp qw(fatalsToBrowser);
I got this error:
Content-type: text/html
<H1>Software error:</H1>
<PRE>Insecure dependency in open while running with -T switch
</PRE>
<P>
For help, please send mail to the webmaster (or webmaster), giving this error message
and the time and date of the error.
It seems it was not liking the -T. Once I removed that it worked. Thanks again
Why you use ../cgi-bin to write into cgi-bin ?
Just use:
open (FILE, ">$name$ext") or die $!;
and use CGI::Carp qw(fatalsToBrowser); to carp fatals on the browser (suitable for this debug) with file creation
-T is Perl's "tainted data" flag. It stops you from doing unsafe operations with untrusted data. Yes, your script works without the -T flag, but now you have a very insecure script.
If someone passes in a username value of ../../../../../../../../home/badguy/secret, then you will write the username and password into secret.txt in badguy's home directory. -T prevents you from doing that. That's why -T exists.

Perl SSH Script unable to load math library?

I am trying to run a Perl script that takes user, pass, ip arguments and uses that to check the version of a network switch through ssh. However when i run it on our server i keep getting:
Math::BigInt: couldn't load specified math lib(s), fallback to Math::BigInt::FastCalc at /usr/lib/perl5/site_perl/5.10.0/Crypt/DH.pm line 6
It the proceeds to hang for a bit then return with no output. What is causing this to fail and how can i get around it? I do not have access to install extra modules to the server.
EDIT: I have checked the currently installed modules and Net::SSH:Perl, Math::BigInt::FastCalc, and Math::Pari are all installed, so i have no idea why it is having problems loading those modules.
Here is my script for reference:
#!/usr/bin/perl
# Outputs the name of the Flash File (which denotes the software version) of the switch
#Input: User Pass Host
open(FH, ">>unparsed.txt");
open (FH2, ">>versions.txt");
use strict;
use Net::SSH::Perl;
my $user = $ARGV[0];
my $pass = $ARGV[1];
my $host = $ARGV[2]; #Hostname given as command line argument
my $cmd = "show version";
my $version;
print("\n");
print($ARGV[2]);
print("\n");
my $ssh = Net::SSH::Perl->new($host);
$ssh->login($user, $pass); # login to switch
my($stdout, $stderr, $exit) = $ssh->cmd($cmd);
printf FH ($stdout); #output all test to file
close(FH);
open(FH, "unparsed.txt");
while(<FH>){ #look through file for flash filename
if($_ =~ /System image file is "(.*)"/){
$version = $1;
}
}
print ($version); #output flash filename
print ("\n");
printf FH2 ($ARGV[2]);
printf FH2 ("\n");
printf FH2 ($version);
printf FH2 ("\n");
close(FH2);
close(FH);
Crypt::DH loads Math::BigInt with:
use Math::BigInt lib => "GMP,Pari";
Therefore, you need either GMP or Pari on your system.
Your distribution's package manager may already provide a means to install them.
Have you tried using Net::SSH?
I prefer Net::SSH, and Net::SFTP::Foreign, math libs usally give me troubles when I try to install them on older systems (specially with the mess that some sysadmin do with paths on Unix systems). Most systems either use OpenSSH, or some fork of it (like SunSSH on Solaris), so it's less likely that you you'll have any sort of trouble using those distributions.
Try using Net::OpenSSH instead of Net::SSH::Perl.

How to run SSH commands using Net::SSH::Perl?

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

Perl - How to send a binary (image) file to a remote host from "master" host and create directory structure too?

I have done a bunch of reading and some testing to no avail.
I found this script here on stackoverflow which guided me in the right direction but, my abilities are inadequate to modify and fully understand for my needs.
#! /usr/bin/perl
use warnings;
use strict;
use File::Basename;
use File::Copy;
use File::Spec::Functions qw/ abs2rel catfile /;
use Net::SSH qw/ sshopen3 /;
my $HOST = "user\#host.com";
my $SRC_BASE = "/tmp/host";
my $SRC_FILE = "$SRC_BASE/a/b/c/file";
my $DST_BASE = "/tmp/dest";
system("md5sum", $SRC_FILE) == 0 or exit 1;
my $dst_file = catfile $DST_BASE, abs2rel $SRC_FILE, $SRC_BASE;
my $dst_dir = dirname $dst_file;
sshopen3 $HOST, *SEND, *RECV, *ERRORS,
"mkdir -p $dst_dir && cat >$dst_file && md5sum $dst_file"
or die "$0: ssh: $!";
binmode SEND;
copy $SRC_FILE, \*SEND or die "$0: copy failed: $!";
close SEND or warn "$0: close: $!"; # later reads hang without this
undef $/;
my $errors = <ERRORS>;
warn $errors if $errors =~ /\S/;
close ERRORS or warn "$0: close: $!";
print <RECV>;
close RECV or warn "$0: close: $!";
Scenario:
I have image files in a directory on the "main" host eg /home/user/public_html/images/red/id100/image01.jpg (thru image012.jpg)
I would like to copy/send them to my remote host creating the red/id100 path if !-d. (the "/images" folder pre exists.)
I need to send a username and password to the remote host as well to get in. It is a typical cpanel shared server environment.
My main server is dedicated, also with cpanel management installed.
I looked into NET::FTP, File::Remote, Net::Telnet and Net::SFTP but, no examples available that were "dumbed" down to my level.
I will eventually need to do this with ascii files too but, I believe once I get it working with the images, I can figure out xfermode switch, I hope.
Thanks for any help and verbose examples. I always learn great things here.
Using SFTP it should be pretty simple:
use Net::SFTP::Foreign;
my $sftp = Net::SFTP::Foreign->new($HOST, passwd => $PASSWD);
$sftp->error and die "Unable to connect to remote host: " . $sftp->error;
$sftp->rput($SRC_BASE, $DST_BASE);
...or using ssh+rsync...
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($HOST, passwd => $PASSWD);
$ssh->error and die "Unable to connect to remote host: " . $ssh->error;
$ssh->rsync_put({recursive => 1}, $SRC_BASE, $DST_BASE)
or die "rsync failed: " . $ssh->error;