2023年8月12日 星期六

autossh: 建立具有韌性的反向 ssh 隧道

家裡或公司的防火牆裡面有一部 24 小時開機的電腦 H, 沒有對外的 IP 位址。 另外有一部具有公開位址的伺服器 PubS。 這時可以從 H 向 PubS 啟動一個 反向 ssh 隧道連線, 像這樣: ssh -4fNR '4380:localhost:22' ckhung@PubS 這裡的 4380 是任選的 port。 接下來就可以隨時隨地 (從網路其他任何地方) 先登入 PubS, 再從 PubS 登入 H: ssh -p 4380 localhost。 可是, 只要家裡跳電或分享器關機一陣子, 連線就斷掉, 哭哭了。 這時你需要 autossh, 它可以定時檢查連線是否暢通, 有必要時就會自動重新啟動連線。

先在 PubS 上面作一些準備。 這部分都採用 root 的身份做事。 我喜歡建立一個拉管線專用、 最低權限的機器人帳號: useradd sshbot -m -s /bin/rbash 詳見 The Restricted Shell。 又, 確認它無法用密碼登入: grep sshbot /etc/shadow (密碼欄是一個 "!" )

再來, 以 root 的身份幫 sshbot 建立一些檔案與目錄:

cd /home/sshbot
echo 'export PATH=$HOME/bin' > .bashrc
mkdir .ssh bin
ln -s /usr/bin/ls bin/ # 非必要。 只是示範如何授權指令給 sshbot
touch .ssh/authorized_keys
chown -R sshbot:sshbot .ssh
chmod -R 700 .ssh

原本的 .bashrc 被蓋掉沒關係, 反正我們這個帳號沒有要執行指令, 只是用來拉管線而已。 很多檔案與目錄的擁有人都保持是 root 沒關係, 反正本來就不希望 sshbot 有什麼修改權限。

然後回到 H。 以下動作如果沒有特別說明, 都是在 H 上執行。

  1. 要讓 autossh 可以自動重建連線, 前提是它必須可以無密碼登入 PubS。 請先 (用誰的帳號都可以, 我是在常用的 ckhung 帳號底下執行 ssh-keygen) 建立一個 「只有公鑰、私鑰, 沒有密碼」 的 ssh 身份, 例如產生了 $HOME/.ssh/id_bot.pub 跟 $HOME/.ssh/id_bot 這一對公鑰私鑰。
  2. 把 $HOME/.ssh/id_bot.pub 的內容剪貼到 sshbot@PubS 的 $HOME/.ssh/authorized_keys 裡面, 讓它信任這把公鑰的持有者。
  3. 安裝套件: apt install autossh

先手動測試:

ps x | grep ssh
autossh -f -o "ServerAliveInterval 20" -o "ServerAliveCountMax 3" -NR 4380:localhost:22 -i $HOME/.ssh/id_bot sshbot@PubS
ps x | grep ssh

啟動後, 會多出兩個 processes, 長得像這樣:

  21156 ? Ss     0:00 /usr/lib/autossh/autossh    -o ServerAliveInterval 20 -o ServerAliveCountMax 3 -NR 4380:localhost:22 -i /home/ckhung/.ssh/id_bot sshbot@PubS
  21257 ? S      0:00 /usr/bin/ssh -L 57605:127.0.0.1:57605 -R 57605:127.0.0.1:57606 -o ServerAliveInterval 20 -o ServerAliveCountMax 3 -NR 4380:localhost:22 -i /home/ckhung/.ssh/id_bot sshbot@PubS

那個 autossh 是主要的程序; 而很帶有長一串 (抄自 autossh 命令列) 參數的那個 ssh 反向隧道則是由 autossh 啟動的。 此時即可在 PubS 上面用 ssh -p 4380 ckhung@localhost 測試連線。 注意: 用戶名稱 "ckhung" 是 H 機器上的 ckhung, 不是 PubS 上的 sshbot。

要測試自動重新連線, 不可以把上述兩個程序砍掉。 即使只是砍 ssh, autossh 也會跟著死掉。 請把網路線拔掉。 每隔 20 秒 (ServerAliveInterval) autossh 會試著測試連向 PubS 的 ssh 反向隧道是否還正常運作。 失敗 3 次 (ServerAliveCountMax) 之後, autossh 會試著重新啟動一個新的 ssh 反向隧道, 此時那個 ssh 程序的 pid 會改變。 再把網路線接回去。 應該又可以從 PubS 連回 H 了。

測試成功之後, 在 H 建一個文字檔 /etc/systemd/system/autossh-tunnel.service 裡面填入以下:

[Unit]
Description=AutoSSH tunnel service
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -o "ServerAliveInterval 60" -o "ServerAliveCountMax 5" -NR 4380:localhost:22 -i /home/ckhung/.ssh/id_cronbots sshbot@PubS

[Install]
WantedBy=multi-user.target

# 從某處抄來的; 忘記寫下網址 (^_^;)

這裡我改成 「每 60 秒測一次連線、 連續五次失敗, 就重啟 ssh」, 還有 $HOME 也改成完整的路徑。 實際上我的 PubS 跟 H 的 ssh 服務都不是開在預設的 port 22, 所以還要再加上 -p 參數等等。 最後: systemctl enable autossh-tunnel 設定它一開機就自動啟動。 然後就可以重開機 H 測試看看。

以前我不知道有 autossh 這個東西, 還自己寫亂七八糟的 cron, 太辛苦啦~ (中文世界好像多半是簡中的文章。) 趕快幫忙向命令列控們推廣一下吧!

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。