如何使用PHP建立一个交互式的SSH会话?

4
我正在尝试通过Mac OS X 10.6上的命令行使用PHP建立与远程服务器的交互式SSH连接。我目前正在使用PHP的proc_open函数执行以下命令:
ssh -t -t -p 22 user@server.com

这个几乎可以工作。 -t -t选项应该强制使用伪终端,它们几乎实现了这个功能。我能够输入SSH密码并按回车键。但是,在按下回车键后,终端似乎会挂起。没有输出,什么都没有-就像SSH会话失败了一样。我无法运行命令或任何其他操作,只能使用Ctrl + C杀死整个进程。我知道登录成功,因为我可以执行像ssh -t -t -p 22 user@server.com "ls -la"这样的命令并获得正确的输出。

我认为问题可能与我在proc_open调用中使用标准管道有关,因此我将它们替换为pty。我收到以下错误:"此系统不支持pty伪终端..."

Mac OS X是否根本不支持pty或伪终端?(我在使用所有这些shell术语方面还比较新手)。

这里是PHP代码:

$descriptorspec = array(0 => array("pty"), 1 => array("pty"), 2 => array("pty"));  
$cwd = getcwd();  
$process = proc_open('ssh -t -t -p 22 user@server.com', $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上是不可能的吗?谢谢你的帮助!


我已经为您修复了格式。要格式化代码块,只需在每行代码的开头添加四个空格即可。要格式化内联代码示例,请使用反引号```。 - BoltClock
如果您指定了从PHP打开SSH的原因,也许我们可以找到其他实现您尝试做的事情的方法。 - Halil Özgür
我正在尝试在PHP中打开一个SSH连接,因为我正在开发一个部署框架,可以自动将代码部署到远程服务器,类似于Capistrano的功能。它的主要目标是能够从Subversion/Git/Bazaar仓库中检出代码并自动化其他任务。 - Cameron
3
@battal - 他不需要理由,他只是想这么做。停止那种必须知道每一个不相关的细节才能回答问题的心态。 - Christian
6个回答

5

你应该使用公钥认证,而不是试图编程绕过交互式密码认证。

密码提示应该从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 for Linux的一部分)代替OpenSSH,因为它可以在命令行上输入密码plink -pw password example.com。但是这样做存在安全风险,因为任何在服务器上运行ps aux的人都可以在进程列表中看到密码。

还有一个名为sshpass的程序,它从环境变量或命令参数中获取密码并将其传递给ssh


我不建议最后一个选项,即在命令中显示密码。任何在服务器上运行 ps aux 的人都可以看到你的密码。 - Lekensteyn
没问题,Alexandre - 我并不是试图在会话中序列化它们,事实上整个项目都是基于命令行的。问题是,即使我使用“pty”代替“pipe”,我的Mac OS X终端仍然拒绝创建伪终端。 - Cameron
...但是如果你在ssh中传递了-t -t,我认为PHP端根本不需要ptys。你唯一的障碍应该是初始密码提示。我编辑了我的帖子,添加了sshpass作为另一个选择。 - Alex Jasmin
好的,谢谢Alexandre。我忘记了descriptorspec数组的第三个元素(stderr流的元素),现在它的效果更好了。我可以交互式地输入SSH密码,看起来已经成功登录了。但是,由于某种原因,我无法向stdin写入新命令。我注意到PHP的system()函数可以完成我需要的所有操作,除了提供对stdin/out的访问。是否有一种方法可以使用system(),还是应该坚持使用proc_open()? - Cameron
如果您的脚本需要操作stdin/out/err,您将不得不使用proc_open()。理想情况下,您可以使用system()执行远程命令,例如ssh host command,但似乎您的要求更为复杂... 如果您只需要输入或输出,也可以使用popen() - Alex Jasmin
显示剩余3条评论

4
看起来最好的解决方法是使用PHP的passthru()函数。 经过更多(相当痛苦的)研究,我能够通过这个函数发出一个命令,并且可以像手动运行ssh和svn export一样通过终端与远程服务器进行交互(它们都需要密码,因此是很好的测试)。 我将不得不构建一串由&&分隔的(潜在非常长的)命令字符串,并将它们附加到ssh命令的末尾:ssh -t -t -p 22 hostname command1 && command2 ... 即使这些命令在远程服务器上执行,输出也将发送到我的Mac OS X终端。 看起来这就是我一直在寻找的解决方案 - 确实非常简单! 感谢所有帮助我的人。 我给Alexandre打了“绿色复选标记”,因为他是唯一一直回应并在推断问题的最终答案方面非常有帮助的人。 谢谢Alexandre!

3

这篇文章可能有些旧,但对于任何正在搜索的人来说,这里提供了一个使用proc_open的实际解决方案:

PHP中可用Pty描述符,但必须在编译期间进行配置(参见这个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])

关于python文档中的pty.spawn:

生成一个进程,并将其控制终端连接到当前进程的标准io。这通常用于困扰那些坚持从控制终端读取的程序。


1
非常好用,谢谢。我把你的代码片段保存为ssh.py,然后在我的PHP中使用proc_open('python ssh.py username@host', $descriptorSpec, $pipes) - Daniel Howard

1

你尝试过phpseclib,一个纯PHP SSH实现吗?

<?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:~$');
?>

1

我尝试了SSH2扩展,但无济于事。它不会通过管道或pty为我提供交互式会话。我可以执行命令并获得输出,但任何需要输入的内容都无法工作(例如需要密码的任何内容)。 - Cameron

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接