Использование SSH и SFTP с языком PHP

В современном мире, где так много сторонних компонентов и программ для создания совместного доступа, важно понимать и использовать протоколы SCP и SFTP. Для PHP есть расширение-оболочка для библиотеки libssh2, которая реализует протокол SSH2. Она обеспечивает несколько функций, которые можно использовать для безопасной передачи файлов.
Перед тем как приступить к использованию этих функций, нужно установить пакет SSH2. Поскольку это PECL-компонент, процесс установки будет зависеть от вашей операционной системы. Следуйте инструкциям на php.net.

Установление соединения

Давайте начнем с подключения к службе SSH. Установление соединения выглядит просто:

$conn = ssh2_connect('example.com', 22);
ssh2_auth_password($conn, 'username', 'password');

Некоторые администраторы предпочитают использовать открытые и закрытые ключи для аутентификации входа в систему. Если вы хотите подключиться таким образом, нужно использовать следующий код:

$conn = ssh2_connect('example.com', 22);
ssh2_auth_pubkey_file(
        $conn,
        'username',
        '/home/username/.ssh/id_rsa.pub',
        '/home/username/.ssh/id_rsa'
);

Если при аутентификации вы используете имя пользователя или пароль, открытый или закрытый ключ, функции ssh2_auth_password () и ssh2_auth_pubkey_file () возвращают логическое значение, указывающее на то, была ли осуществлена проверка подлинности успешно.

Выполнение основных команд

После того как вы успешно прошли аутентификацию с сервером, вы можете совершить операцию передачи файлов. Функция SCP позволяет вам отправлять или получать файлы следующим образом:

// send a file
ssh2_scp_send($conn, '/local/filename', '/remote/filename', 0644);
// fetch file
ssh2_scp_recv($conn, '/remote/filename', '/local/filename');

При копировании файла на удаленный сервер с помощью дополнительного параметра в функции ssh2_scp_send () можно установить права доступа.
Большей функциональности можно добиться с использованием функции SFTP. Вы можете изменять права к файлу или директории, получать информацию о файле, создавать каталоги, переименовывать элементы, удалять элементы и т.д. Функция SFTP очень похожа на SCP, но дополнительное подключение через ssh2_sftp () должно быть сделано до начала использования функции:

$sftp = ssh2_sftp($conn);
// Create a new folder
ssh2_sftp_mkdir($sftp, '/home/username/newdir');
// Rename the folder
ssh2_sftp_rename($sftp, '/home/username/newdir', '/home/username/newnamedir');
// Remove the new folder
ssh2_sftp_rmdir($sftp, '/home/username/newnamedir');
// Create a symbolic link
ssh2_sftp_symlink($sftp, '/home/username/myfile', '/var/www/myfile');
// Remove a file
ssh2_sftp_unlink($sftp, '/home/username/myfile');

Посредством ssh2_sftp () устанавливается подключение к ресурсу, и затем все операции проходят через SFTP-соединение, которое использует вызовы различных ssh2_sftp_* функций. Вызов функции возвращает логическое значение, что позволяет определить, было ли действие успешным.

Использование функции-оболочки

Когда определенной функции управления файлами для SFTP или SCP-протокола не существует, на помощь приходит оболочка. Ниже приведено несколько примеров:

// Create a new folder
mkdir('ssh2.sftp://' . $sftp . '/home/username/newdir');
// Remove the new folder
rmdir('ssh2.sftp://' . $sftp . '/home/username/newdir');
// Retrieve a list of files
$files = scandir('ssh2.sftp://' . $sftp . '/home/username');

Перед выполнением любой из этих операций соединение с сервером SSH и SFTP должно быть установлено, так как используется ранее созданная переменная $sftp.

Собираем все вместе

Теперь, когда вы научились подключаться, проверять аутентификацию и выполнять команды на сервере SSH, мы можем создать несколько вспомогательных классов для упрощения процесса выполнения этих команд: один — для выполнения SCP вызовов, один — для вызовов SFTP, родительский класс для общей функциональности и пару классов для инкапсуляции идентификационной информации (паролей и ключей).
Давайте создадим класс аутентификации первым, так как он будет использоваться другими классами:

abstract class SSH2Authentication
{
        protected $connect;
        protected $username;
       
        public function setConnect($connect){
                $this->connect = $connect;
        }
       
        public function authenticate(){
                throw new Exception('Method "authenticate" must be defined.');
        }
}
class SSH2Password extends SSH2Authentication
{
        protected $password;
        public function __construct($username, $password) {
                $this->username = $username;
                $this->password = $password;
        }
       
        public function authenticate(){
                return ssh2_auth_password(
                        $this->connect,
                        $this->username,
                        $this->password
                );
        }
}
class SSH2Key extends SSH2Authentication
{
        protected $publicKey;
        protected $privateKey;
        public function __construct($username, $publicKey, $privateKey) {
                $this->username = $username;
                $this->publicKey = $publicKey;
                $this->privateKey = $privateKey;
        }
       
        public function authenticate($passphrase=''){
                return ssh2_auth_pubkey_file(
                        $this->connect,
                        $this->username,
                        $this->publicKey,
                        $this->privateKey,
                        $passphrase
                );
        }
}

SSH2Password и SSH2Key просто оборачивают соответствующие им данные аутентификации. Они имеют общий базовый класс, поэтому мы можем воспользоваться PHP для перехода от источника к получателю.
Двигаемся дальше. Давайте создадим протокол SSH2 для подключения и аутентификации на SSH сервере.

class SSH2
{
        protected $connect;
        public function __construct($host, SSH2Authentication $auth, $port = 22) {
                $this->connect = ssh2_connect($host, $port);
                $auth->setConnect($this->connect);
                if ($auth->authenticate() === false) {
                        throw new Exception('SSH2 login is invalid.');
                }
        }
}

Будет создан очень простой SCP-класс, который расширяет функционал SSH2 и использует метод __call(). Это позволяет нам сделать две важные вещи: автоматически предварять «ssh_scp_» при вызове функции и поддерживать связь с переменной.

class SSH2SCP extends SSH2
{
        public function __call($func, $args) {
                $func = 'ssh2_scp_' . $func;
                if (function_exists($func)) {
                        array_unshift($args, $this->conn);
                        return call_user_func_array($func, $args);
                }
                else {
                        throw new Exception($func . ' is not a valid SCP function.');
                }
        }
}

Класс SFTP схож в плане конструкции, хотя и перегружен вызовом функции ssh2_sftp (). Результаты будут храниться в защищенной переменной и автоматически добавляться ко всем вызовам SFTP-функции.

class SSH2SFTP extends SSH2
{
        protected $sftp;
        public function __construct($host, SSH2Authentication $auth, $port = 22) {
                parent::__construct($host, $auth, $port);
                $this->sftp = ssh2_ftp($this->conn);
        }
       
        public function __call($func, $args) {
                $func = 'ssh2_sftp_' . $func;
                if (function_exists($func)) {
                        array_unshift($args, $this->sftp);
                        return call_user_func_array($func, $args);
                }
                else {
                        throw new Exception($func . ' is not a valid SFTP function.');
                }
        }
}

Эти классы могут быть использованы для вызова SCP- и SFTP-функций. Благодаря полезному методу __call в обоих классах нет необходимости заново открывать соединение или повторно вводить «ssh2_scp_» либо «ssh2_ftp_» при каждом вызове.

// Create SCP connection using a username and password
$scp = new SCP(
        'example.com',
        new SSH2Password('username', 'password')
);
// Receive a file via SCP
if ($scp->recv('remote/file', 'local/file')) {
        echo 'Successfully received file';
}
       
// Create SFTP connection using a public/private key
$sftp = new SSH2SFTP(
        'example.com',
        new SSH2Key('username', 'public_key', 'private_key')
);
// Create a directory via SFTP
if ($sftp->mkdir('directory/name')) {
        echo 'Successfully created directory';
} 

 

Заключение

Установка расширения SSH2 для PHP нужна для того, чтобы обеспечить выполнение скриптов с возможностью подключения к SSH2-серверу. У вас есть выбор: либо опираться на удобные классы, которые упрощают код создания SFTP- или SCP-функций, либо, если конкретная функция не предусмотрена библиотекой, пользоваться функциональностью SSH2-оболочки. При этом вам доступно большинство основных операций с файловой системой.