
使用 ProxyCommand 時,為什麼 OpenSSH_8.4p1 會終止共享相同連線的其他會話?有什麼辦法可以防止這種情況嗎?
注意:如果省略 ProxyCommand 參數,則似乎不會發生此行為。
重現步驟:
- 終止與本機的任何現有共享連線:
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
- 每次在不同的終端機中並行執行以下命令兩次:
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'
- 透過鍵入 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
情況下,從一開始就由ControlPersist
no
完後還有 ssh
故意在自己的進程組中產生的進程,這使其能夠生存。在這種情況下,您可以將其稱為真正的 main ssh
。
意外的行為是因為 指定的命令ProxyCommand
在前台進程組中生成,並且它確實SIGINT
與ssh
您想要中斷的命令一致。該命令對信號做出反應,就像平常一樣。在您的情況下,命令終止於SIGINT
。由於該命令應該中繼所有數據,因此主連接(真正的 main ssh
)現在毫無用處。它與所有相關ssh
進程一起終止。
因此,原始版本ssh
做了額外的工作來確保ssh
處理主連接(和套接字)在Ctrl+中倖存C,但它不會為指定的命令執行此操作,ProxyCommand
這同樣重要。我想你可以稱之為錯誤。
如果 中 指定的命令是由中 的ProxyCommand
免疫生成的,可能會更好ssh
它是進程組。我對這個問題分析得還不夠徹底。不管怎樣,現在情況並非如此,該命令是在您點擊Ctrl+時的前台進程組生成的進程組中生成C的,因此它得到SIGINT
。
解決方法是使命令不受SIGINT
.在裡面ProxyCommand
你不能trap
直接使用,因為自動ProxyCommand
使用並且沒有意義且不起作用。你還需要另一個外殼:exec
exec 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
設置,主連接應終止。