perl ssh then read file on remote server - perl

I need to
connect to a remote server; then
do some things, like open and read the contents of a file.
For step 1:
my $server = "remoteservername.company.com";
my $ssh = Net::SSH::Perl->new("$server", debug => 1, protocol => 2, StrictHostKeyChecking => "no") or die "Error connecting server $server";
yields msg on terminal
Connection established.
so I presume I am ssh connected to remote server, via the code.
For step 2, how do i open and read a file on the remote server using code from the local server? this is the best i can do so far:
use strict;
use warnings;
use diagnostics;
use warnings::register;
use Net::SSH::Perl;
use Net::SSH::Expect;
use Math::BigInt lib => "Calc,GMP,Pari";
my $server = "server09";
my $ssh = Net::SSH::Perl->new("$server", debug => 1, protocol => 2, StrictHostKeyChecking => "no") or die "Error connecting server $server";
#open(FILE, "/home/myid/f09.txt") || print("Unable to open test.o\n"); #works, on local, opens file[does not fail].
#open(FILE, "server09://home/myid/f09.txt") || print("Unable to open test.o\n"); #---> error: "Unable to open test.o"
my #remote_text = `this text is put into array.`;
my $remote_text = join ('',#remote_text);
open (FILE,'>/home/myid/f09.txt');
print FILE "$remote_text";
close (FILE);
exit(0);
yet, it does not add anything to existing file f09.txt; also if i delete the file, the open does not create it. no errors, but this does not seem to contact the remote file.
just a simple explanation of ssh, then read from remote file would be helpful. other examples i see aren't cutting it. of course, could be me, long day, gotta walk away from it for a while. your time is very much appreciated!

You are essentially trying to modify a file that exists on another machine over SSH.
File operations functions cannot handle that.
Is it possible for you to download the file from the other server, update on your local and re-upload the file?
You may also want to experiment with the ssh command:
my #remote_text = ('this text is put into array.');
my $remote_text = join ('',#remote_text);
my #args = ("ssh server09", "echo '$remote_text' > /home/myid/f09.txt");
system(#args) == 0 or die "system #args failed: $?"

For performing interesting things over an SSH connection to another machine, you might like to try IPC::PerlSSH. From one of its examples:
use IPC::PerlSSH;
my $ips = IPC::PerlSSH->new( Host => "over.there" );
$ips->use_library( "FS", qw( mkdir chmod writefile ) );
$ips->call( "mkdir", "/tmp/testing" );
$ips->call( "chmod", 0600, "/tmp/testing" );
$ips->call( "writefile", "/tmp/testing/secret", <<EOF );
Some secret contents of my file here
EOF

Related

Perl SSH to remote server and check file exists

I have developed a script which should login into remote server and do certain operation.
In remote server it will check for sample_file.txt file exists or not, if YES then it should create create_user_file.txt file in home path with certain set of data.
The thing here is I am able to do ssh to remote machine though below script but unable to check for the files since I don't have idea how we can open the remote session and do all the operation.
Below is my code:
#!/usr/bin/perl
use Net::OpenSSH;
.
.
.
foreach my $ip( #list_of_ips){
print "Connecting to $ip...\n";
my $ssh = OpenSSH_Connection( $ip, $user, $passwd );
print "Connected!\n";
$dir = "/remote/server/home/path/";
$file = $dir."sample_file.txt";
if (-e $file){ #if sample_file.txt exists then create user file
open(my $fh, '>', $dir."create_user_file.txt") || die "Couldn't open file $!";;
print $fh "ID=123&PW=Passw0rd";
close $fh;
}
last if($ssh); #if connection is success no need to login into another server
$ssh->close();
}
sub OpenSSH_Connection {
my ( $host, $user, $passwd ) = #_;
my $ssh = Net::OpenSSH->new("$user:$passwd\#$host");
$ssh->error and die "Couldn't establish SSH connection: ". $ssh->error;
return $ssh;
}
Here in the code, below part of code should be executed in the remote server. But its been checking for a file from where I am executing this script.
if (-e $file){ #if sample_file.txt exists then create user file
open(my $fh, '>', $dir."create_user_file.txt") || die "Couldn't open file $!";;
print $fh "ID=123&PW=Passw0rd";
close $fh;
}
How can I keep open the remote session of the server and execute above code, or do we have any Perl module which could do this kind of operation from local server. TIA.
Your assumption that Net::SSH will make remote resources available locally is incorrect.
SSH creates encrypted channel to remote system and drops you either into shell, or runs remote command (for example perl/shell script), or allows to copy/retrieve a file.
You can open SSH channel into remote shell and run commands as on local computer, but if you want to check if file exists on remote system then you have to communicate it through remote shell or perl script.
Probably what you are after is SSH FS which may make remote files available locally.
I must make note that users on local and remote system can have different id on system level and it might be not what you want (manipulate files of different user). This approach may work well if your local and remote id is the same and belongs to you.
I guess that most easy way would be write perl script on remote system and pass some arguments to it. The script on remote system will do rest part of the job.
In such situation you could use Net::SSH2 to establish connection into remote shell and then run remote perl script with parameters.
To get a better idea how it works without creating long post I refer you to PerlMonks Net::SSH2 demo.
Other more difficult way would be writing enough perl code to establish connection with remote system over SSH2 protocol to remote perl script with capability to 'translate' your local commands
$chan->command($argument)
into remote perl code blocks to perform particular task.
Net::SSH2

Perl sFTP : how to check remote file doesnt exist

I am 1 day old to Perl, was going through API doc here, have few basic questions
$sftp = Net::SFTP::Foreign->new($host, autodie => 1);
my $ls = $sftp->ls("/bar");
# dies as: "Couldn't open remote dir '/bar': No such file"
Question
with autodie will the connection be auto closed ?
we see in above example how to use folder , similar syntax also works for file ?
Or something like this makes more sense ??
my $sftp = Net::SFTP::Foreign->new($host, autodie => 1);
$sftp->find("/sdfjkalshfl", # nonexistent directory
on_error => sub { print "foo!\n";sftp->disconnect();exit; });
I was trying to run following code on my windows machine
use Net::SFTP::Foreign;
my $host = "demo.wftpserver.com";
my $sftp = Net::SFTP::Foreign->new($host ,ssh_cmd => 'plink',autodie => 1);
my $ls = $sftp->ls("/bar");
But i get error
'plink' is not recognized as an internal or external command ,
however when i run plink from windows command line it works fine !!
with autodie will the connection be auto closed ?
Yes. When the program ends, everything is destroyed and connections are closed. That is also the case when the $sftp variable goes out of scope. Modules like this usually implement a DESTROY sub. Those are invoked when the object (which is just a reference in Perl) goes out of scope. There can be some cleanup in that sub. Another example that has that is DBI, and of course lexical filehandles (like $fh from a open call).
we see in above example how to use folder , similar syntax also works for file ?
No. The docs say ls is for a directory:
Fetches a listing of the remote directory $remote. If $remote is not given, the current remote working directory is listed.
But you can just do ls for the directory that the file you want is in, and use the wanted option.
my $ls = $sftp->ls( '/home/foo', wanted => qr/^filename.txt$/ );
Though with the autodie that should die, so if you don't want it to actually die here, you should wrap it in a Try::Tiny call or an eval.
use Try::Tiny
# ...
my $ls = try {
return $sftp->ls( '/home/foo', wanted => qr/^filename.txt$/ );
} catch {
return; # will return undef
};
say 'Found file "filename.txt" on remote server' if $ls;
As to plink being not found, probably the Windows PATH is different from what your Perl sees.

Display Output In Browser Perl CGI SSH

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.

How to access file on a remote server and get the file data as return value in perl?

I want to write a perl script which will read a file on a remote server, alter some parameters inside the file and get whole file data or specific data as a return value.
Actually what I was thinking is to use cpan openssh utility to create a ssh connection, then call the script on the server side, which does all the reading and changing the parameters value. But the problem in this approach is that I will get only success or failure as a return value - not the file data.
Can anyone please tell me how I will achieve this functionality?
Thanks in advance....
Here is one way to do what you are asking with Net::SSH::Expect
use Net::SSH::Expect;
my $ssh = Net::SSH::Expect->new (
host => "some.example.net",
password=> 'your-password',
user => 'you',
raw_pty => 1
);
my $login_output = $ssh->login();
if ($login_output !~ /Welcome/) {
die "Login has failed. Login output was $login_output";
}
$ssh->exec("perl -i -pe 'tr/9/a/;' temp.txt");
my $cat=$ssh->exec("cat temp.txt");
print($cat);
The file on the remote server (temp.txt) contains just one line 0123456789
and running the script has this output
$ perl /tmp/a.pl
012345678a
The main point here is that with this module I can log in to a remote machine and execute commands as I would with any other ssh session.

Whilst using Perl::FTP, What shall I put in the host field for connecting to a Local computer connected on the network

I am using the Net::FTP in perl to do some file transfers. when I run the following code :-
Are there any issues with using the IP address ? Am I correct in providing this in the host field ?
use strict;
use Net::FTP;
my $host = "10.77.69.124";
my $user = "administrator";
my $password = "Password";
my $f = Net::FTP->new($host, Debug =>0) or die "Can't open $host\n";
$f->login($user, $password) or die "Can't log $user in\n";
The code is not able to connect to the remote host. Why is this happening ? Shouldn't this work with the IP address provided in the $host ?
The constructor of Net::FTP allows you to pass a single scalar value or an array of hosts to try. The value of this field should be the same as the PeerAddr from IO::Socket::INET (either a hostname or an ip address).
Have a closer look at what is happening by specifying Debug. If you are behind a firewall or a NAT setup, you should probably also set Passive to a non-zero value and make sure to check if the constructor failed by printing out $#.
my $ftp = Net::FTP->new(Host=>$host, Debug=>1, Passive=>1) || die $#;
If the constructor succeeded, you might want to check if any of the other methods fail:
$ftp->login($user, $pass) || die $ftp->message;
$ftp->cwd($path) || die $ftp->message;
By the way: If you are unsure if you've used the correct host parameter, you can ask Net::FTP which host it tried to connect to:
print $ftp->host, "\n";
If this still doesn't work, please provide a detailed output of your application.
Hope this helps.
First be sure that you can reach the remote side:
From command line use telnet (available on linux and windows too, a it different in syntax)
telnet host 21
If you are not able to connect the from commandline, check for firewall rules or maybe your FTP server running on different port?
If you are able to connect try out login with plain FTP commands:
USER user#remote.host
PASS yourpassword
This will use ACTIVE ftp connection to the remote. This is the old way.
Nowadays most ftp server use PASSIVE ftp. To test try this command out (from linux commandline)
ftp -v -p host
In perl you could use passive mode this way:
my $f = Net::FTP->new($host, Debug =>1, Passive => 1) or die "Can't open $host\n";
I hope this will help you.