Auto-Establishing SSH Tunnels

Standard

SSH Tunnels – Auto-Establishing

I have a cron job which connects manually replicates a database over an SSH tunnel for those ‘Oh Sh!t’ moments. Sometimes that SSH tunnel will drop or fail to establish. Within the cron job I needed a way to make sure that didn’t happen.

The Code

The code below is the first part of the bash file executed by cron.

We run netstat -a and grep for the port the tunnel is supposed to be established on, and if it is less than 2, it will execute and create the tunnel.

[bash]
#!/bin/bash
REMOTEHOST=10.1.1.1
TUNNEL=$(netstat -a | grep -c 3307)

if [ $TUNNEL -lt 2 ]; then
ssh -f root@$REMOTEHOST -L 3307:localhost:3306 -N
fi
[/bash]

After the above snippet, you can continue whatever script or application which needed the SSH tunnel to be established.
Enjoy!

PHP SSH2 Class with Exception Handling

Standard

SSH2 Wrapper Class

The following class takes some of the ideas used to create a contextual socket, and applied to the setup of a SSH2 stream. Currently, only password authentication is supported, but will be updated shortly. Support for SCP and SFTP will be included at a later time.

SSH2 Usage

[php]

$options = array(
‘host’ => ’127.0.0.1′,
‘port’ => 22,
‘auth’ =>
array(
‘type’ => SSH2::PASS,
‘username’ => ‘user’,
‘password’ => ‘password’,
‘fingerprint’ => ‘xxxx’,
‘ssh_auth_pub’ => ’3sg325235y43yre’,
‘ssh_auth_priv’ => ’3sg325235y43yre’,
‘ssh_auth_priv_key’ => ’3sg325235y43yre’,
)
);

try{
$ssh = new SSH2($options)->connect();
if($ssh->authenticate()){
/* For devices which support OpenSSH and OpenSSH2 Variants */
$exampleOutput = $ssh->exec(array(‘ls -lSha’, ‘cat ~/.bashrc’));

if(!$exampleOutput ){
echo $ssh->getLastError();
}else{
echo $exampleOutput ;
}

/* For devices which support net-sshd, vt100, etc */
$exampleOutput = $ssh->shell(array(‘ls -lSha’, ‘cat ~/.bashrc’));

if(!$exampleOutput ){
echo $ssh->getLastError();
}else{
echo $exampleOutput ;
}
}

$ssh->disconnect();
}
catch(SSH2FailedToConnectException $e){
print_r($e->getMessage());
}
catch(SSH2FailedToAuthenticate $e){
print_r($e->getMessage());
}
[/php]

Get The SSH2 Class

[php]
class SSH2{
private static $connection;
private $error;

var $port = 22;

const PASS = ‘password’;
const PUBKEY = ‘publickey’;

public function __construct(Array $options){
foreach($options as $opt => $value){
$this->$opt = $value;
}

return $this;
}

final function xdisconnect($reason, $message, $language) {
printf(“Server disconnected with reason code [%d] and message: %s\n”,
$reason, $message);
}

final function connect(){
$callbacks = array(‘disconnect’ => ‘xdisconnect’);

self::$connection = @ssh2_connect($this->host, $this->port, NULL, $callbacks);
if(self::$connection === FALSE){

throw new SSH2FailedToConnectException($this->host, $this->port);
return false;

}

$this->fingerprint = @ssh2_fingerprint(self::$connection);
return true;
}

// sm business
final function authenticate(){
$method = “ssh2_auth_{$this->auth['type']}”;

if(@$method(self::$connection, $this->auth['username'], $this->auth['password']) === FALSE){
throw new SSH2FailedToAuthenticate($this->host, $this->auth['username'], $this->auth['type']);
return false;
}else{
return true;
}
}

public function exec($cmd){
if(is_array($cmd)){
foreach($cmd as $command){
$stream = @ssh2_exec(self::$connection, $command);
$errorStream = @ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);

/* Enable Blocking */
@stream_set_blocking($errorStream, true);
@stream_set_blocking($stream, true);

/* Grab Response */
$response .= stream_get_contents($stream);
$this->error .= stream_get_contents($errorStream);
}
}
else{
$stream = @ssh2_exec(self::$connection, $cmd);
$errorStream = @ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);

/* Enable Blocking */
@stream_set_blocking($errorStream, true);
@stream_set_blocking($stream, true);

/* Grab Response */
$response .= stream_get_contents($stream);
$this->error .= stream_get_contents($errorStream);
}

if(is_null($response)){
return false;
}

return $response;
}

final function shell($cmd){
$stream = ssh2_shell (self::$connection, ‘vt102′, null, 80, 40, SSH2_TERM_UNIT_CHARS);
$output = NULL;

if(is_array($cmd)){
foreach($cmd as $command){
fwrite($stream, $command.PHP_EOL);
sleep(2);

while(( $res = stream_get_contents($stream, -1)) !== false){
$output .= $res;
if($res == ”){
break;
}
}
}
}
else{
fwrite($stream, $cmd.PHP_EOL);
sleep(2);

while(( $res = stream_get_contents($stream, -1)) !== false){
$output .= $res;
if($res == ”){
break;
}
}

}

fwrite($stream, ‘exit’.PHP_EOL);

return $output;
}

public function disconnect(){
@ssh2_exec(self::$connection, ‘exit’);
}

public function getLastError(){
return $this->error;
}
}

/*
* Thrown if a class tries to access the XML parser’s functionality
*
* @author sixeightzero
* @license http://opensource.org/licenses/gpl-license.php GNU General Public Licence
* @copyright (c) 2011 – Mike Mackintosh
* @version 0.1
* @package Zepnik Framework
*/

final class SSH2FailedToConnectException extends Exception
{
/**
* Sets the error message
*
* @todo Add logger output
*/
public function __construct($host, $port)
{
$message = “Failed to connect to host ‘{$host}’ on port {$port}\n”;

// Call the parent constructor
parent::__construct($message);
}
}

final class SSH2FailedToAuthenticate extends Exception
{
/**
* Sets the error message
*
* @todo Add logger output
*/
public function __construct($host, $username, $type)
{
$message = “Failed to authenticate ‘{$username}’ by ‘{$type}’ on host ‘{$host}’\n”;

// Call the parent constructor
parent::__construct($message);
}
}
[/php]