Fragmented Thought

Using PHP to SSH

By

Published:

Lance Gliser

Heads up! This content is more than six months old. Take some time to verify everything still works as expected.

I needed the ability to fire off some ssh commands today testing out how to do sftp through php for a client. Turns out there's a bit of a layering process here. This should get you started, and going on from here will require a bit more customization. It's important to note that many sftp services do not allow you shell access. Just because you have an ssh connection, does not mean you can issue commands. And just because you cannot issue commands, does not mean you can't do scp or use regular sftp commands. It's a bit of a bother.

Start by getting the libraries you need. On a debian Linux system, the install commands below worked for me. Be sure to use the current version of the pecl ssh library.

sudo apt-get install libssh2-1-dev libssh2-php sudo pecl install channel://pecl.php.net/ssh2-0.12

On Macs, you can try using ports:

sudo port install php5-ssh2

After that, you just need a class do a bit of the logic for you:

class FileRetriever { private $connection; public $connected; /** * Constructor */ public function __construct(){ $this->connected = false; } /** * Creates a connection to the ssh server * @param array $parameters */ public function connect($parameters){ // Merge in connection defaults $parameters += array( 'port' => 22, ); try { $this->connection = ssh2_connect($parameters['host'], $parameters['port']); if( !$this->connection ){ return false; } if( !ssh2_auth_password($this->connection, $parameters['user'], $parameters['password']) ){ return false; } $this->connected = true; return true; } catch(Exception $e) { return false; } } /** * Downloads a file * @param string $source * @param string $destination * @return mixed string destination or false */ public function scp($source, $destination){ if( !$this->connected ){ return false; } try { if( ssh2_scp_recv($this->connection, $source, $destination) ){ return $destination; } } catch (Exception $e) {} return false; } /** * Sends a command through the ssh connection * @param string */ public function execute($command){ if( !$this->connected ){ return false; } try { if (!($stream = ssh2_exec($this->connection, $command))) { throw new Exception('SSH command failed'); } // This affects calls like fgets() and fread() that read from the stream. // In non-blocking mode an fgets() call will always return right away while // in blocking mode it will wait for data to become available on the stream. stream_set_blocking($stream, true); $data = ""; while ($buffer = fread($stream, 4096)) { $data .= $buffer; } fclose($stream); return $data; } catch(Exception $e){} return false; } /** * Streams a file through fopen and fread on the ssh.sftp stream, in case the above method fails */ private function receiveFile($remote_file, $destination){ $sftp = $this->sftp; if ( !file_exists("ssh2.sftp://$sftp$remote_file") ){ return false; } if ( !$stream = fopen("ssh2.sftp://$sftp$remote_file", 'rt') ){ return false; } $filesize = filesize("ssh2.sftp://$sftp$remote_file"); if( !$filesize ){ return false; } // Create our temp file and open a handle to write to it $out = fopen($destination, 'w'); $contents = ''; $read = 0; while ($read < $filesize && ($buffer = fread($stream, $filesize - $read))) { // Increase our bytes read $read += strlen($buffer); // Write to our local file if (fwrite($out, $buffer) === FALSE) { throw new Exception("Unable to write to local file: $destination"); } } unset($buffer); fclose($stream); fclose($out); return true; } }

From there you can call simple commands doing the following:

$retriever = new FileRetriever(); if( !$retriever->connect(array( 'host' => '####', 'user' => '####', 'password' => '####', )) ){ die('connect failed'); return false; } $output = $retriever->execute('ls -halF'); echo "Output: " . $output . "<br />\n";

If the system you're connected to allows it, you'll get preformatted text that looks similar to:

Output: total 452K drwxr-xr-x 50 lance www-data 4.0K Nov 19 08:19 ./ drwxr-xr-x 3 root root 4.0K Jul 12 2012 ../ drwx------ 3 lance www-data 4.0K Jul 17 2012 .adobe/ drwxr-xr-x 3 lance www-data 4.0K Mar 15 2013 .android/ -rw------- 1 lance www-data 42K Nov 19 08:22 .bash_history -rw-r--r-- 1 lance www-data 220 Jul 2 2012 .bash_logout -rw-r--r-- 1 lance www-data 3.6K Oct 23 2012 .bashrc drwxr-xr-x 2 lance www-data 4.0K Mar 13 2013 bin/ drwx------ 21 lance www-data 4.0K Nov 18 13:36 .cache/ drwxr-xr-x 3 lance www-data 4.0K Oct 23 2012 .codeintel/ drwxr-xr-x 3 lance www-data 4.0K Jul 3 2012 .compiz-1/ drwxr-xr-x 3 lance www-data 4.0K Nov 18 13:35 .composer/ drwx------ 22 lance www-data 4.0K Oct 28 15:54 .config/ drwxr-xr-x 3 lance www-data 4.0K Sep 24 16:04 .cordova/ drwx------ 3 lance www-data 4.0K Jul 2 2012 .dbus/ ...