將 SSH 反向連接(反向隧道)設置變成系統服務

我有一部主機放在防火牆裡面,外部無法直接連上。只能透過某部電腦(abc.abc.abc) 以 ssh 反向連接方式跳進去。

因此,我得先登入放在防火牆裡面的那部主機,執行以下命令:
/usr/bin/ssh -NfR 3270:localhost:22 username01@abc.abc.abc

現在我想把以上命令改寫成 systemctl 的 service ,且可以開機時自動啟動,斷線時,也能自動偵測並自動啟動。

透過與 Google Gemini 問答後,結果如下:

[Unit]
Description=SSH 反向通道 (3270 -> localhost:22)
After=network-online.target

[Service]
#指定遠端主機使用者帳號
Environment=”Remote_host_account=(ex:username01)

#指定連線遠端主機
Environment=”Remote_host=(ex:abc.abc.abc)

#定義遠端主機對外打開的埠
Environment=”Remote_host_port=(ex:3270)

# 使用 simple 類型。ExecStart 啟動的程序是主程序。
Type=simple

# 確保 SSH 在背景執行,並使用標準的 -N -R 參數
# -N: 不執行遠端指令
# -R: 反向通道 (Remote Port Forwarding)
# -o ExitOnForwardFailure=yes: 若通道建立失敗則 SSH 退出,以便 Systemd 重新啟動
# -o ServerAliveInterval=60: 每隔 60 秒發送一個心跳包給遠端伺服器
# -o ServerAliveCountMax=3: 若連續 3 次心跳包無回應,則斷線並退出


ExecStart=/usr/bin/ssh -N -R ${Remote_host_port}:localhost:22  ${Remote_host_account}@${Remote_host} \
    -o ExitOnForwardFailure=yes \
    -o ServerAliveInterval=60  \
    -o ServerAliveCountMax=3

# 關鍵:設定在 SSH 斷線退出(非正常退出)時自動重啟服務
# on-failure:僅在非零退出碼時重啟,這符合 SSH 因斷線而退出的情況
Restart=on-failure

# 設定重啟延遲,避免短時間內重複失敗
RestartSec=5s

# 建議設定使用者,以避免使用 root 權限執行 SSH
#這裡不接受變數,只能輸入該主機裡真實的帳號名稱
User=(ex:username01)

[Install]
# 服務會在多用戶環境(即正常開機完成)時啟用
WantedBy=multi-user.target


##將以上內容存成 my-tunnel.service 後,放到 /etc/systemd/system/ 目錄下
sudo cp my-tunnel.service /etc/systemd/system/

##讓該檔案屬性成為可執行
sudo chmod 755 /etc/systemd/system/my-tunnel.service

##將該檔案設定成開機自動啟動
sudo systemctl enable my-tunnel.service

##現在立刻啟動。
sudo systemctl start my-tunnel.service

 

Systemd 的補充說明

Type 參數用來指示 Systemd 如何管理和判斷服務的生命週期。以下針對幾個主要的 Type 舉例說明其適用的情境:

  1. Type=simple (簡單/基本類型)
  2. 情境:
    啟動一個常駐性 (Always Running) 的應用程式,該程式啟動後會持續在前台運行,且不會自行轉為背景程序(Daemonize)。

    範例:
    啟動一個由 Python 撰寫的 Echo 網路伺服器 或一個簡單的 API 服務。

    Systemd 行為:
    一旦 ExecStart 執行的程序開始運行,Systemd 就認為服務啟動完成。如果該程序結束,Systemd 會根據 Restart= 設定來決定是否重啟。

  3. Type=forking (背景化類型)
  4. 情境:
    啟動一個傳統的 Linux 背景服務(Daemon),該服務在啟動後會執行自身背景化的操作(父程序退出,子程序在背景運行)。

    範例:
    傳統的 httpd 網頁伺服器(舊版 Apache)或某些舊式的資料庫服務。

    Systemd 行為:
    Systemd 會等待父程序退出後,認為服務已啟動。此類型通常需要使用 PIDFile= 參數來告訴 Systemd 哪個 PID 是真正的背景主程序,以便正確追蹤。

  5. Type=oneshot (單次執行類型)
  6. 情境:
    執行一個一次性的設定、維護、或清理任務,該任務完成後即退出,且不需要常駐。

    範例:
    在系統啟動時掛載特殊的網路檔案系統或執行防火牆初始化腳本。執行一個排程任務的清理腳本。

    Systemd 行為:
    Systemd 會等待 ExecStart 指令執行完畢並成功退出後,才認為服務執行成功,然後服務狀態轉為停止。

  7. Type=notify (通知類型)
  8. 情境:
    啟動一個現代應用程式,該程式具有內建 Systemd 整合,可以在內部確認所有資源(如資料庫連線、網路埠)準備就緒後,再明確通知 Systemd。

    範例:
    現代版本的 Web 伺服器或需要較長初始化時間的應用程式。

    Systemd 行為:
    Systemd 會在 ExecStart 啟動後等待,直到程序透過 sd_notify() 函式發送「就緒 (READY=1)」訊號後,才認為服務啟動完成。這提供了最精確的啟動狀態追蹤。

在 [Service] 區塊中,Restart= 參數用於定義在服務程序(Service Process)退出後,Systemd 應採取何種自動重啟策略。

  1. Restart=on-failure 的意義
    • Restart=on-failure:
    • 表示 Systemd 僅在服務程序以非零狀態碼退出或因為非正常原因(如被未處理的信號終止、逾時、或因為 Watchdog 機制)終止時,才會嘗試重啟該服務。

    • 非零狀態碼 (Unclean Exit):
    • 通常代表應用程式執行過程中發生了錯誤。

    • 正常退出 (Clean Exit):
    • 如果服務程序是以狀態碼 0 退出,則被視為正常停止,Systemd 不會重啟服務。這符合您所上傳檔案中註釋的意圖:僅在 SSH 因斷線而退出(通常是非零退出碼)時才重啟。

  2. Restart= 的主要類型 (值)
  3. Systemd 提供了多種重啟策略,您可以將 Restart= 設置為以下類型之一:

    類型(Value) 意義 (何時重啟)
    no (或留空) 不重啟。服務退出後即結束 (這是預設值)。
    always(總是重啟) 無論服務是正常退出 (狀態碼 0) 或非正常退出,Systemd 都會嘗試重啟。
    on-success 僅在正常成功退出時重啟。即服務以狀態碼 0 退出,或被特定的成功信號終止時重啟。
    on-failure 僅在非正常失敗退出時重啟。服務以非零狀態碼退出、被終止信號終止、逾時或 Watchdog 失敗時重啟。
    on-abnormal 僅在非正常終止時重啟。服務被信號(例如 SIGKILL, SIGSEGV)終止、逾時或 Watchdog 失敗時重啟,但不包括狀態碼非零的退出。
    on-watchdog 僅在 Watchdog 逾時時重啟。服務因未能定期向 Systemd 報告其健康狀態而逾時時重啟。
    on-abort 僅在被未捕獲的信號中止時重啟。服務因非正常信號退出時重啟。

重要提示:

無論設定為何種重啟策略,Systemd 的重啟嘗試都會受到 StartLimitIntervalSec= StartLimitBurst= 參數設定的重啟頻率限制。

如果服務在短時間內失敗次數過多,Systemd 會停止嘗試重啟,並將服務置於 failed 狀態。

可以使用 RestartSec= 參數指定重啟服務前的等待時間。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*