关于shell:如何在Alpine Linux中修复“因为stdin不是终端而不会分配伪终端”?

How to fix “Pseudo-terminal will not be allocated because stdin is not a terminal” in Alpine Linux?

我正在编写一个运行各种shell命令的PHP程序。有时它需要调用su,并且根据设计,我希望它提示输入提升的特权密码。在PHP中使用passthru()可以很好地解决这个问题。

我选择只为我的程序编写功能测试,因为它依赖于sshsu和其他shell命令。因此,我想在phpunit内部运行真正的东西,看看它是否如我所期望的那样工作。

因为这需要一个ssh服务器和用户帐户配置,所以我已经设置了要在Docker中运行的测试。这种方法看起来会很好——当图像运行时,它会生成phpunit,然后退出。我希望有一种方法可以通过Docker将结果返回到调用系统,如Travis CI。

我选择了AlpineLinux作为我的基本Docker镜像,但是终端分配有问题。我原以为phpunit挡了路(参见这个问题的原始版本),但现在我已经把范围缩小到ssh,要么用php运行,要么甚至只在控制台上运行。

这很好用:

1
su -c whoami

但是,这并不意味着:

1
ssh localhost -t 'su -c whoami'

我得到:

su: must be suid to work properly
Connection to localhost closed.

注意,ssh是通过对localhost的无密码访问(出于测试目的)设置的,所以我希望输入的唯一密码是su密码。

ssh的手册建议-f有助于在需要密码的地方(开关"在命令执行之前请求ssh进入后台"),因此我尝试:

1
ssh localhost -t -f 'su -c whoami'

我得到:

Pseudo-terminal will not be allocated because stdin is not a terminal.
4275760bde94:~$ su: must be suid to work properly

啊,又一个错误!好吧,所以我尝试过这些想法,特别是强迫终端:

1
ssh localhost -tt -f 'su -c whoami'

Double-T仍然对Suid发出投诉,但仍然不起作用。

但是,如果我在我的Ubuntu dev机器上这样做(也配置了对self的ppk访问),它会很好地工作:

1
2
3
4
$ ssh localhost -t 'su -c whoami'
Password:
root
Connection to localhost closed.

这使得它看起来像是阿尔卑斯山或布希堡的OpenSSH有问题。我怎么能更深入地研究这个问题呢?

一种解决方案是换成另一个基础发行版,Ubuntu肯定会工作得很好,但这会大大增加我的Docker图像的尺寸(目前总共有68m的图像)。所以,如果可以的话,我想和阿尔卑斯山一起坚持一段时间。

不太可能是码头工人

我想知道Docker是否会阻止终端的创建或连接,但很快就取消了,因为我可以得到一个与docker exec -it container_name sh交互的shell。此外,我还可以在Docker shell中用两个单独的命令来执行sshsu,这很好。

巴什没用

我注意到Bash在阿尔卑斯山有售,但这也没有起到任何作用,这让我吃惊:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/ $ apk add bash
bash-4.3$ bash
bash-4.3$ su nonpriv
bash-4.3$ ssh localhost whoami
nonpriv
bash-4.3$ ssh localhost 'su -c whoami'
su: must be suid to work properly
bash-4.3$ ssh localhost 'su -s /bin/bash -c whoami'
su: must be suid to work properly
bash-4.3$ ssh -t localhost 'su -s /bin/bash -c whoami'
su: must be suid to work properly
Connection to localhost closed.
bash-4.3$ ssh -tt localhost 'su -s /bin/bash -c whoami'
su: must be suid to work properly
Connection to localhost closed.
bash-4.3$ ssh -tf localhost 'su -s /bin/bash -c whoami'
Pseudo-terminal will not be allocated because stdin is not a terminal.
bash-4.3$ su: must be suid to work properly

bash-4.3$ ssh -ttf localhost 'su -s /bin/bash -c whoami'
bash-4.3$ su: must be suid to work properly
Connection to localhost closed.

尝试壳插值

我发现这几乎是可行的:

1
2
3
4
/ $ ssh -t localhost"$( su -c whoami )"
sh: Password:: not found
sh: root: not found
Connection to localhost closed.

这使用了标准的阿尔卑斯山脉sh外壳,使用了上述链接上的"$()"构造,我不完全理解。它输出Password:,这是su中的提示,然后等待密码。输入密码后,运行whoami,打印root

所以看起来它正在运行这个命令,但我不确定它是否立即运行whoami,然后将它传递给ssh(不是我想要的),或者它是否正在将远程shell传递给localhost,然后再执行它(这是我想要的)。

无论如何,它试图运行stdout行,就好像它们是命令本身一样。我如何才能只打印通过ssh运行的输出?


在我的问题中,我猜想这在Ubuntu容器中是可行的,事实证明这是正确的。有趣的是,我仍然得到"must be run from a terminal"这个命令的错误:

1
su -c whoami

但在这里,终端在ssh无密码命令中显式分配给self:

1
ssh -t localhost 'su -c whoami'

不幸的是,新的操作系统把我68m的图像提高到430m,呃!因此,我应该很高兴收到新的答案,使这项工作阿尔卑斯山。