一种查看 sshd 到底执行什么命令的方法?

一种查看 sshd 到底执行什么命令的方法?

我正在考虑通过 ssh 将环境变量传递给命令:

$ ssh HOST A=1 set | grep ^A
$ ssh HOST A=1 sh -c set | grep ^A
A='1'
$ ssh HOST A=1 echo '$A'
$ ssh HOST A=1 sh -c 'echo $A'

因此,set看不到环境变量,但它在单独的进程中看到。似乎没有发生同样的情况echo

有没有办法找出到底发送了什么,以及它是如何执行的(sshd启动哪个命令)?

答案1

当您运行 时ssh user@host command,由 扩展产生的标记command将通过中间的空格连接起来ssh并作为字符串发送到远程主机。然后,远程主机执行远程用户的默认 shell 的一个实例(在 中指定的实例/etc/passwd),将从客户端接收到的字符串作为参数传递给 shell 的-c选项(下面将详细介绍)。

因此,例如,在

$ foo="bar    baz"
$ ssh user@host echo "$foo"
bar baz

本地ssh接收两个参数echobar baz。它将它们连接起来形成字符串echo bar baz并将其发送到服务器。服务器按照以下方式执行某些操作/bin/sh -c 'echo bar baz',并且该命令的输出打印在本地ssh的标准输出上。

如何查看客户端实际发送到服务器的内容?

作为卡米尔·马乔罗夫斯基指出在问题的评论中,ssh -v将发送到服务器的命令字符串作为调试消息输出:

$ ssh -v host A=1 sh -c set | grep ^A
...
debug1: Sending command: A=1 sh -c set
...

您如何才能看到sshd实际执行的内容?

我不知道有什么方法可以请求服务器向客户端显示其自身操作的某种跟踪。但是,出于测试目的,您可以使用strace守护程序的实例:

$ sudo strace -e execve -f /usr/bin/sshd -p 2222

这特别有用,因为在编写用于远程执行的命令行时很容易无法正确引用。举个例子(您问题中提供的示例的变体):

$ ssh -p 2222 host bash -c 'A=1 set'
                                        # Prints nothing

strace揭示了我们所看到的命令字符串实际上是在远程主机上分割的,它首先在用户的默认 shell 中运行完整的命令行(当本地 shell 构建参数列表时,单引号已被删除ssh):

... execve("/bin/bash", ["bash", "-c", "bash -c A=1 set"] ...

进而:

... execve("/usr/bin/bash", ["bash", "-c", "A=1", "set"] ...

请注意命令字符串( 的参数-c)恰好是A=1。作为第零个参数set传递bash(可用作$0并用作命令名称)。

如何查看远程shell实际执行了什么?

再说一遍,我不知道有什么简单的方法。在简单的情况下,shell 选项的使用xtrace很简单。例如,ssh host A=1 set可能会变成:

$ command=$(printf '%s ' A=1 set); printf '%s\n' "${command% }" |
  ssh host sh -x

sh应替换为远程用户的默认 shell。

该命令通过管道传输到ssh的标准输入,因为这种形式允许不加更改地键入它(ssh host sh -xc A=1 set由于上面所示的原因而不起作用,并且添加引号在某种程度上会破坏本练习的真正目的)。

在不太简单的情况下,特别是当ssh您正在研究的调用已经使用标准输入时,您可以强制远程主机在xtrace打开的情况下运行 shell。出于实验目的,并明确错误可能会使您无法登录到远程系统,一种(相当粗略的)方法可能是替换 中远程用户的默认 shell /etc/passwd。假设它是/bin/sh,将其更改为/home/your_user/bin/sh并将后者创建为可执行脚本:

#!/bin/sh
exec /bin/sh -x "$@"

这将使 shell 向您显示正在执行的内容的踪迹:

$ ssh host A=1 echo foo
foo
+ A=1
+ echo foo

最后,关于您的示例命令:

  • ssh HOST A=1 set不打印的原因A=1可能是您的用户的默认 shell 是 Bash(可能作为 调用sh)。与dashkshyash、中发生的情况相反zshset在 Bash (至少是我拥有的版本5.0.17)中,不输出变量集对于set命令本身。另一方面,您可能可以确认:

    $ ssh host 'A=1
    > set' | grep ^A
    A=1
    

    如果在调用之前声明了 Ie,则它A会显示在set的输出中set

  • 在 中A=1 sh -c setA被添加到 的环境中sh,因此毫不奇怪地由 打印set

  • A=1 echo '$A'不打印 的值,A因为仅在 扩展后A添加到执行环境(假设它是内置的) 。将其与打印的进行比较。echo$Assh host 'A=1; echo $A'1

  • A=1 sh -c 'echo $A'不打印任何内容,因为如上所示,远程主机使用选项-cA=1 sh -c echo $A作为参数执行远程用户的默认 shell,然后该 shell 运行sh -c echo $A:命令字符串最终只是echo,没有参数。

    为了使其工作,您需要确保周围的引号echo $A发送到远程主机 $A防止过早扩展(通过本地 shell 或“外部”远程 shell):例如ssh host A=1 sh -c '"echo \$A"'ssh host A=1 sh -c "'echo \$A'"

    当显式调用远程 shell 时,让它从标准输入读取命令可能更容易:

    $ echo 'echo $A' | ssh host A=1 sh -s
    1
    

也可以看看

相关内容