如何解析 ssh 远程命令行参数

onl*_*one 14 shell bash process ssh quoting

我已经看到了关于需要对远程 ssh 命令的参数进行双重转义的问题和答案。我的问题是:第二次解析究竟在何时何地完成?

如果我运行以下命令:

$ ssh otherhost pstree -a -p
Run Code Online (Sandbox Code Playgroud)

我在输出中看到以下内容:

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p
Run Code Online (Sandbox Code Playgroud)

远程命令 ( pstree)的父进程是sshd,那里似乎没有任何外壳程序可以解析远程命令的命令行参数,因此似乎不需要双引号或转义(但它绝对是)。相反,如果我先在那里 ssh 并获得登录 shell,然后运行,pstree -a -p我会在输出中看到以下内容:

  ??sshd,3736
  ?   ??sshd,3733
  ?       ??sshd,3735
  ?           ??bash,3737
  ?               ??pstree,4130 -a -p
Run Code Online (Sandbox Code Playgroud)

很明显,那里有一个bashshell 可以在这种情况下进行命令行解析。但是我直接使用远程命令的情况,好像没有shell,那为什么还要双引号呢?

Gil*_*il' 27

总是有一个远程shell。在 SSH 协议中,客户端向服务器发送一个字符串来执行。SSH 命令行客户端获取其命令行参数并使用参数之间的空格将它们连接起来。服务器获取该字符串,运行用户的登录 shell 并将该字符串传递给它。(更准确地说:服务器运行在用户数据库中注册为用户外壳的程序,向它传递两个命令行参数:-c和客户端发送的字符串。外壳不会作为登录外壳调用:服务器不会将第零个参数设置为以-.开头的字符串。)

绕过远程shell是不可能的。该协议没有任何类似发送可以在服务器上解析为 argv 数组的字符串数组的功能。并且 SSH 服务器不会绕过远程 shell,因为这可能是一个安全限制:使用受限程序作为用户的 shell 是一种提供仅允许运行某些命令的受限帐户(例如,仅 rsync 帐户或一个仅限 git 的帐户)。

您可能看不到外壳,pstree因为它可能已经消失了。许多 shell 有一个优化,如果它们检测到他们将要执行“运行此外部命令,等待它完成,然后以命令的状态退出”,则 shell 会运行“此外execve部命令”。这就是您的第一个示例中发生的情况。对比以下三个命令:

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'
Run Code Online (Sandbox Code Playgroud)

前两个是相同的:客户端向服务器发送完全相同的数据。第三个发送一个 shell 命令,它破坏了 shell 的 exec 优化。

  • 哈!不敢相信你打败了我来回答我自己的问题。我在发布问题的中途想通了,并认为我应该自己进行提问和回答。 (2认同)

onl*_*one 10

我想我想通了:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001
Run Code Online (Sandbox Code Playgroud)

参数pstree是:显示命令行参数、显示 pid 和仅显示给定 pid 的父进程。这'$$'是一个特殊的 shell 变量,当 bash 评估命令行参数时,bash 将用它自己的 pid 替换它。它被引用一次以阻止它被我的本地 shell 解释。但是它没有被双重引用或转义以允许它被远程 shell 解释。

正如我们所看到的,它被替换为12001所以这是 shell 的 pid。从输出我们也可以看出:pstree,12001pid为12001的进程就是pstree本身。那么pstree外壳呢?

我收集到的bash是正在被调用并且正在解析命令行参数,但随后它会调用exec以用正在运行的命令替换自身。

似乎它只在单个远程命令的情况下执行此操作:

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我要求运行两个命令:pstree后跟echo. 我们可以在这里看到,bash它实际上作为pstree.