cbj4074;10933858 wrote:I will post back as soon as I [determine what, exactly, I did to make public key authentication work as expected] .
Well, here's the short and long of it. It seems to work fine, both on Windows 7 with Apache 2 and on CentOS 5 Final.
To install the SSH functions on Windows, I had to grab the PECL DLL files and enable the extension (i.e., add "extension=php_ssh2.dll" to php.ini).
On CentOS, I had to build the module from source, but it was easy:
Install source and compile ssh2 extension for PHP:
rpm -Uvh libssh2-0.17-1.el5.rf.i386.rpm
rpm -Uvh libssh2-devel-0.17-1.el5.rf.i386.rpm
tar -zxvf ssh2-0.11.0.tgz
cd ssh2-0.11.0
phpize && ./configure --with-ssh2 && make
Copy the module to PHP's modules directory:
(Get PHP module directory with: cat /etc/php.ini | grep extension_dir | grep -v ";")
cp ./ssh2-0.11.0/modules/ssh2.so /usr/lib/php/modules/
Of course, Apache needs to know about the module, too (this example demonstrates the procedure on a Plesk-enabled webserver):
vim /etc/php.d/ssh2.ini
(Note that in the above example, your server configuration may require you to enable the SSH2 module in a different file!)
The first two lines of ssh2.ini (or an appropriate section of whatever your configuration file happens to be) should contain:
; Enable SSH2 extension module
extension=ssh2.so
Refresh the Apache server configuration files:
/usr/local/psa/admin/bin/websrvmng -a
What good is the above, absent a "real-life" PHP example? "As always, I never stop workin'" (to quote the Diceman):
<?php
//Based largely on code from the comments at:
//http://php.net/manual/en/function.ssh2-connect.php
class ssh2
{
private $host = 'host';
private $user = 'user';
private $port = '22';
private $password = 'password';
private $con = null;
private $pubKeyFile = null;
private $privKeyFile = null;
private $shell_type = 'xterm';
private $shell = null;
private $log = array();
private $sftp = null;
private $knownHost = null;
function __construct($host = '', $port = '', $knownHost = NULL)
{
if ($host != '') {
$this->host = $host;
}
if ($port != '') {
$this->port = $port;
}
if (!is_null($knownHost)) {
$this->knownHost = $knownHost;
}
//See PHP manual for further explanation.
//XXX The remaining three callbacks still need to be implemented.
$callbacks = array(
'ignore' => array($this, 'handleSshDisconnect'),
'debug' => array($this, 'handleSshDisconnect'),
'macerror' => array($this, 'handleSshDisconnect'),
'disconnect' => array($this, 'handleSshDisconnect'),
);
$this->con = ssh2_connect($this->host, $this->port, array('hostkey', 'ssh-rsa'), $callbacks);
//If a value is not returned (nor the connection made null)
//it appears as though some kind of time-out must be reached on a failure.
//Adding a return value or setting the value to null reduced the
//execution time from several minutes to near-instant in my tests.
//Is anyone able to confirm?
if (!$this->con) {
$this->log[] = 'Connection failed!';
$this->con = NULL;
}
else {
//If a host fingerprint has been required, check it.
if (!is_null($this->knownHost)) {
$fingerprint = ssh2_fingerprint($this->con, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);
if (is_string($fingerprint)) {
if (strcasecmp($fingerprint, $this->knownHost) != 0) {
$this->log[] = 'HOSTKEY MISMATCH! (Expected "' . $this->knownHost . '" and received "' . $fingerprint . '" [comparison is case-insensitive]; possible Man-In-The-Middle Attack?';
$this->con = NULL;
}
}
else {
$this->log[] = 'Could not determine remote host fingerprint (returned value was "' . $fingerprint . '"); aborting connection';
$this->con = NULL;
}
}
}
}
function getCon()
{
return $this->con;
}
function getSftp()
{
return $this->sftp;
}
function authPassword($user = '', $password = '')
{
if ($user != '') {
$this->user = $user;
}
if ($password != '') {
$this->password = $password;
}
if (!ssh2_auth_password($this->con, $this->user, $this->password)) {
$this->log[] = 'Authorization failed!';
return FALSE;
}
else {
return TRUE;
}
}
//XXX For additional discussion, see:
//http://phpbuilder.com/board/showthread.php?t=10369507
function authKeyFile($user, $pubKeyFile, $privKeyFile)
{
if ($user != '') {
$this->user = $user;
}
if ($pubKeyFile != '') {
$this->pubKeyFile = $pubKeyFile;
}
if ($privKeyFile != '') {
$this->privKeyFile = $privKeyFile;
}
if (!ssh2_auth_pubkey_file($this->con, $this->user, $this->pubKeyFile, $this->privKeyFile)) {
$this->log[] = 'Public key authorization failed (public key "' . $this->pubKeyFile . '" and private key "' . $this->privKeyFile . '")!';
return FALSE;
}
else {
return TRUE;
}
}
function addPubKey()
{
$res = $this->cmdExec('env');
var_dump($res);
#$pkey = ssh2_publickey_init($this->con);
#var_dump($pkey);
#die;
}
function openShell($shell_type = '')
{
if ($shell_type != '') {
$this->shell_type = $shell_type;
}
$this->shell = ssh2_shell($this->con, $this->shell_type);
if (!$this->shell) {
$this->log[] = 'Shell connection failed!';
return FALSE;
}
else {
return TRUE;
}
}
function writeShell($command = '')
{
fwrite($this->shell, $command . PHP_EOL);
return NULL;
}
function cmdExec()
{
$argc = func_num_args();
$argv = func_get_args();
$cmd = '';
for ($i = 0; $i < $argc; $i ++) {
if ($i != ($argc - 1)) {
$cmd .= $argv[$i] . ' && ';
} else {
$cmd .= $argv[$i];
}
}
$stream = ssh2_exec($this->con, $cmd);
if ($stream !== FALSE) {
stream_set_blocking($stream, true);
$contents = '';
while($line = fgets($stream)) {
flush();
$contents .= $line;
}
fclose($stream);
return $contents;
}
else {
$this->log[] = 'SSH command execution failed!';
return FALSE;
}
}
function sftpCon()
{
$sftp = ssh2_sftp($this->con);
$this->sftp = $sftp;
return NULL;
}
function getLog()
{
return $this->log;
}
//XXX Does the $language parameter actually need to be specified in the call?
//I guess we'll find out the first time it happens!
function handleSshDisconnect($reason, $message, $language) {
$error = printf("Server disconnected with reason code [%d] and message: %s" . PHP_EOL, $reason, $message);
return $error;
}
}
To implement the above class, try something like this:
function connectToServer()
{
//Establish an SSH session, so we can use file_exists()
//on the remote server (requires sftp wrapper).
$ssh = new ssh2(SOME_SERVER, '22', SOME_SERVER_FINGERPRINT);
if ($ssh->getCon() !== NULL) {
//We were able to open an SSH session and the remote
//server has the fingerprint that we expect. It's safe
//to supply authentication credentials (as safe as possible, anyway).
if ($ssh->authKeyFile(SSH_USER, SSH_PUBKEY, SSH_PRIVKEY)) {
//If authenticaton was successful, establish
//an SFTP session so that sftp wrappers are available.
$ssh->sftpCon();
return $ssh;
}
else {
return FALSE;
}
}
else {
return FALSE;
}
}
$result = connectToServer();
Don't forget to define the constants, which appear in UPPERCASE letters, if you decide to use the above code. For example:
define('NAME_OF_CONSTANT', $value);