1

我在 Gentoo Linux 的 wiki 中遇到了以下关于动态 jumphost 列表的内容

    ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') nc -w1 $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')

它有效,但我想sed完全理解这个表达。

阅读其原始参考资料,我能够很好地理解使用该Host *+*模式的命令的递归调用。但我有两个问题:

  1. 表达式使用%%. 为了了解原因,我使用ssh -v了 ,并观察到当ssh客户端解析时$HOME/.ssh/config,似乎第一个%被剥离了。试图确认上述内容,我下载了openssh 源代码,但readconf.c没有给我任何线索。我是 OpenSSH 源代码的新手,但不怕用调试信息和它来编译gdb它。不过,如果有更快的方法来证实我的猜想,我将不胜感激。
  2. ssh -v还透露:

    [...]
    debug1: Executing proxy command: exec ssh $(echo zackp%node0+zackp%node1+node3 | sed 's/+[^+]*$//;s/\\([^+%]*\\)%\\([^+]*\\)$/\\2 -l \\1/;s/:/ -p /')
    [....]
    

    \(现在\在子shell 中用a 转义。为什么这是必要的?

谢谢,

——扎克

4

1 回答 1

2

好问题。这是一个相当曲折的命令!听起来你已经掌握了它。在您的机器上,主机字符串已剥离了一个加号分隔的跃点;为方便起见,该令牌然后将任何端口和用户提取并转换为选项(-l-p)。最后,将其他跃点的信息弹出到一个字符串中传递给 netcat。您机器上的 ssh 建立一个连接,并在其目标机器上使用包含有关剩余跃点信息的字符串执行 netcat。然后相同的过程再次在那里发生,依此类推,直到所有跃点完成,每个中继上运行一个 netcat 实例来转发流量。相当整洁的命令行乐趣!

您的具体问题:

  1. 为什么 % 符号被转义了?这是特定于ProxyCommand选项的!从有关以下内容的手册页ProxyCommand

    在命令字符串中,任何出现的“%h”都将替换为要连接的主机名,“%p”替换为端口,“%r”替换为远程用户名。

    像所有表现良好的 unix 实用程序一样,当出现元字符时,很自然的事情是使用该字符加倍来表示文字。否则,就无法表示某些字符串!它可能只是程序员出于整洁而添加的,没有想到有人会使用 % 为跳转列表编写自己的迷你语法并将其发布在 Gentoo wiki 上!

    % 代码特定于此选项,因此转义可能隐藏在 OpenSSH 源中处理该选项的位置附近。

  2. 棘手的问题!指定为 ProxyCommand 选项的字符串不是将直接传递给 ssh 的命令字符串;它是专门“使用用户的外壳”执行的。因此,该选项中的内容是对用户友好的,因此您可以在 ssh.conf 中输入您在 shell 中输入的内容。

    现在,大多数人(包括我!)对 100% 精确的日志记录并不太在意,但是 OpenBSD 的家伙有一个 strnvis 函数,OpenSSH 在输出之前传递所有日志字符串。它对控制字符和其他讨厌的字符进行编码,以便日志输出提供字符串日志记录函数传入的精确(无空)缓冲区的可读记录。这很棒,但唯一的窍门是,在阅读日志时,您必须将其“srunvis”回其原始形式。

    基本上,反斜杠是他们的日志格式的一个奇怪之处。它不会传递给外壳。

    现在,我在这里猜测(我认为不值得深入研究!),但基本上问题在于日志记录 ssh 的输出在冗长时会吐出。我之前写过用于进程启动的日志记录,考虑到参数可能有多复杂(嵌入换行符?尾随空格?疯狂引号?),这有点草率。您通常不需要 100%“准确”的方式将参数无损地记录到 exec,因为它太乏味了。看起来这里的 OpenSSH 代码的作者,在寻找要记录的单个字符串时,只是吐出他方便作为最后一个参数传递给 sh 的字符串的转义形式。这不是将要执行的内容的“完美”表示(因为我怀疑一些空格会在日志记录中丢失)并且它'

于 2012-08-25T23:54:07.487 回答