我在Gentoo Linux's wiki about dynamic jumphost list中遇到了以下内容:
ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') nc -w1 $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')
它有效,但我想完全理解sed
表达式。
阅读original reference,我能够使用Host *+*
模式很好地理解命令的递归调用。但我有两个问题:
%%
。为了了解原因,我使用了ssh -v
,并观察到当ssh
客户端解析$HOME/.ssh/config
时,似乎第一个%
被剥离了。尝试确认上述内容后,我下载了openssh source codes,但readconf.c
没有给我一些线索。我是OpenSSH源代码的新手,但我不怕用调试信息编译它,gdb
它。然而,如果有更快的方法来证实我的猜想,我将不胜感激。 ssh -v
还透露:
[...]
debug1: Executing proxy command: exec ssh $(echo zackp%node0+zackp%node1+node3 | sed 's/+[^+]*$//;s/\\([^+%]*\\)%\\([^+]*\\)$/\\2 -l \\1/;s/:/ -p /')
[....]
即。现在\(
已在子shell中使用\
进行转义。为什么这是必要的?
谢谢,
- 扎克
答案 0 :(得分:2)
好问题。这是一个非常曲折的命令!听起来你已经非常了解它。在您的机器上,主机字符串中有一个正分离的跳跃被剥离;为方便起见,该令牌随后有任何端口和用户被提取并转换为选项(-l
和-p
)。最后,有关其他跃点的信息会弹出到字符串中以传递给netcat。您的计算机上的ssh建立一个连接,并在其目标计算机上执行netcat,其中包含有关剩余跃点的信息的字符串。然后在那里再次发生相同的过程,依此类推,直到完成所有跳,并在每个中继上运行netcat实例以转发流量。非常整洁的命令行乐趣!
您的具体问题:
为什么%符号会被转义?这是ProxyCommand
选项特有的!从有关ProxyCommand
的手册页:
在命令字符串中,出现'%h' 将由主机名替换为'%p'连接 端口,以及远程用户名的'%r'。
与所有表现良好的unix实用程序一样,当有一个元字符时,自然就是使用该字符加倍来表示文字。否则,无法表示某些字符串!程序员可能只是简单地添加它,而不是认为有人会使用%为跳转列表编写自己的迷你语法并将其发布在Gentoo wiki上!
%代码特定于此选项,因此转义可能隐藏在OpenSSH源中处理选项的位置附近。
提出疑问!指定为ProxyCommand选项的字符串不是将直接传递给ssh的命令字符串;它是专门用“使用用户的shell”执行的。因此,选项中的内容是用户友好的,因此您可以在ssh.conf中输入您在shell中键入的内容。
现在,大多数人(包括我!)对于100%精确的日志记录并不太感兴趣,但OpenBSD的人有一个strnvis函数,OpenSSH在输出之前会传递所有日志字符串。它对控制字符和其他恶意进行编码,以便日志输出提供字符串日志记录函数传入的精确(无空)缓冲区的可读记录。这很棒,但唯一的诀窍是,在阅读日志时,你必须'将它''恢复原状。
基本上,反斜杠是他们日志格式的一个奇怪之处。它没有传递给shell。
现在,我在这里猜测(我认为这不值得深入研究!),但基本上关于日志记录ssh输出的问题在它冗长时会吐出来。我之前已经为过程启动编写了日志记录,而且考虑到复杂的参数是多么复杂(嵌入式换行?拖尾空格?疯狂引用?)。你并不经常需要100%“准确”的方法来无损地记录exec的参数,因为它太繁琐了。它看起来像这里的OpenSSH代码的作者,当寻找一个字符串来记录时,只是吐出他方便传递的字符串的转义形式作为sh的最后一个参数。它并不是一个“完美”的代表,表示会执行什么(因为我怀疑某些空格会在日志记录中丢失)而且它可能不是最友好的日志记录(因为它输入的内容更多!) ,但没关系。