The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Net::SSH::Any - SSH client module

SYNOPSIS

  use Net::SSH::Any;

  my $ssh = Net::SSH::Any->new($host, user => $user, password => $passwd);

  my @out = $ssh->capture(cat => "/etc/passwd");
  my ($out, $err) = $ssh->capture2("ls -l /");
  $ssh->system("foo");

  my $sftp = $ssh->sftp; # returns Net::SFTP::Foreign object
  $sftp->put($local_path, $remote_path);

DESCRIPTION

  **************************************************************
  ***                                                        ***
  *** NOTE: This is an early release that may contain bugs.  ***
  *** The API is not stable and may change between releases. ***
  ***                                                        ***
  *** Also, the module tests are quite aggresive, containing ***
  *** checks for experimental features, and may fail even if ***
  *** the module mostly works.                               ***
  ***                                                        ***
  **************************************************************

Net::SSH::Any is a SSH client module providing a high level and powerful API.

It can run remote commands and redirect its output or capture it, and perform file transfers using SCP or SFTP easily.

Net::SSH::Any does not implement the SSH protocol itself. Instead, it has a plugable architecture allowing it to delegate that task to other SSH client modules or external binaries.

BACKENDS

The backends (modules that interface with other Perl SSH client modules or external binaries) currently available are as follows:

Net_OpenSSH

Uses the perl module Net::OpenSSH which relies itself on the OpenSSH ssh binary to connect to the remote hosts. As it uses the multiplexing feature of OpenSSH, it can run several commands (or other operations) over one single SSH connection and so it is quite fast and reliable.

Using the OpenSSH client also ensures maximum interoperability and a mature an secure protocol implementation.

If you are going to run your program in a Linux/Unix box with a recent version of the OpenSSH client installed, this is probably your best option. On the other hand, Net::OpenSSH does not support Windows.

See Net::SSH::Any::Backend::Net_OpenSSH.

Net_SSH2

Uses the perl module Net::SSH2 which is a wrapper for the libssh2 C library, a fast and portable implementation of the client side of the SSH version 2 protocol.

Net::SSH2 is an actively maintained module that works on both Unix/Linux an Windows systems (don't known about VMS). Compiling it may be a hard task, specially on Windows, but PPM packages are available from the Internet.

That was intended to be main backend for Net::SSH::Any when used on Windows. Unfortunately, the current stable version of libssh2 is still somewhat buggy, causing this backend to be unreliable.

See Net::SSH::Any::Backend::Net_SSH2.

Ssh_Cmd

This backend uses any binary <c>ssh</c> client available on the box that accepts the same command line arguments as the OpenSSH one. In practice that means SSH clients forked from old versions of OpenSSH as for instance, the one bundled in Solaris and other commercial unixen.

Password authentication is only supported on Linux/UNIX and it requires the additional module IO::Pty. It may work under Cygwin too.

This backend establishes a new SSH connection for every remote command run and so it is quite slow, although reliable.

See Net::SSH::Any::Backend::Ssh_Cmd.

This backend uses the plink utility, part of the PuTTY package.

It supports password authentication, but in a somewhat insecure manner, as passwords are given to putty as a command line argument. Anybody (user or program) logged on the machine would be able to see them.

This backend also establishes a new SSH connection for every remote command run and so it is reliable but slow.

See Net::SSH::Any::Backend::Plink_Cmd.

Sexec_Cmd

This backend uses the sexec utility that is bundled with the non-free Bitwise SSH client.

This backend also establishes a new SSH connection for every remote command run and so it is reliable but slow.

See Net::SSH::Any::Backend::Sexec_Cmd.

Sshg3_Cmd

This backend uses the sshg3 utility that is bundled with the non-free Tectia SSH client.

This module supports password authentication in a secure manner and it is also quite fast as the Tectia client reuses connections.

See Net::SSH::Any::Backend::Sshg3_Cmd.

DEPENDENCIES

Depending on the backend selected and on the feature set used, you may need to install additional Perl modules.

What follows is a summary of the optional modules and when they are required:

IO::Pty

Used for password authentication with the Net_OpenSSH and Ssh_Cmd backends.

Net::OpenSSH

Used by the Net_OpenSSH backend and also when a non-POSIX shell quoter is required.

Net::SSH2

Used by the Net_SSH2 backend.

Net::SFTP::Foreign

Required for SFTP support.

Win32::SecretFile

Used by the Sshg3_Cmd backend on Windows.

API

The API of Net::SSH::Any is heavily based on that of Net::OpenSSH. Basic usage of both modules is mostly identical and it should be very easy to port scripts between the two.

Optional parameters

Almost all methods in this package accept as first argument a reference to a hash containing optional parameters. In example:

  $ssh->scp_get({recursive => 1}, $remote_src, $local_target);
  my @out = $ssh->capture({stderr_to_stdout => 1}, "ls ~/");

The hash reference can be omitted when optional parameters are not required. In example:

  $ssh->scp_get($remote_src, $local_target);
  my @out = $ssh->capture("ls ~/");

Error handling

Most methods return undef or an empty list to indicate failure. Exceptions to this rule are the constructor, which always returns and object, and those methods able to generate partial results as for instance <c>capture</c> or <c>scp_get_content</c>.

The "error" method can always be used to explicitly check for errors. For instance:

  my $out = $ssh->capture($cmd);
  $ssh->error and die "capture method failed: " . $ssh->error;

Shell quoting

By default when calling remote commands, this module tries to mimic perl system builtin in regard to argument processing.

When calling some method as <c>capture</c>:

   $out = $ssh->capture($cmd)

the given command ($cmd) is first processed by the remote shell who performs interpolation of environment variables, globs expansion, redirections, etc.

If more than one argument is passed, as in the following example:

   $out = $ssh->capture($cmd, $arg1, $arg2)

The module will escape any shell metacharacter so that, effectively, the remote call is equivalent to executing the remote command without going through a shell (the SSH protocol does not provides a way to just avoid the shell by not calling it).

All the methods that invoke a remote command (system, capture, etc.) accept the option quote_args allowing one to force or disable shell quoting.

For instance, spaces in the command path will be correctly handled in the following case:

  $ssh->system({quote_args => 1}, "/path with spaces/bin/foo");

Deactivating quoting when passing multiple arguments can also be useful, for instance:

  $ssh->system({quote_args => 0}, 'ls', '-l', "/tmp/files_*.dat");

In that case, the argument are joined with spaces interleaved.

When the glob option is set in SCP file transfer methods, an alternative quoting mechanism which leaves file wildcards unquoted is used.

Another way to selectively use quote globing or fully disable quoting for some specific arguments is to pass them as scalar references or double scalar references respectively. In practice, that means prepending them with one or two backslashes. For instance:

  # quote the last argument for globing:
  $ssh->system('ls', '-l', \'/tmp/my files/filed_*dat');

  # append a redirection to the remote command
  $ssh->system('ls', '-lR', \\'>/tmp/ls-lR.txt');

  # expand remote shell variables and glob in the same command:
  $ssh->system('tar', 'czf', \\'$HOME/out.tgz', \'/var/log/server.*.log');

The builtin quoting implementation expects a remote shell compatible with Unix sh as defined by the POSIX standard. The module can also use the shell quoters available from Net::OpenSSH when installed (that currently includes quoters for csh and MS Windows).

The remote_shell option can be used to select which one to use both at construction time or when some remote command in invoked. For instance:

  $ssh = Net::SSH::Any->new($host, remote_shell => 'csh');

  $ssh->system({remote_shell => 'MSWin'}, dir => $directory);

For unsupported shells or systems such as VMS, you will have to perform any quoting yourself:

  # for VMS
  $ssh->system('DIR/SIZE NFOO::USERS:[JSMITH.DOCS]*.TXT;0');

Timeouts

Several of the methods described below support a timeout argument that aborts the remote command when the given time lapses without any data arriving via SSH.

In order to stop some remote process when it times out, the ideal approach would be to send appropriate signals through the SSH connection , but unfortunately, this is a feature of the standard that most SSH implementations do not support.

As a less than perfect alternative solution, in order to force finishing a remote process on timeout, the module closes its stdio streams. That would deliver a SIGPIPE on the remote process next time it tries to write something.

Most backends are able to detect broken connections due to network problems by other means, as for instance, enabling SO_KEEPALIVE on the TCP socket, or using the protocol internal keep alive (currently, only supported by the Net::OpenSSH backend).

Net::SSH::Any methods

These are the methods available from the module:

$ssh = Net::SSH::Any->new($target, %opts)

This method creates a new Net::SSH::Any object representing a SSH connection to the remote machine as described by $target.

$target has to follow the pattern <c>user:password@hostname:port</c> where all parts but hostname are optional. For instance, the following constructor calls are all equivalent:

   Net::SSH::Any->new('hberlioz:f#nta$71k6@harpe.cnsmdp.fr:22');
   Net::SSH::Any->new('hberlioz@harpe.cnsmdp.fr',
                      password => 'f#nta$71k6', port => 22);
   Net::SSH::Any->new('harpe.cnsmdp.fr',
                      user => 'hberlioz', password => 'f#nta$71k6');
user => $user_name

Login name

port => $port

TCP port number where the remote server is listening.

password => $password

Password for user authentication.

key_path => $key_path

Path to file containing the private key to be used for user authentication.

Some backends (i.e. Net::SSH2), require the public key to be stored in a file of the same name with .pub appended.

passphrase => $passphrase

Passphrase to be used to unlock the private key.

batch_mode => 1

Disable any authentication method requiring user interaction.

timeout => $seconds

Default timeout.

argument_encoding => $encoding

The encoding used for the commands and arguments sent to the remote stream.

stream_encoding => $encoding

On operation interchanging data between perl and the remote commands (as opposed to operations redirecting the remote commands output to the file system) the encoding to be used.

encoding => $encoding

This option is equivalent to setting argument_encoding and stream_encoding.

remote_shell => $shell

Name of the remote shell. This argument lets the module pick the right shell quoter.

known_hosts_path => $path

Location of the known_hosts file where host keys are saved.

On Unix/Linux systems defaults to ~/.ssh/known_hosts, on Windows to %APPDATA%/libnet-ssh-any-perl/known_hosts.

strict_host_key_checking => $bool

When this flag is set, the connection to the remote host will be aborted unless the host key is already stored in the known_hosts file.

Setting this flag to zero, relaxes that condition so that remote keys are accepted unless a different key exists on the known_hosts file.

remote_*_cmd => $remote_cmd_path

Some operations (i.e. SCP operations) execute a remote command implicitly. By default the corresponding standard command without any path is invoked (i.e scp).

If any other command is preferred, it can be requested through these set of options. For instance:

   $ssh = Net::SSH::Any->new($target,
                             remote_scp_cmd => '/usr/local/bin/scp',
                             remote_tar_cmd => '/usr/local/bin/gtar');
local_*_cmd => $local_cmd_path

Similar to remote_*_cmd parameters but for local commands.

For instance:

   $ssh = Net::SSH::Any->new($target,
                             remote_ssh_cmd => '/usr/local/bin/ssh');
backends => \@preferred_backends

List of preferred backends to be tried.

backend_opts => \%backend_opts

Options specific for the backends.

$ssh->error

This method returns the error, if any, from the last method.

$ssh->system(\%opts, @cmd)

Runs a command on the remote machine redirecting the stdout and stderr streams to STDOUT and STDERR respectively.

Note than STDIN is not forwarded to the remote command.

The set of options accepted by this method is as follows:

timeout => $seconds

If there is not any network traffic over the given number of seconds, the command is aborted. See "Timeouts".

stdin_data => $data
stdin_data => \@data

The given data is sent as the remote command stdin stream.

stdout_fh => $fh

The remote stdout stream is redirected to the given file handle.

stdout_file => $filename

The remote stdout stream is saved to the given file.

stdout_discard => $bool

The remote stdout stream is discarded.

stderr_to_stdout => $bool

The remote stderr stream is mixed into the stdout stream.

stderr_fh => $fh

The remote stderr stream is redirected to the given file handle.

stderr_file => $filename

The remote stderr stream is saved on the given file.

stderr_discard => $bool

The remote stderr stream is discarded.

$output = $ssh->capture(\%opts, @cmd)
@output = $ssh->capture(\%opts, @cmd)

The given command is executed on the remote machine and the output captured and returned.

When called in list context this method returns the output split in lines.

In case of error the partial output is returned. The error method should be used to check that no error happened even when output has been returned.

The set of options accepted by this method is as follows:

timeout => $seconds

Remote command timeout.

stdin_data => $data
stdin_data => \@data

Data to be sent through the remote command stdin stream.

stderr_to_stdout => $bool

The remote stderr stream is redirected to the stdout stream (and then captured).

stderr_discard => $bool

Remote stderr is discarded.

stderr_fh => $fh

Redirect remote stderr stream to the given file handle.

stderr_file => $filename

Save the remote stderr stream to the given file.

($stdout, $stderr) = $ssh->capture2(\%opts, @cmd)

Captures both the stdout and stderr streams from the remote command and returns them.

timeout => $seconds

Command is aborted after the given numbers of seconds with no activity elapse.

stdin_data => $data
stdin_data => \@data

Sends the given data through the stdin stream of the remote process.

Example:

    $ssh->system({stdin_data => \@data}, "cat >/tmp/foo")
        or die "unable to write file: " . $ssh->error;
$pipe = $ssh->pipe(\%opts, @cmd)

Returns a bidirectional file handle object (that may be a real operating system file handle or an emulated tied file handle, depending on the used backend), connected to the remote command stdin and stdout streams.

The returned pipe objects provide most of the API of IO::Handle.

stderr_to_stdout => $bool

Redirects the stderr stream of the remote process into its stdout stream.

stderr_discard => $bool

Discards the stderr stream of the remote process.

$ssh->scp_get(\%opts, @srcs, $target)

Copies the given files from the remote host using scp.

The accepted set of options are as follow:

glob => $bool

Allows the remote shell to expand wildcards when selecting the files to download.

recursive => $bool

When this flag is set, the module will descend into directories and retrieve them recursively.

copy_attr => $bool

When this flag is set the attributes of the local files (permissions and timestamps) are copied from the remote ones.

copy_perm => $bool
copy_time => $bool

Selectively copy the permissions or the timestamps.

update => $bool

If the target file already exists locally, it is only copied when the timestamp of the remote version is newer. If the file doesn't exist locally, it is unconditionally copied.

numbered => $bool

When for some remote file a local file of the same name already exists at its destination, a increasing suffix is added just before any extension.

For instance, foo may become foo(1), foo(2), etc.; foo.txt may become foo(1).txt, foo(2).txt, etc.

overwrite => $bool

When a local file of the same name already exist, overwrite it. Set by default.

$ssh->scp_put(\%opts, @srcs, $target)

Copies the set of given files to the remote host.

The accepted options are as follows:

glob => $bool

Allows wildcard expansion when selecting the files to copy.

recursive => $bool

Recursively descend into directories.

copy_attr => $bool

Copy permission and time attributes from the local files.

Symbolic links are not supported by SCP. By default, when a symbolic link is found, the method just copies the file pointed by the link.

If this flag is unset symbolic links are skipped.

$data = $ssh->scp_get_content(\%opts, @srcs)

Retrieves the contents of some file or files via SCP.

glob => $bool

Allows wildcard expansion on the remote host when selecting the files to transfer.

recursive => $bool

Recursively descends into directories

$ssh->scp_put_content(\%opts, $target, $content)

Creates or overwrites the remote file $target with the data given in $content.

perm => $perm

The permissions for the new remote file. Defaults to 0666.

atime => $atime
mtime => $mtime

Sets the atime and mtime properties of the remote file.

$ssh->scp_mkdir(\%opts, $dir)

Creates a directory using SCP.

perm => $perm

Sets the permissions of the remote directories created. Defaults to 0777.

atime => $atime
mtime => $mtime

Sets the atime and mtime properties of the remote directories.

$sftp = $ssh->sftp(%opts);

Returns a new Net::SFTP::Foreign object connected to the remote system or undef in case of failure.

fs_encoding => $encoding
timeout => $seconds
%data = $ssh->autodetect(@tests)

Calls Net::SSH::Any::Autodetect, which implements tests and heuristics that allow one to discover several properties about the remote machine as for instance its operating system or the user shell.

That module is still highly experimental and the way it is used or the format of the returned data may change in future releases.

FAQ

Frequent questions about this module:

Disabling host key checking

Query: How can host key checking be completely disabled?

Answer: You don't want to do that, disabling host key checking breaks SSH security model. You will be exposed to man-in-the-middle attacks, and anything transferred over the SSH connection may be captured by a third party, including passwords if you are also using password authentication.

Q: I don't mind about security, can I disable host key checking?

A: You have been warned...

The way to disable host key checking is to unset the strict_host_key_checking flag and point known_hosts to /dev/null or your preferred OS equivalent.

In example:

  my $ssh = Net::SSH::Any->new($host,
                               strict_host_key_checking => 0,
                               known_hosts_path => ($^O =~ /^Win/
                                                    ? 'nul'
                                                    : '/dev/null'));

I am not making that easier on purpose!

known_hosts file

Q: How can I manipulate the known_hosts file. I.e, adding and removing entries?

A: If you have a recent version of OpenSSH installed on your machine, the companion utility ssh-keygen(1) provides a relatively easy to use command line interface to such file.

Otherwise, you can just add or remove the entries manually using a text editor.

If you are on Linux/Unix and using the default known_hosts file, an easy way to add some host key to it is to just log once manually from the command line using your system ssh command. It will get the key from the remote host and ask you if you want to add the key to the store.

Later versions of Net::SSH2 provide basic support for known_hosts file manipulation in Net::SSH2::KnownHosts.

More questions

See also the FAQ from the "FAQ" in Net::OpenSSH module as most of the entries there are generic.

SEE ALSO

Net::OpenSSH, Net::SSH2, Net::SSH::Perl.

Net::SFTP::Foreign

BUGS AND SUPPORT

To report bugs send an email to the address that appear below or use the CPAN bug tracking system at http://rt.cpan.org.

Post questions related to how to use the module in Perlmonks http://perlmoks.org/, you will probably get faster responses than if you address me directly and I visit Perlmonks quite often, so I will see your question anyway.

The source code of this module is hosted at GitHub: http://github.com/salva/p5-Net-SSH-Any.

Commercial support

Commercial support, professional services and custom software development around this module are available through my current company. Drop me an email with a rough description of your requirements and we will get back to you ASAP.

My wishlist

If you like this module and you're feeling generous, take a look at my Amazon Wish List: http://amzn.com/w/1WU1P6IR5QZ42.

Also consider contributing to the OpenSSH project this module builds upon: http://www.openssh.org/donations.html.

TODO

Thinks that I would like to add in this module in the future:

  • Host key checking policies

    I.e. strict, tofu, ask, advisory.

  • Install client software automatically

    Add infrastructure to download, maybe compile and install client software from the internet. This will be used to test the module in automating testing environments as CPAN Testers or Travis CI.

  • Expect like functionality

    A subset of Expect adapted to work on top of Net::SSH::Any.

  • Gateway support

    I am still not sure about how viable it would be, but I would like to get something like Net::OpenSSH::Gateway available for Net::SSH::Any.

  • Move to Moo+Moo::Role or Role::Tiny

    The ad-hoc composition model used internally by Net::SSH::Any has several quirks that would be gone using the dynamic inheritance model provided by Role::Tiny, though that would probably be a huge effort.

COPYRIGHT AND LICENSE

Copyright (C) 2011-2016 by Salvador Fandiño, <sfandino@yahoo.com>

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.12.4 or, at your option, any later version of Perl 5 you may have available.