NAME

Fugu::SSH - run a command on another machine over SSH

SYNOPSIS

use Fugu::SSH;

my $ssh = Fugu::SSH->new(
    host => '127.0.0.1',
    port => 2222,
    user => 'root',
);

$ssh->wait_available(120) or die "the host never answered\n";

my $result = $ssh->run_command('uname -a');
print $result->{stdout};

DESCRIPTION

Fugu::SSH wraps Net::SSH2 for the things a provisioning tool does: run a command and capture its output, write a file, and read a file back. An interactive session falls back to ssh(1), because Net::SSH2 does not give correct control of a terminal.

Net::SSH2 loads at connect time and not at compile time. Thus the module keeps the core-Perl load contract of Fugu, and an installation without the library still loads it and fails with a clear message at the first connect.

Authentication tries the SSH agent first, when SSH_AUTH_SOCK is set, and falls back to the password when the caller gave one.

new

new(%args) creates a connection object. The method opens nothing: every other method connects for itself and disconnects when it is done.

These are the arguments:

host

The host to reach. The default is 127.0.0.1.

port

The port. The default is 22.

user

The user to log in as. The default is root.

password

The password, for a host that has no key yet.

timeout

The connection timeout in seconds. The default is 10.

known_hosts

The path of the known_hosts file that the strict mode reads. The default is absent: each back end then reads ~/.ssh/known_hosts, which is its own default. The value has no effect when strict is false, and the module never writes the file.

strict

Verify the host key of every connection. The default is 0.

The policy belongs to the object, and no method takes a per-call override, because one object reaches one host. With strict set, every connecting method verifies the key against the known_hosts file before it authenticates, and interactive runs ssh(1) with StrictHostKeyChecking=yes. The name selects LIBSSH2_HOSTKEY_POLICY_STRICT for the library and StrictHostKeyChecking=yes for ssh(1).

The strict mode needs Net::SSH2 0.60 or later, which fixed the argument order of check_hostkey.

run_command

run_command($command) runs one command and returns a hash reference with stdout, stderr and exit_code. A connection that fails reports exit code 1 and a message in stderr.

write_file

write_file($remote_path, $content, $mode) writes a file over SFTP. The default mode is 0644.

One SFTP write can accept less than the full buffer, so the method loops until every byte is accepted, and it reports a write that makes no progress as a failure. A provisioning script that arrives half-written is worse than one that never arrived.

read_file

read_file($remote_path, $max_size) reads a remote file over SFTP and returns its bytes. The default for $max_size is MAX_READ_SIZE, 64 MiB.

The method reads the size of the file first, and it refuses a size above $max_size before it reads one byte. It then compares the byte count with that size, and it reports a short read as a failure. A file that arrives half-read is worse than one that never arrived, because the caller cannot see the difference.

The method returns undef for every failure: a connect that fails, an SFTP session that fails, a stat that fails, a size above $max_size, an open that fails, and a byte count that differs from the size. It reports each reason on the debug level of Fugu::Log.

An empty remote file returns the empty string, which is false. Test defined on the result, and do not test truth.

is_available

is_available() reports if the host takes an authenticated connection now.

wait_available

wait_available($timeout) polls until the host takes a connection, or until the timeout ends. The default timeout is 120 seconds.

The wait stops early when a signal arrives, through Fugu::Timeout. A poll loop must not outlive the interrupt that told the program to stop.

interactive

interactive() runs ssh(1) with the terminal of the caller and returns its exit code.

The code is a value between 0 and 255, and not a raw wait status. A caller that passed the raw status to exit would turn a remote exit code of 1 into exit(256), which the kernel truncates to 0, and a failed remote command would read as a success.

The method carries the host-key policy as ssh(1) options. In the strict mode it sets StrictHostKeyChecking=yes, and it names the known_hosts file with UserKnownHostsFile when the caller gave one. The method does not die for a host key that does not verify: ssh(1) refuses the session itself and returns 255. In the permissive mode the method sets StrictHostKeyChecking=no, sends the known hosts to /dev/null, and lowers the log level to hide the new-key report.

One difference between the back ends stays. ssh(1) also reads /etc/ssh/ssh_known_hosts, and the library reads the named file only. The method does not set GlobalKnownHostsFile: an entry that an administrator wrote there is a local trust decision.

RETURN VALUES

run_command() returns a hash reference.

The two transfer methods report a result in two shapes. write_file() returns 0 on success and 1 on failure, like a command. read_file() returns the bytes on success and undef on failure, like Fugu::File->read.

is_available() and wait_available() return 1 or 0. interactive() returns an exit code.

ERRORS

Every method dies when Net::SSH2 is not installed. The message names the module, because that failure is an installation problem and not a connection problem. In the strict mode, every connecting method dies when Net::SSH2 is older than 0.60, and the message names the version.

No method dies for a connection that fails. One exception exists: in the strict mode, a host key that does not verify makes the connecting method die. The one-line message names the host, the port, the file, and the reason of the back end, which tells a wrong key from a file that holds no key for the host. A wrong key is permanent, a retry cannot repair it, and a caller that asked for verification must not lose the answer to a missing return-value test. interactive() is the other shape of the same fault: it does not die, and ssh(1) returns 255.

SEE ALSO

ssh(1), Fugu::Log, Fugu::Process, Fugu::Timeout, Net::SSH2

AUTHORS

Dick Olsson <hi@senzilla.io>

CAVEATS

Every method opens its own connection and closes it at the end. A caller that runs many commands pays the handshake for each one.

run_command() holds the whole output of the command in memory, and read_file() holds the whole file. The MAX_READ_SIZE cap of read_file() bounds the second one at 64 MiB.

The module verifies the host key only in the strict mode, through the strict argument of new. The default verifies nothing. The module is a provisioning tool for a machine that the caller just created, on a port on the loopback address, and such a machine holds a new key after each install. Set strict to reach a host across a network you do not trust.