Make SSH connections with PHP

Not everyone knows about PHP's capabilities of making SSH connections and executing remote commands, but it can be very useful. I've been using it a lot in PHP CLI applications that I run from cronjobs, but initially it was a pain to get it to work. The PHP manual on Secure Shell2 Functions is not very practice or thorough for that matter, so I would like to share my knowledge in this how to, to make it a little less time consuming setting this up.

In this article I'm going to assume that:

  • You're running Debian / Ubuntu
    If not, you will have to substitute the package manager aptitude with whatever your distribution provides
  • You're running PHP 5
    If not, just replace php5 with php4 everywhere
  • You have basic knowledge of PHP & server administration
  • You already have PHP installed

Prerequisites

Packages

First let's install the following packages:

sudo aptitude update
sudo aptitude install php5-dev php5-cli php-pear buid-essential \
openssl-dev zlib1g-dev

That should set us up alright.

libssh2

Now we need libssh2 from sourcefourge. We have to compile this, but no worries, this is all you need to do:

cd /usr/src
wget http://surfnet.dl.sourceforge.net/sourceforge/libssh2/libssh2-0.14.tar.gz
tar -zxvf libssh2-0.14.tar.gz
cd libssh2-0.14/
./configure
make all install

That's it! Easy right? If you want you can check if there's a newer version at sf.net but 0.14 will do just fine.

Installation

ssh2.so

Next we need to link libssh & PHP together. There's a PECL module for this so let's install using:

pecl install -f ssh2

The -f makes sure ssh2 is installed even though there's not a stable candidate. You could also use the package name: ssh2-beta to overrule this.

Now you need to make sure our new ssh2.so module is loaded by PHP. Edit your php.ini file (for CLI utitilies: /etc/php5/cli/php.ini, for Apache utilities /etc/php5/apache2/php.ini)

extension=ssh2.so

It should be placed beneath the: "Dynamic Extensions" section somewhere around line 515.

Great! PHP supports SSH - time to code

You've just enabled ssh2 support in PHP. Now how can we make use of this? There are 2 options. SSH supports the: 

  • execute method
    This tells the server's operating system to execute something and pipe the output back to your script. (recommended)
  • shell method
    This opens an actual shell to the operating system, just as you would normally when logging in with your terminal application. Some routers that don't have a full POSIX compliant implementation, but run their own application as soon as you login, require this. (advanced)

Method 1: Execute

Best would be to create functions or even a class for the following code, but this is the basic idea and will definitely get you started:

if (!function_exists("ssh2_connect")) die("function ssh2_connect doesn't exist")
// log in at server1.example.com on port 22
if(!($con = ssh2_connect("server1.example.com", 22))){
  echo "fail: unable to establish connection\n";
} else {
  // try to authenticate with username root, password secretpassword
  if(!ssh2_auth_password($con, "root", "secretpassword")) {
    echo "fail: unable to authenticate\n";
  } else {
    // allright, we're in!
    echo "okay: logged in...\n";
    
    // execute a command
    if(!($stream = ssh2_exec($con, "ls -al" )) ){
      echo "fail: unable to execute command\n";
    } else{
      // collect returning data from command
      stream_set_blocking( $stream, true );
      $data = "";
      while( $buf = fread($stream,4096) ){
        $data .= $buf;
      }
      fclose($stream);
    }
  }
}

Method 2: Shell

Best would be to create functions or even a class for the following code, but this is the basic idea and will definitely get you started:

if (!function_exists("ssh2_connect")) die("function ssh2_connect doesn't exist")
// log in at server1.example.com on port 22
if(!($con = ssh2_connect("server1.example.com", 22))){
  echo "fail: unable to establish connection\n";
} else {
  // try to authenticate with username root, password secretpassword
  if(!ssh2_auth_password($con, "root", "secretpassword")) {
    echo "fail: unable to authenticate\n";
  } else {
    // allright, we're in!
    echo "okay: logged in...\n";
    
    // create a shell
    if(!($shell = ssh2_shell($con, 'vt102', null, 80, 40, SSH2_TERM_UNIT_CHARS))){
      echo "fail: unable to establish shell\n";
    } else{
      stream_set_blocking( $shell, true );
      // send a command 
      fwrite($shell,"ls -al\n");
      sleep(1);
      
      // & collect returning data
      $data = "";
      while( $buf = fread($shell,,4096) ){
        $data .= $buf;
      }
      fclose($shell);
    }
  }
}

Tips

Sometimes when a server is busy, or a connection is buggy, the buffer may run dry, and the PHP script stops collecting data from a command output (even though the command hasn't completed yet!). There are a couple of things you could do about that:

ssh2_exec($con, 'ls -al; echo "__COMMAND_FINISHED__"' );

Now, in the loop where you keep checking for the buffer, just see if the COMMAND_FINISHED line is coming by. Because then you know you have all the data. To avoid infinite loops, just limit the loop with a timeout of 10 seconds or so:

$time_start = time();
$data = "";
while( true ){
  $data .= fread($stream, 4096);
  if(strpos($data,"__COMMAND_FINISHED__") !== false){
    echo "okay: command finished\n";
    break;
  }
  if( (time()-$time_start) > 10 ){
    echo "fail: timeout of 10 seconds has been reached\n";
    break;
  }
}

In the example above, you'd better set stream_set_blocking to false.

Can't get enough?

PHP can send files over ssh!

ssh2_scp_send($con, "/tmp/source.dat", "/tmp/dest.dat", 0644);

Doesn't work?

Check the following:

 - Did you follow every step of the prerequisites & installation how to in this article?
 - On the serverside, 'PasswordAuthentication yes' must be enabled in the sshd_config. Default is yes on most servers, but in some cases you will have to turn this on yourself by making sure the following lines are in place in the file: /etc/ssh/sshd_config:

# Change to yes to enable tunnelled clear text passwords
PasswordAuthentication yes

If you've made any changes, ssh needs a restart

/etc/init.d/ssh restart 

 

Post a comment here if it's still failing. Don't forget to paste the error that you're getting.


Like this article?

The only thing that I ask is if you find this site useful, help me spread the word by linking to it or bookmarking it with sites like digg.com or del.icio.us.



Related articles

tags: php, ssh, ubuntu, PEAR, PECL, libssh2, CLI, apache, router
category: How to
read: 5,958 times

Congratulations! You've successfully submitted your comment

Comments

#15. Oriol on 23 September 2007

OriolI'm using Ubuntu dapper 6.06 LTS in my servers. When I try to install openssl-dev I don't found it. But, the correct package for me is libssl-dev.

Tancks for your guide is very useful for me.

#14. ServerChief on 13 September 2007

ServerChiefHi,

Just wanted to say thank you for this tutorial. I'm writting an application for VmWare ESX Updater Services, this tutorial, without a doubt will help me do it more efficiently.

Thanks,
... [more]
ServerChief

http://www.serverchief.com

#13. Jason on 11 September 2007

JasonHi Kevin,

Do you have any idea about SSH Tunneling? I tried the following code but don't work:

$connection = ssh2_connect($SSH_HOST, $SSH_PORT);
... [more] ssh2_auth_password($connection, $SSH_LOGIN, $SSH_PASSWORD);
$tunnel = ssh2_tunnel($connection, $REMOTE_HOST.":".$REMOTE_PORT, $LOCAL_PORT);

Could you please help me?

#12. Kevin on 30 August 2007

Kevin@ trume: sorry dude I don't have email ;)

Anyhow, you might want to concatenate all of your commands with semicolons like this:
ls -al; echo "!BREAK!"; cat /proc/cpuinfo; echo "!BREAK!"; cat /proc/loadavg; echo "!BREAK!"; df -h

... [more] The shell on the other side will just run every command and give one big output.

Then just parse the output in PHP by exploding on "!BREAK!" (see 'explode' function in PHP manual)

#11. trume on 30 August 2007

trumehi,kevin,i wander if i can run the ssh connection once and get all infomation in that way.

i need to get the infomation realtime remote in ssh2,and now i need some advise if i can do it with php.

i will very glad if you can send a email, ^_^ my email is aquajamy@gmail.com.
... [more]
thanks.

#10. Kevin on 28 August 2007

Kevin@ Jan: When I install I get similar warnings like:
/tmp/pear/cache/ssh2-0.10/ssh2.c: In function 'zif_ssh2_methods_negotiated':
/tmp/pear/cache/ssh2-0.10/ssh2.c:483: warning: assignment discards qualifiers from pointer target type

However the building just continues and so they can be ignored. But in your case the 'make' exits with an error before it can run: /bin/bash /var/tmp/pear-build-root/ssh2-0.10/libtool
... [more]
Can it be that you ran into this bug?
http://pecl.php.net/bugs/bug.php?id=11779

#9. jan on 27 August 2007

jani get this ...

/tmp/pear/cache/ssh2-0.10/ssh2.c: In function 'zif_ssh2_methods_negotiated':
/tmp/pear/cache/ssh2-0.10/ssh2.c:481: warning: passing argument 2 of 'libssh2_session_methods' makes integer from pointer without a cast
/tmp/pear/cache/ssh2-0.10/ssh2.c:481: error: too many arguments to function 'libssh2_session_methods'
... [more] /tmp/pear/cache/ssh2-0.10/ssh2.c: In function 'zif_ssh2_fingerprint':
/tmp/pear/cache/ssh2-0.10/ssh2.c:536: warning: assignment discards qualifiers from pointer target type
/tmp/pear/cache/ssh2-0.10/ssh2.c: In function 'zif_ssh2_publickey_add':
/tmp/pear/cache/ssh2-0.10/ssh2.c:1038: warning: passing argument 1 of '_efree' discards qualifiers from pointer target type
/tmp/pear/cache/ssh2-0.10/ssh2.c: In function 'zif_ssh2_publickey_list':
/tmp/pear/cache/ssh2-0.10/ssh2.c:1097: warning: passing argument 4 of 'add_assoc_stringl_ex' discards qualifiers from pointer target type
/tmp/pear/cache/ssh2-0.10/ssh2.c:1098: warning: passing argument 4 of 'add_assoc_stringl_ex' discards qualifiers from pointer target type
/tmp/pear/cache/ssh2-0.10/ssh2.c:1106: warning: initialization discards qualifiers from pointer target type
/tmp/pear/cache/ssh2-0.10/ssh2.c:1107: warning: passing argument 2 of '_zend_hash_add_or_update' discards qualifiers from pointer target type
make: *** [ssh2.lo] Error 1


makes me sad :(

#8. Peter on 14 August 2007

PeterNever mind, my mistake. I forgot to run 'make all install'. Duh!

#7. Peter on 14 August 2007

PeterGreat tutorial; now to make it work. ;-)

When I run 'pecl install -f ssh2-beta' I get this error:

configure: error: The required libssh2 library was not found. You can obtain that package from http://sourceforge.net/projects/libssh2/
... [more]
Any suggestions?

#6. tr on 13 August 2007

trInstead of 'pear install -f ssh2' try with 'pecl install -f ssh2'.

#5. Kevin on 10 August 2007

KevinDid you type '-f' ?

#4. Will on 09 August 2007

WillFirst of all, great article. Second I'm having an error I was wondering if you had any thoughts on. I've been following the instructions pretty closely and all seems to have gone well up until the ssh2.so portion. Here is the error that I get when trying to run pear....

No releases available for package "pear.php.net/ssh2" - package pecl/ssh2 can be installed with "pecl install ssh2"
Cannot initialize 'ssh2', invalid or missing package file
Package "ssh2" is not valid
... [more] install failed


Any help would be great. Thanks...

#3. NiKo on 30 July 2007

NiKoGreat article, thanks.

#2. Kevin on 28 July 2007

KevinHi Sara, nice to know that improvements are on it's way. And I'd just like to say you did a great job on integrating ssh into php. It really made my life easier (it definitely beats making system calls and parsing output ;)

And as for the PHP manual, maybe I will contribute directly to it. Not a bad idea. Thanks for your comment!

#1. Sara Golemon on 27 July 2007

Sara GolemonGlad you like PECL/ssh2 and libssh2! :)

A few notes:

Yes, 0.15 is out, with several refactoring points and I need to get around to updating the PHP extension to take advantage of new stuff in the library.
... [more]
Part of the reason for the refactorings is that some of the cURL developers have gotten involved in its development and wanted to make it better before building support for it into cURL (which...by the way, cURL now supports SFTP via libssh2).

SCP doesn't work entirely right. I recommend SFTP to anyone who has it available. It's a much more robust protocol and libssh2's implementation of it works much more solidly than it's implementation of SCP. (If ANYONE has a spec document describing SCP or RCP, I'd love to get a link...)

As to docs... Yeah... they could definately stand to be better... You're welcome to contribute directly to the PHP manual. CVS repository can be browsed at http://cvs.php.net/phpdoc/en/reference/ssh2 , anonymous CVS access instructions are at http://php.net/anoncvs.php, and instructions for getting your own cvs account can be found at http://php.net/cvs-php.php . You can also, of course, just send diffs (unified) to the phpdoc mailing list and ask someone politely to commit it for you.