7

我正在尝试通过使用ssh-add添加新密钥来控制系统ssh-agent。为此,我使用了Symfony Process组件。

当我从网站运行此代码时,它工作得非常好,但是当我在 shell/控制台中运行相同的代码时,ssh-add 进程挂起Enter passphrase for <path to key>:

代码的简化版本看起来像这样

use Symfony\Component\Process\Process;

$keyPath = '<path to key>';
$keyPassword = '<password for unlocking the key>';
$socketPath = '<path to ssh-agent socket>';

$sshAdd = new Process(
    "ssh-add {$keyPath}",
    null,
    [
        'SSH_AUTH_SOCK' => $socketPath
    ],
    $keyPassword
);
$sshAdd->run();

正如您在上面的代码中看到的那样,我调用,在环境中ssh-add设置 ,以便与代理交谈,然后在输入中发送密码。正如我之前所说,当我在 web 上下文中运行它时,它可以工作,但它挂在 shell/console 上下文中。SSH_AUTH_SOCKssh-add

strace在控制台中运行时做了一个,相关部分看起来像这样

open("<path to key>", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(4, "<key password>", <length of password>)      = 20
close(4)                                              = 0
wait4(9650, 0x7fff00ab3554, WNOHANG|WSTOPPED, NULL)   = 0
select(8, [5 7], [], [], {0, 0})                      = 0 (Timeout)
wait4(9650, 0x7fff00ab3554, WNOHANG|WSTOPPED, NULL)   = 0
select(8, [5 7], [], [], {0, 0})                      = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000}Enter passphrase for <path to key>:) = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
...

如您所见,写入似乎被忽略了,ssh-add程序开始阻塞等待输入。

4

1 回答 1

8

在阅读了 ssh-add 的源代码并阅读了 Wez Furlong 的这篇非常古老的文章后,终于找到了解决这个问题的方法,他谈到了向 PHP 添加 PTY 支持。

引用文章:

这样做类似于为进程创建管道,而是使用操作系统的 /dev/ptmx 接口创建主(用于您的脚本)和从属(用于您正在运行的进程)pty 句柄。这允许您向显式打开 /dev/tty 的应用程序发送数据并从中捕获数据——这通常在交互式提示输入密码时完成。

事实证明 Symfony Process 也支持 PTY,所以原始代码只需要几处更改。首先,我需要通过调用来指定我想使用 PTY 而不是管道setPty(true)。然后我需要模拟用户在输入密码后按下ENTER,只需在输入中添加换行即可。

最终代码看起来像这样(在更改的行上有注释)

use Symfony\Component\Process\Process;

$keyPath = '<path to key>';
$keyPassword = '<password for unlocking the key>';
$socketPath = '<path to ssh-agent socket>';

$sshAdd = new Process(
    "ssh-add {$keyPath}",
    null,
    [
        'SSH_AUTH_SOCK' => $socketPath
    ],
    $keyPassword . "\n"   // Append a line feed to simulate pressing ENTER
);
$sshAdd->setPty(true);   // Use PTY instead of the default pipes
$sshAdd->run();
于 2014-11-25T18:38:42.907 回答