題 如何可靠地保持SSH隧道打開?


我從工作中使用SSH隧道繞過各種idotic防火牆(我的老闆可以:))。問題是,一段時間後ssh連接通常掛起,隧道壞了。

如果我至少可以自動監視隧道,我可以在隧道掛起時重新啟動隧道,但我甚至都沒想辦法。

當然,可以告訴我如何阻止我的ssh連接懸掛的人的獎勵積分!


207
2017-09-08 13:04


起源


你的隧道因為不活動而死了嗎?從我的手機隧道端口時我遇到了這個問題所以我終於在連接上產生虛擬命令,使其“活著”使用 watch 命令如下: watch -n1 60 echo "wiiiii"。除非網絡中斷或您不使用隧道,否則隧道不會死亡。 - erm3nda
有關: unix.stackexchange.com/q/200239 - sampablokuper


答案:


聽起來像你需要的 autossh。這將監視ssh隧道並根據需要重新啟動它。我們已經使用它幾年了,它似乎運作良好。

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

有關-M參數的更多詳細信息 這裡


247
2017-09-08 13:43



自動搜索的+1,它在錫上執行的操作。我相信它的部分功能還包括發送保持活動樣式的數據包以防止任何類型的超時。 - akent
你可以使用示例隧道嗎? autossh 在答案? - Ehtesh Choudhury
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com   您可能會注意到我使用-nNT設置了它,它不會創建遠程終端,因此我可以將autossh放入後台,並使用-i選項讓SSH使用.pem文件。如果您要保持連接始終打開,我絕對建議您完成額外設置。 - juckele
對於它的價值,看起來通常更好的省略 -M 參數: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162 - rinogo
如果我有密碼短語,那該怎麼辦?它會將密碼鏈存儲在鑰匙串中(例如在mac上)嗎? - Yannick Y


在一段時間內沒有看到該連接的數據包之後,所有狀態防火牆都忘記了連接(為了防止狀態表在沒有關閉連接的情況下完成兩端死亡的連接)。大多數TCP實現將在很長一段時間後發送keepalive數據包,而不會從另一方聽到(2小時是常見值)。但是,如果有一個狀態防火牆在發送keepalive數據包之前忘記了連接,則一個長期但空閒的連接將會死亡。

如果是這種情況,解決方案是防止連接變為空閒。 OpenSSH有一個叫做的選項 ServerAliveInterval 這可以用來防止連接閒置太久(作為獎勵,即使連接空閒,它也會檢測到對等體何時死機)。


35
2017-09-28 13:47



指定的間隔以秒為單位,因此您可以提供一些微調。如果您的狀態防火牆有5分鐘的空閒超時,則60或120秒足以保持連接打開。這是我通過家用路由器打開ssh會話的方式之一。 - Darren Hall
謝謝,這有幫助。但請注意(來自排名較低的答案, superuser.com/a/146641/115515)如果您指定ServerAliveInterval而不是ServerAliveCountMax,您可能會發現ssh故意斷開連接,而不是您想要的。 - metamatt
@metamatt,你提到的排名較低的答案是排名較低的,有充分理由:這是錯誤的。 - Lambart


在你自己的mac或linux機器上配置你的ssh每3分鐘保持服務器ssh活著。打開一個終端,在你的家中找到你看不見的.ssh:

cd ~/.ssh/ 

然後創建一個1行配置文件:

echo "ServerAliveInterval 180" >> config

你還應該補充:

ServerAliveCountMax xxxx (high number)

默認值為3,因此ServerAliveInterval 180將在9分鐘後停止發送(ServerAliveInterval指定的3分鐘間隔中的3個)。


23
2018-05-29 13:45



請注意,如果您已有配置文件,則不建議使用您的命令。使用>>進行重定向會好很多! - Peltier
為什麼 ServerAliveInterval 180 給我們6分鐘?直覺讓我試試這個: 180/60 == 3。那麼,是的 ServerAliveInterval 工作在30秒的倍數? - nemesisfixx
@mcnemesis:ServerAliveInterval 180表示3分鐘。 ServerAliveCountMax默認值為3表示其中3個間隔,因此為9分鐘。 - metamatt
我正在投票給出這個答案,因為感謝您提及ServerAliveCountMax,以及如果您在沒有ServerAliveCountMax的情況下指定ServerAliveInterval會發生什麼。但是就像前面的評論一樣,我注意到“將停止發送後”的計算是錯誤的,我認為如果它只提供有關這些選項的信息,而不告訴我們如何使用cd和echo命令應用它們,這個答案會更好。 。 - metamatt
Downvoting因為將ServerAliveCountMax設置為“高數字”沒有意義。 ServerAliveCountMax指定在放棄之前嘗試發送“keepalive”消息的次數。默認值為3,因此對於ServerAliveInterval 180,如果服務器在9分鐘後沒有響應,它將僅停止發送,在這種情況下,您的連接可能已經完全失效。 - Lambart


我使用以下Bash腳本來保持在前一個ssh隧道死亡時生成新的ssh隧道。當您不希望或無法安裝其他軟件包或使用編譯器時,使用腳本非常方便。

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

請注意,這需要一個密鑰文件來自動建立連接,但autossh也是如此。


20
2017-09-22 14:58



您應該在autossh中添加使用此腳本的任何原因,還是只是這樣更容易? - kyrias
如果ssh本身凍結,這無濟於事,不是嗎? - nafg
如果您無法在服務器中安裝內容,它會有所幫助。 autossh沒有預先安裝和bureucracy它有時非常遲鈍。 - quarkex
是的,最好不要安裝東西。我已經這樣做了一年,這是我保持遠程機器可訪問的唯一方法(甚至設置crontab在重啟時運行它)。它永遠不會失敗,更重要的是,我知道為什麼它永遠不會失敗。 - sudo


Systemd非常適合這種情況。

創建服務文件 /etc/systemd/system/sshtunnel.service 含:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(修改ssh命令以適應)

  • 這將以用戶身份運行 sshtunnel 所以請確保用戶首先存在
  • 問題 systemctl enable sshtunnel 將其設置為在啟動時啟動
  • 問題 systemctl start sshtunnel 立即開始

2018年1月更新:某些發行版(例如Fedora 27)可能使用SELinux策略來阻止從systemd init使用SSH,在這種情況下,需要創建自定義策略以提供必要的豁免。


11
2017-07-28 06:10



這與我的要點非常相似: gist.github.com/guettli/... 歡迎反饋! - guettli
非常適合 systemd 系統。如果使用 Restart=on-failure 然後手動終止SSH客戶端將不會導致作為SSH客戶端重新啟動系統,並且退出成功。 - David Tonhofer
如果你想從作為參數給出的(bash)腳本啟動ssh ExecStart 例如建立 ssh 參數列表,進行基本檢查等,然後從腳本中調用它 exec /bin/ssh -N ...。這是我的命令: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}" 哪裡 TUNNEL_INLET="127.0.0.1:3307" 和 TUNNEL_OUTLET="127.0.0.1:3306" - David Tonhofer


它肯定在我看來你們都誤解了ServerAliveCountMax。據我了解文檔,它是服務器活動消息的數量,如果沒有終止連接,它們可能無法應答。因此,在我們這裡討論的情況下,將其設置為高值只會確保不會檢測到並終止掛起的連接!

簡單地設置ServerAliveInterval應該足以解決防火牆忘記連接的問題,並且將ServerAliveCountMax保持為低將允許始發端注意到故障並且如果連接失敗則終止。

你想要的是,1)連接在正常情況下永久保持打開狀態,2)檢測到連接失敗並且發起失敗時退出,3)每次重新發出ssh命令退出(你如何做到這是非常依賴於平台的,Jawa建議的“while true”腳本是單向的,在OS XI上實際設置了一個啟動項)。


9
2017-11-28 01:04





一直用 ServerAliveInterval 如果隧道問題是由過期的NAT會話生成的,則使用SSH選項。

如果連接完全斷開,請始終使用重新生成方法,此處至少有三個選項:

  • 自動程序
  • bash腳本(while true do ssh ...; sleep 5; done)不要刪除sleep命令, ssh 可能會很快失敗,你會重新生成太多的進程
  • /etc/inittab,要訪問在NAT後面運送和安裝在另一個國家/地區的盒子,而無需將端口轉發到該框,您可以將其配置為創建一個ssh隧道給您:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • Ubuntu上的upstart腳本,在哪裡 /etc/inittab 不可用:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    

或者總是使用這兩種方法。


9
2018-03-08 21:30



如果您不希望它用於所有SSH連接,則為內聯選項+1 - user1146334
你寫“萬一連接完全崩潰”。現在我不明白,autossh自己修復了什麼問題,它不是什麼問題?我想,當然,它會照顧任何斷開的連接,比如拔掉電纜幾個小時,但也許不是嗎? - Mads Skjern