From eda833bc70c8795b36eea1e63c9b984024a97d64 Mon Sep 17 00:00:00 2001 From: AdriwanKenoby Date: Fri, 2 Mar 2018 10:53:47 +0100 Subject: [PATCH 1/3] add features for connection with rsa keys authentication - remove deprecated extend Exception - handle error on ssh error command --- .gitignore | 1 + Entity/Profile.php | 127 -------------------------- Exception/ConnectionException.php | 17 +++- Exception/Exception.php | 28 ------ Factory/ConnectionFactory.php | 23 +++++ {Entity => Model}/Connection.php | 41 +++++++-- Model/Profile.php | 145 ++++++++++++++++++++++++++++++ Resources/config/services.yml | 2 +- Service/Connection.php | 32 ------- 9 files changed, 220 insertions(+), 196 deletions(-) create mode 100644 .gitignore delete mode 100644 Entity/Profile.php delete mode 100644 Exception/Exception.php create mode 100644 Factory/ConnectionFactory.php rename {Entity => Model}/Connection.php (76%) create mode 100644 Model/Profile.php delete mode 100644 Service/Connection.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..757fee3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea \ No newline at end of file diff --git a/Entity/Profile.php b/Entity/Profile.php deleted file mode 100644 index 072efb6..0000000 --- a/Entity/Profile.php +++ /dev/null @@ -1,127 +0,0 @@ -setAddress($address); - $this->setLogin($login); - $this->setPassword($password); - $this->setPort($port); - } - - /** - * Define address - * - * @param string $address - * @return $this - */ - public function setAddress($address) - { - $this->address = $address; - - return $this; - } - - /** - * Return address - * - * @return string - */ - public function getAddress() - { - return $this->address; - } - - /** - * Define port - * - * @param int $port - * @return $this - */ - public function setPort($port) - { - $this->port = $port; - - return $this; - } - - /** - * Return port - * - * @return int - */ - public function getPort() - { - return $this->port; - } - - /** - * Define login - * - * @param string $login - * @return $this - */ - public function setLogin($login) - { - $this->login = $login; - - return $this; - } - - /** - * Return login - * - * @return string - */ - public function getLogin() - { - return $this->login; - } - - /** - * Define password - * - * @param string $password - * @return $this - */ - public function setPassword($password) - { - $this->password = $password; - - return $this; - } - - /** - * Return password - * - * @return string - */ - public function getPassword() - { - return $this->password; - } -} diff --git a/Exception/ConnectionException.php b/Exception/ConnectionException.php index 1227a96..fd9c389 100644 --- a/Exception/ConnectionException.php +++ b/Exception/ConnectionException.php @@ -5,7 +5,22 @@ /** * SSH2 connection exception */ -class ConnectionException extends Exception +class ConnectionException extends \Exception { + /** + * Constructor + * + * @param string $message + * @param Profile $profile + */ + public function __construct($message, Profile $profile = null) + { + if ($profile instanceof Profile) { + $message = '[' . (string)$profile . '] ' . $message; + } else { + $message = '[No profile specified] ' . $message; + } + parent::__construct($message); + } } diff --git a/Exception/Exception.php b/Exception/Exception.php deleted file mode 100644 index 1bcda42..0000000 --- a/Exception/Exception.php +++ /dev/null @@ -1,28 +0,0 @@ -getLogin() . '@' . $profile->getAddress() . ':' . $profile->getPort() . '] ' . $message; - } else { - $message = '[No profile specified] ' . $message; - } - - parent::__construct($message); - } -} diff --git a/Factory/ConnectionFactory.php b/Factory/ConnectionFactory.php new file mode 100644 index 0000000..366a95c --- /dev/null +++ b/Factory/ConnectionFactory.php @@ -0,0 +1,23 @@ +connection === false) { $this->state = self::STATE_INVALID_ADDRESS; } else { - $auth = @ssh2_auth_password($this->connection, $profile->getLogin(), $profile->getPassword()); + $auth = @ssh2_auth_pubkey_file($this->connection, $profile->getLogin(), $profile->getRsaPubKey(), $profile->getRsaPemKey(), $profile->getPassphrase()); + if ($auth === false) { + $auth = @ssh2_auth_password($this->connection, $profile->getLogin(), $profile->getPassword()); + } if ($auth === false) { $this->state = self::STATE_INVALID_LOGIN; } else { @@ -119,20 +122,40 @@ public function getProfile() return $this->profile; } + public function getConnection() + { + return $this->connection; + } + /** * Execute a command * - * @param string $command - * @return string + * @param $command + * @return bool|string + * @throws ConnectionException + * */ public function exec($command) { $this->assertConnected(); - $stream = ssh2_exec($this->connection, $command); - stream_set_blocking($stream, true); - $streamOut = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO); - return stream_get_contents($streamOut); + if( !($stream = ssh2_exec($this->connection, $command)) ) { + throw new ConnectionException( + sprintf('%s command failed through ssh', $command), + $this->profile + ); + } + $errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR); + stream_set_blocking($errorStream, TRUE); + stream_set_blocking($stream, TRUE); + $output = stream_get_contents($stream); + $error_output = stream_get_contents($errorStream); + fclose($stream); + fclose($errorStream); + if (!empty($error_output)) { + throw new ConnectionException($error_output, $this->profile); + } + return $output; } /** @@ -141,6 +164,7 @@ public function exec($command) * @param string $command * @param string $separator * @return array + * @throws ConnectionException */ public function execExplode($command, $separator = ' ') { @@ -155,6 +179,7 @@ public function execExplode($command, $separator = ' ') * @param string $separator Separator * @param mixed $default Default value if $index is not found * @return string + * @throws ConnectionException */ public function execExplodeIndex($command, $index, $separator = ' ', $default = null) { @@ -167,6 +192,7 @@ public function execExplodeIndex($command, $index, $separator = ' ', $default = * * @param type $command * @return array + * @throws ConnectionException */ public function execLines($command) { @@ -182,6 +208,7 @@ public function execLines($command) * @param int $index Index of the line to return * @param string $default Default value * @return string + * @throws ConnectionException */ public function execLine($command, $index = 0, $default = null) { diff --git a/Model/Profile.php b/Model/Profile.php new file mode 100644 index 0000000..331f44f --- /dev/null +++ b/Model/Profile.php @@ -0,0 +1,145 @@ + $val) { + if ($key === self::ADDRESS || + $key === self::PORT || + $key === self::LOGIN || + $key === self::PASSWORD || + $key === self::PASSPHRASE) { + + $this->{$key} = $val; + + } elseif ($key === self::ID_RSA_PUB || + $key === self::ID_RSA_PEM) { + + $key = str_replace('-', '_', $key); + $this->{$key} = $val; + + } else { + throw new \InvalidArgumentException( + sprintf('%s is not a valid property in constructor %s', + $key, + self::class + ) + ); + } + } + } + + /** + * Return address + * + * @return string + */ + public function getAddress() : string + { + return $this->address; + } + + /** + * Return port + * + * @return int + */ + public function getPort() : int + { + return $this->port; + } + + /** + * Return login + * + * @return string + */ + public function getLogin() : string + { + return $this->login; + } + + /** + * Return password + * + * @return string + */ + public function getPassword() : ?string + { + return $this->password; + } + + /** + * @return string + */ + public function getRsaPubKey() : ?string + { + return $this->id_rsa_pub; + } + + /** + * @return string + */ + public function getRsaPemKey() : ?string + { + return $this->id_rsa_pem; + } + + /** + * @return string + */ + public function getPassphrase() : ?string + { + return $this->passphrase; + } + + public function __toString() + { + return $this->login .'@' . $this->address . ':' .(string) $this->port; + } +} diff --git a/Resources/config/services.yml b/Resources/config/services.yml index f38207c..c07806b 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -1,3 +1,3 @@ services: ssh2: - class: steevanb\SSH2Bundle\Service\Connection + class: steevanb\SSH2Bundle\Factory\ConnectionFactory diff --git a/Service/Connection.php b/Service/Connection.php deleted file mode 100644 index 1f3ba58..0000000 --- a/Service/Connection.php +++ /dev/null @@ -1,32 +0,0 @@ -setAddress($address); - $profile->setLogin($login); - $profile->setPassword($password); - $profile->setPort($port); - return new EntityConnection($profile, $autoConnect); - } -} From 357a0c733b19a25550b2ee8a676c107003506325 Mon Sep 17 00:00:00 2001 From: AdriwanKenoby Date: Fri, 2 Mar 2018 11:02:57 +0100 Subject: [PATCH 2/3] improve README --- Resources/doc/InstallPHPSSH2.md | 9 ++------- Resources/doc/changelog.md | 8 ++++++++ Resources/doc/profile.md | 12 +++++++++++- Resources/doc/usage.md | 12 ++++++++++-- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Resources/doc/InstallPHPSSH2.md b/Resources/doc/InstallPHPSSH2.md index 8f0b4ac..4220018 100644 --- a/Resources/doc/InstallPHPSSH2.md +++ b/Resources/doc/InstallPHPSSH2.md @@ -4,13 +4,8 @@ SSH2 PHP extension installation Linux / Ubuntu -------------- ``` -mkdir /etc/php5/conf.d -apt-get install libssh2-php -# only if XX-ssh2.ini doesn't exists in /etc/php5/apache2/conf.d/ -cp /etc/php5/conf.d/ssh2.ini /etc/php5/apache2/conf.d/ -# only if XX-ssh2.ini doesn't exists in /etc/php5/cli/conf.d/ -cp /etc/php5/conf.d/ssh2.ini /etc/php5/cli/conf.d/ -service apache2 restart +apt-get install libssh2-1 +apt-get install php-ssh2 ``` [Back to index](../../README.md) diff --git a/Resources/doc/changelog.md b/Resources/doc/changelog.md index 92d494d..c798ed0 100644 --- a/Resources/doc/changelog.md +++ b/Resources/doc/changelog.md @@ -1,3 +1,11 @@ +3.0.0 (2018-03-02) +------------------ + +- Remove deprecated extend Exception +- Add support for rsa keys authentication +- Handle error on ssh command error +- change constructors + 2.0.0 (2015-06-01) ------------------ diff --git a/Resources/doc/profile.md b/Resources/doc/profile.md index f33504b..8debd87 100644 --- a/Resources/doc/profile.md +++ b/Resources/doc/profile.md @@ -3,7 +3,17 @@ Entity\Profile ```php # constructor -__construct($address = null, $login = null, $password = null, $port = 22) +__construct([ + 'adrress' => $address, + 'port' => $port, + 'login' => $login, + 'id-rsa-pub' => $id_rsa_pub, + 'id-rsa-pem' => $id_rsa_pem +]) + +As the user who exec comand is www-data you have to create you keys using www-data user, +the simplest is to temporary allow bash for this user by editing /etc/passwd + # address setAddress($address) : Profile diff --git a/Resources/doc/usage.md b/Resources/doc/usage.md index fd6962f..13227c8 100644 --- a/Resources/doc/usage.md +++ b/Resources/doc/usage.md @@ -6,11 +6,19 @@ use steevanb\SSH2Bundle\Entity\Profile; use steevanb\SSH2Bundle\Entity\Connection; # create connection profile -$profile = new Profile('host', 'login', 'password'); +$profile = new Profile([ + 'address' => $address, + 'login' => $login, + 'password' => $password +]); # create connection, and connect $connection = new Connection($profile); # or use ssh2 service -$connection = $container->get('ssh2')->connect('host', 'login', 'password'); +$connection = $container->get('ssh2')->connect([ + 'address' => $address, + 'login' => $login, + 'password' => $password +]); # exec command, and return it's output as string $connection->exec('ls -la'); From 292b5aa427a278920b5d06c48844805d913d6705 Mon Sep 17 00:00:00 2001 From: AdriwanKenoby Date: Fri, 2 Mar 2018 11:07:24 +0100 Subject: [PATCH 3/3] correct namespace --- Factory/ConnectionFactory.php | 2 +- Model/Connection.php | 2 +- Model/Profile.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Factory/ConnectionFactory.php b/Factory/ConnectionFactory.php index 366a95c..3609379 100644 --- a/Factory/ConnectionFactory.php +++ b/Factory/ConnectionFactory.php @@ -1,6 +1,6 @@