Как я могу использовать PHP для настройки интерактивного сеанса SSH?

Я пытаюсь установить интерактивное SSH-соединение с удаленным сервером с помощью PHP через командную строку в Mac OS X 10.6. В настоящее время я использую функцию PHP proc_open для выполнения следующей команды:

ssh -t -t -p 22 [email protected]

Это почти работает. Предполагается, что параметры -t -t вызывают псевдотерминал, что они почти и делают. Я могу ввести пароль SSH и нажать Enter. Однако после нажатия Enter терминал просто зависает. Ни вывода, ничего - как будто сеанс SSH не удался. Я не могу запускать команды или что-то еще, и мне приходится убивать все это, используя Ctrl + C. Я знаю, что вход в систему выполнен успешно, потому что я могу выполнить команду типа ssh -t -t -p 22 [email protected] "ls -la" и получить правильный вывод.

Я подумал, что проблема должна быть связана с тем, что я использовал стандартные каналы в своем вызове proc_open, поэтому я заменил их на pty. Я получаю следующую ошибку: "псевдотерминал pty не поддерживается в этой системе..."

Mac OS X просто не поддерживает pty или псевдотерминалы? (Я новичок в использовании всей этой терминологии оболочки).

Вот PHP-код:

$descriptorspec = array(0 => array("pty"), 1 => array("pty"), 2 => array("pty"));  
$cwd = getcwd();  
$process = proc_open('ssh -t -t -p 22 [email protected]', $descriptorspec, $pipes, $cwd);  
if (is_resource($process))  
{  
    while (true)  
    {  
        echo(stream_get_contents($pipes[1]));  
        $status = proc_get_status($process);  
        if (! $status["running"])  
            break;  
    }  
} 

(Извините, я не могу понять инструкции по форматированию SO...)

Что я делаю неправильно? Почему я не могу использовать pty? Это просто невозможно в Mac OS X? Спасибо за вашу помощь!


person Cameron    schedule 05.11.2010    source источник
comment
Я исправил форматирование для вас. Для форматирования блоков кода просто добавьте четыре пробела в начало каждой строки кода. Для форматирования примеров встроенного кода используйте обратные кавычки `.   -  person BoltClock    schedule 05.11.2010
comment
Если вы укажете причину открытия SSH из PHP, возможно, мы сможем найти другие способы выполнить то, что вы пытаетесь сделать.   -  person Halil Özgür    schedule 05.11.2010
comment
Я пытаюсь открыть SSH-соединение в PHP, потому что я работаю над инфраструктурой развертывания, которая будет автоматизировать развертывание кода на удаленном сервере, как это делает Capistrano. Его главная цель — иметь возможность проверять код из репозитория Subversion/Git/Bazaar и автоматизировать другие задачи.   -  person Cameron    schedule 05.11.2010
comment
@battal - Ему не нужна причина, он просто хочет это сделать. Перестаньте думать, что вам нужно знать каждую чертовски не связанную деталь, чтобы ответить на вопрос.   -  person Christian    schedule 24.01.2011


Ответы (6)


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

Подсказка пароля должна использоваться с tty, и я полагаю, что ее намеренно затруднили для использования в противном случае. Также аргумент -t -t вступает в силу только после подключения к удаленному хосту. И я не верю, что функция PHP proc_open() может запускать команду внутри виртуального терминала.

Чтобы настроить аутентификацию с открытым ключом:

# Generate keypair
ssh-keygen -t rsa

# Copy public key to server
scp ~/.ssh/id_rsa.pub example.com:.ssh/authorized_keys

# Now you shouldn't be prompted for a password when connecting to example.com
# from this host and user account.
ssh example.com

# Since the web server (and thus PHP) probably has its own user account...
# Copy the ~/.ssh/id_rsa file somewhere else
cp ~/.ssh/id_rsa /some_path/id_rsa

# Change ownership of the file to the web server account
chown www-data:www-data /some_path/id_rsa

# Fix the file permissions (ssh ignore the keyfile if it is world readable)
chown 600 /some_path/id_rsa

# Try connecting to the server through the web server account
su -c "ssh -i /some_path/id_rsa -o UserKnownHostsFile=/some_path/known_hosts example.com" www-data

# Add the host to the known hosts file when prompted


В качестве альтернативы вы можете использовать plink (часть PuTTY для Linux) вместо OpenSSH, так как он может принимать пароль в командной строке plink -pw password example.com. Но это представляет угрозу безопасности, поскольку любой, кто запускает ps aux на сервере, может увидеть пароль в списке процессов.

Существует также программа под названием sshpass, которая берет пароль из переменной среды или аргумента команды и передает его ssh.

person Community    schedule 05.11.2010
comment
Я бы не рекомендовал последний вариант, когда пароль виден в команде. Любой, кто запускает ps aux на сервере, может видеть ваш пароль. - person Lekensteyn; 05.11.2010
comment
Спасибо, Александр, но это выходит за рамки простого пароля для входа в систему. Я также хотел бы иметь возможность предлагать интерактивный сеанс пользователям моего фреймворка, чтобы они могли отвечать на любые запросы, которые они могут получить от сервера. Логин с открытым ключом великолепен, но не для этой цели. Я просто использую логин SSH в качестве примера. - person Cameron; 05.11.2010
comment
@Cameron Запрос пароля специальный, он не отображается на удаленном компьютере. После подключения вы сможете взаимодействовать с оболочкой, используя $descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => => array("pipe", "w")); в PHP и неблокирующий ввод-вывод. Однако большим ограничением является то, что ресурсы PHP, такие как каналы, не могут быть сериализованы в сеансе (т.е. храниться между HTTP-запросами), поэтому, например, эмулятор веб-терминала невозможен. - person Alex Jasmin; 05.11.2010
comment
Это нормально, Александр - я не пытаюсь сериализовать их в сеансе, на самом деле весь проект основан на командной строке. Проблема в том, что мой терминал в Mac OS X отказывается создавать псевдотерминал, даже если я использую pty вместо pipe. - person Cameron; 06.11.2010
comment
@Cameron Вы не можете использовать 'pty' с proc_open(), для этого есть некоторый код в исходном коде PHP, но если вы посмотрите на строка 65 proc_open.c вы можете видеть, что код отключен с помощью #if 0, поэтому он никогда не собирался. - person Alex Jasmin; 06.11.2010
comment
.... но если вы передадите -t -t в ssh, я не думаю, что вам вообще нужны ptys на стороне PHP. Единственным препятствием для вас должен быть начальный запрос пароля. Я отредактировал свой пост, добавив sshpass в качестве еще одной альтернативы. - person Alex Jasmin; 06.11.2010
comment
Хорошо, спасибо Александр. Я забыл третий элемент массива descriptorspec (тот, что для потока stderr), и теперь он работает более эффективно. Я могу ввести пароль SSH в интерактивном режиме, и он, кажется, входит в систему. Однако по какой-то причине я не могу писать новые команды на стандартный ввод. Я заметил, что функция PHP system() делает все, что мне нужно, кроме предоставления доступа к stdin/out. Есть ли способ использовать system() или мне следует придерживаться proc_open()? - person Cameron; 06.11.2010
comment
Если ваш скрипт должен манипулировать stdin/out/err, вам придется использовать proc_open(). В идеале вы должны просто выполнять удаленные команды, такие как ssh host command с system(), но кажется, что ваши требования более сложны... Вы также можете использовать popen(), если вам нужен только ввод или вывод. - person Alex Jasmin; 06.11.2010

Похоже, проблема лучше всего решается с помощью PHP-функции passthru(). После гораздо более (довольно болезненных) исследований я смог выполнить команду через эту функцию и мог взаимодействовать с удаленным сервером через терминал, как если бы я запускал экспорт ssh и svn вручную (они оба требуют пароли, поэтому были хорошими тестами) . Что мне нужно сделать, так это создать (возможно, очень длинную) строку команд, разделенных &&, и присоединить их к концу команды ssh: ssh -t -t -p 22 hostname command1 && command2 ... Вывод будет отправлен на мой терминал в Mac OS X, даже если команды выполняются на удаленном сервере. Похоже, это решение, которое я искал все время - на самом деле довольно простое! Спасибо всем, кто помог мне в этом. Я поставил Александру «зеленую галочку», потому что он был единственным, кто продолжал отвечать и очень помог в выводе окончательного ответа на проблему. Спасибо Александр!

person Cameron    schedule 06.11.2010

Это старо, но для всех гуглеров вот актуальное решение с использованием proc_open:

Дескрипторы Pty доступны в PHP, но должны быть настроены во время компиляции (см. этот отчет об ошибках 10-летней давности https://bugs.php.net/bug.php?id=33147)

Но в Python, однако, у нас нет этой проблемы. Поэтому вместо прямого запуска команды ssh запустите этот скрипт Python:

import sys
import pty

args = " ".join(sys.argv[1:])
pty.spawn(['/usr/bin/ssh', args])

О pty.spawn из документации по python:

Создайте процесс и соедините его управляющий терминал со стандартным вводом-выводом текущего процесса. Это часто используется, чтобы сбить с толку программы, которые настаивают на чтении с управляющего терминала.

person Magnus Jeffs Tovslid    schedule 06.08.2015
comment
Это прекрасно работает, спасибо. Я использовал его, сохранив ваш фрагмент как ssh.py, а затем в моем php proc_open('python ssh.py username@host', $descriptorSpec, $pipes) - person Daniel Howard; 18.09.2018

Пробовали ли вы расширение PHP SSH2?

person Anti Veeranna    schedule 05.11.2010
comment
Я пробовал расширение SSH2 безрезультатно. Он не предоставит мне интерактивный сеанс через каналы или pty. Я могу выполнять команды и получать результат, но все, что связано с вводом, работать не будет (например, все, что требует пароля). - person Cameron; 05.11.2010

Пробовали ли вы phpseclib, реализацию SSH на чистом PHP?:

<?php
include('Net/SSH2.php');

$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
    exit('Login Failed');
}

echo $ssh->read('username@username:~$');
$ssh->write("ls -la\n");
echo $ssh->read('username@username:~$');
?>
person neubert    schedule 18.10.2012

Я написал ssh-клиент на php с расширением ssh2, вы можете посмотреть исходный код на странице github https://github.com/roke22/PHP-SSH2-Web-Client

Пожалуйста, отправьте отзыв.

person Roke    schedule 20.07.2018