强制 OpenSSH 服务器为简单命令生成新的 shell

强制 OpenSSH 服务器为简单命令生成新的 shell

我从安装了 OpenSSH 服务器的 Ubuntu 构建了一个 docker 镜像。假设我通过 ssh 请求一个简单的命令

ssh [email protected] "sleep 10"

然后,ps aux --forest容器内部给出了以下内容:

SER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         18  0.3  0.0  18508  3500 pts/0    Ss   16:12   0:00 bash
root         37  0.0  0.0  34404  2884 pts/0    R+   16:12   0:00  \_ ps aux --forest
root         16  0.0  0.0  72300  3280 ?        Ss   16:12   0:00 /usr/sbin/sshd
root         34  0.0  0.0  74656  6648 ?        Ss   16:12   0:00  \_ sshd: root@notty
root         36  0.0  0.0   4532   744 ?        Ss   16:12   0:00      \_ sleep 1000

但是当我执行更复杂的命令时

ssh [email protected] "sleep 1000; sleep 1"

它现在生成一个 bash shell 并将我的命令传递给它:

root         18  0.1  0.0  18508  3500 pts/0    Ss   16:12   0:00 bash
root         43  0.0  0.0  34404  2896 pts/0    R+   16:13   0:00  \_ ps aux --forest
root         16  0.0  0.0  72300  3280 ?        Ss   16:12   0:00 /usr/sbin/sshd
root         39  0.0  0.0  74656  6712 ?        Ss   16:13   0:00  \_ sshd: root@notty
root         41  0.0  0.0   9920  1312 ?        Ss   16:13   0:00      \_ bash -c sleep 1000; sleep 1
root         42  0.0  0.0   4532   776 ?        S    16:13   0:00          \_ sleep 1000

那么,哪个部分决定是否调用 shell?这由 SSHd 控制吗?如果是这样,有没有办法强制 SSHd 始终调用 shell?

PS 我知道在 Ruby 中,Kernel.exec该函数是根据元字符(如;和)选择生成或不生成 shell 的&,所以也许在我的情况下,选择不是在应用程序级别做出的?

答案1

据我所知,sshd它不执行此类优化。如果它执行了,那就没什么意义了,因为sshd它应该使用特定于用户的命令解释器,通常它可以是任何东西(不一定是真正的 shell)。即使解释器是/bin/bash,它也可能是经过修改的bash,在启动时会做一些不寻常的事情。因为sshd无法知道这一点,所以它真的不应该干扰。

无论分配给用户什么命令解释器,它总是会生成。在你的情况下是bash

您观察到的是 Bash 本身的优化。在某些情况下,如果 Bash 能够判断在执行最后一个命令期间和之后绝对没有什么可做的,那么它将执行exec最后一个命令而不是正常运行它。这意味着该命令将替换bash而不创建新进程。

在您的例子中bash,是 的子级sshd。当它用某个命令替换自身时,该命令将成为子级。这给人一种印象,即 shell 从未存在过。


请参阅以下示例(使用 Bash 4.4.20 测试):

  1. bash -c '
       exec sleep 5
    ' & sleep 1; ps -p "$!"

    您会看到,sleep因为exec。这是意料之中的。

  2. bash -c '
      sleep 5
    ' & sleep 1; ps -p "$!"

    也是sleep因为优化。

  3. bash -c '
       sleep 5
       true
    ' & sleep 1; ps -p "$!"

    这次是bash。shell 无法隐式执行,exec sleep 5因为稍后还要true运行。

  4. bash -c '
       sleep 5
       sleep 4
    ' & sleep 1; ps -p "$!"; sleep 5; ps -p "$!"

    首先你会看到bash;这是第一个sleep运行时的情况。然后你会看到(第二个)sleep具有相同的 PID。

  5. 这有点有趣(特别是与上面的相比):

    bash -c '
       sleep 5; sleep 4
    ' & sleep 1; ps -p "$!"; sleep 5; ps -p "$!"

    这次bash没有exec第二个sleep,尽管代码看起来与前一个相同。我不知道这是否只是缺乏优化,或者有一个不明显的原因exec

  6. 最后:

    bash -c '
       陷阱“echo foo”SIGUSR1
       睡 5
    '&睡眠1;ps -p“$!”

    你会看到bash。shell 知道它需要保留以防信号到达,因此它不会用 替换自己sleep 5

相关内容