重现步骤:

重现步骤:

为什么使用 ProxyCommand 时 OpenSSH_8.4p1 会终止共享同一连接的其他会话?有什么方法可以防止这种情况发生吗?

注意:如果省略 ProxyCommand 参数,则此行为似乎不会发生。

重现步骤:

  1. 终止与本地主机的所有现有共享连接:
ssh -o ControlPath=/tmp/%C -O exit 127.0.0.1 2>/dev/null
ssh -o ControlPath=/tmp/%C -O exit localhost 2>/dev/null
  1. 在不同的终端中并行运行以下命令两次:
ssh -F none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
  -o ControlMaster=auto -o ControlPath=/tmp/%C -o ControlPersist=1d \
  -o ProxyCommand='ssh -W %h:%p 127.0.0.1' \
  localhost 'sleep 3600'
  1. 通过输入 control-C 以 SIGINT 中断第一个 ssh 进程。

预期行为

  • 只有 SIGINT 进程才会终止。
  • 其他进程继续运行,不受影响。

实际行为

  • 两个进程均终止。

答案1

只有 SIGINT 进程才会终止。

“过程”是一个错误的前提。Ctrl+C 发送SIGINT到前台进程团体终端. 一个进程组可能包含多个进程。

那么这是相关的:

ProxyCommand
指定用于连接服务器的命令。命令字符串延伸到行尾,并使用用户的 shellexec指令执行,以避免 shell 进程延迟。
[…]

来源

该命令在与 main 相同的进程组中本地执行ssh。在您的示例中,命令是,ssh …但通常它可以是任何内容。无论如何,命令都无法识别,ControlMaster并且ControlPersist您将其用于 main ssh

当您按下Ctrl+时C,前台进程组中的每个进程都会获得SIGINT。“主”进程ssh退出,但不会影响 中的套接字,因为在之外的ControlPath情况下,此套接字从一开始就由 处理ControlPersistno完后还有 ssh进程是故意在其自己的进程组中生成的,这使其能够存活。在这种情况下,您可以称其为真正的主进程ssh

出现意外行为是因为 指定的命令ProxyCommand在前台进程组中生成,并且它SIGINTssh您想要中断的 相处融洽。该命令对信号的反应就像它通常会做的那样。在您的情况下,命令在 时终止SIGINT。并且由于该命令应该中继所有数据,因此主连接(真正的主)现在毫无用处。它与所有依赖进程ssh一起终止。ssh

因此,原始版本ssh会进行额外的工作以确保ssh处理主连接(和套接字)仍然存在Ctrl+ C,但它不会对ProxyCommand同样重要的指定命令执行此操作。我认为你可以称之为错误。

如果在中指定的命令ProxyCommand是由免疫产生的,ssh那么可能会更好它是进程组。我还没有对此进行足够彻底的分析。无论如何,目前情况并非如此,该命令是在按下Ctrl+时作为前台进程组的进程组中生成的C,因此它会得到SIGINT

一个解决方法是使命令不受 的影响SIGINTProxyCommand你不能trap直接在里面使用,因为会自动ProxyCommand使用exec并且exec trap …没有意义,也不会起作用。你还需要另一个 shell:

ssh -F none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
  -o ControlMaster=auto -o ControlPath=/tmp/%C -o ControlPersist=1d \
  -o ProxyCommand='sh -c "trap \"\" INT; exec ssh -W %h:%p 127.0.0.1"' \
  localhost 'sleep 3600'

我的测试表明,免疫SIGINT不会阻止这个内部ssh在时间到来时退出,并且主连接应该由于有限的ControlPersist设置而终止。

相关内容