重現步驟:

重現步驟:

使用 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. 「main」ssh退出時不會影響套接字,因為在除此套接字之外的ControlPath情況下,從一開始就由ControlPersistno完後還有 ssh故意在自己的進程組中產生的進程,這使其能夠生存。在這種情況下,您可以將其稱為真正的 main ssh

意外的行為是因為 指定的命令ProxyCommand在前台進程組中生成,並且它確實SIGINTssh您想要中斷的命令一致。該命令對信號做出反應,就像平常一樣。在您的情況下,命令終止於SIGINT。由於該命令應該中繼所有數據,因此主連接(真正的 main ssh)現在毫無用處。它與所有相關ssh進程一起終止。

因此,原始版本ssh做了額外的工作來確保ssh處理主連接(和套接字)在Ctrl+中倖存C,但它不會為指定的命令執行此操作,ProxyCommand這同樣重要。我想你可以稱之為錯誤。

如果 中 指定的命令是由中 的ProxyCommand免疫生成的,可能會更好ssh它是進程組。我對這個問題分析得還不夠徹底。不管怎樣,現在情況並非如此,該命令是在您點擊Ctrl+時的前台進程組生成的進程組中生成C的,因此它得到SIGINT

解決方法是使命令不受SIGINT.在裡面ProxyCommand你不能trap直接使用,因為自動ProxyCommand使用並且沒有意義且不起作用。你還需要另一個外殼:execexec trap …

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設置,主連接應終止。

相關內容