實體主機防火牆變更:以定時回復保護 SSH 管理連線
遠端管理實體主機時,變更防火牆最容易犯的判斷錯誤是:目前的 SSH 工作階段還能執行命令,就以為規則已通過驗收。實際情況可能正好相反:連線追蹤保留了既有工作階段,新的連線卻已遭到封鎖。可靠的操作順序應是先保存現況、安排回復,再載入規則,最後以獨立登入確認管理通道仍然可用。
本文使用 Debian 12、nftables 與 systemd,示範只限制 SSH 來源的增量變更。AMDSSH 主機的連線位址與登入憑證,可在付款後至控制台查看;若有多個 IPv4,SSH 使用第一個位址。範例不預設帳號名稱或連接埠,也不建立完整的業務連接埠規則。
一、先釐清規則的管理範圍
以下操作要求帳號具備 sudo 權限、系統已安裝 nftables,且所有規則均由原生 nftables 統一管理。若容器網路、虛擬化橋接或其他防火牆工具正在動態產生規則,就不應直接套用整份快照回復:回復時可能覆蓋這些工具在變更期間新增的內容。此時應先釐清規則由誰管理,再改用對應工具的變更流程。
先查看目前狀態:
sudo nft list ruleset
sudo systemctl cat nftables.service
command -v nft
確認現有規則、啟動時載入的檔案,以及 nft 的實際路徑。本文另外使用 inet ops_guard 表;若同名表已存在,必須核對其用途,不可覆蓋其他工作的規則。
回復計時器只處理本次防火牆規則變更,無法修復因路由、SSH 服務或管理來源同時變動而造成的故障。一次變更只調整一個變數。
讀取實際 SSH 參數
在目前遠端登入的 Bash 中執行,不要先切換到無法保留連線環境變數的其他工作階段:
test -n "$SSH_CONNECTION" || exit 1
read -r ADMIN_IP CLIENT_PORT SERVER_IP SSH_PORT <<< "$SSH_CONNECTION"
case "$ADMIN_IP" in
*:*) printf '%s\n' 'IPv4 SSH session required'; exit 1 ;;
esac
printf 'source=%s destination=%s port=%s\n' \
"$ADMIN_IP" "$SERVER_IP" "$SSH_PORT"
這裡取得的是伺服器看到的連線來源,可能是辦公室的對外出口或跳板機位址,而非工作站的內網位址。若出口會變動,應先建立穩定的管理路徑。本範例要求目前工作階段使用 IPv4;不要從 IPv6 工作階段產生規則後仍強行繼續。
二、保存快照並預約自動回復
快照必須包含清除舊規則的指令,否則回復時可能與現有物件重複。目錄僅允許管理員存取,以免一般帳號讀取規則中的位址規劃。
sudo install -d -m 700 /root/firewall-change
sudo sh -c '
set -e
umask 077
{
printf "flush ruleset\n"
nft list ruleset
} > /root/firewall-change/before.nft
'
sudo nft -c -f /root/firewall-change/before.nft
-c 只檢查,不會載入。它能找出語法與規則參照問題,卻不能證明遠端登入仍然可用。檢查失敗時應停止變更,不可略過錯誤繼續執行。
確認沒有同名的遺留工作後,預約三分鐘後回復:
sudo systemctl status ops-firewall-rollback.timer
sudo systemd-run \
--unit=ops-firewall-rollback \
--on-active=3m \
"$(command -v nft)" \
-f /root/firewall-change/before.nft
sudo systemctl list-timers ops-firewall-rollback.timer
首次查詢時提示單元不存在,是正常情況;若已有工作,應先查明來源。三分鐘是本次操作的回復等待時間,不是服務交付承諾。必須確認計時器已排定執行,才能進入載入步驟。期間不要修改其他網路規則,否則整份規則回復時也會一併撤銷那些修改。
三、用獨立表限制管理來源
首次使用時建立空表。只有確認同名表不存在後,才執行建立命令;若本次專用表已存在,則略過:
sudo nft add table inet ops_guard
接著產生候選檔案:
sudo tee /root/firewall-change/candidate.nft > /dev/null <<EOF
delete table inet ops_guard
table inet ops_guard {
chain input {
type filter hook input priority 10; policy accept;
iifname "lo" accept
ct state established,related accept
ip saddr $ADMIN_IP tcp dport $SSH_PORT accept
tcp dport $SSH_PORT counter drop
}
}
EOF
sudo chmod 600 /root/firewall-change/candidate.nft
sudo nft -c -f /root/firewall-change/candidate.nft
sudo nft -f /root/firewall-change/candidate.nft
sudo nft -a list table inet ops_guard
檔案中的刪除與重建會在同一次 nftables 交易中提交,並非先清空再逐條加入。每次載入候選檔案時,都要求專用表已存在,因此不能直接將它當作冷啟動設定。
規則允許回環流量與已建立的連線,允許目前的 IPv4 管理來源存取實際 SSH 連接埠,接著拒絕其他來源存取該連接埠。最後一條規則也會比對 IPv6,因此本範例不允許建立新的 IPv6 SSH 連線。若需要透過 IPv6 管理,應事先加入明確的 ip6 saddr 允許規則,並另外測試。
其他業務連接埠不受此表限制。這裡的 accept 也不能推翻其他基礎鏈中的 drop:新連線仍可能遭既有規則拒絕。不要將此範例視為完整的安全基準,更不能據此刪除原有的業務規則。
四、以獨立新連線決定是否提交
保留原工作階段,不要登出,並從允許的來源另外建立一條連線。若用戶端已啟用連線多工共用,必須停用,否則新開的視窗可能仍使用舊的 TCP 連線。
以下命令在用戶端 Bash 執行,透過互動提示輸入控制台提供的參數:
read -r -p 'Host: ' HOST
read -r -p 'User: ' LOGIN
read -r -p 'Port: ' PORT
ssh -o ControlMaster=no -o ControlPath=none \
-p "$PORT" "$LOGIN@$HOST"
驗收至少包含三項:
- 允許的來源能建立獨立連線、完成驗證並執行命令。
- 從另一個受控、未被允許的來源發起連線,確認無法建立 SSH 工作階段。
- 查看拒絕規則的計數變化,區分防火牆攔截與帳號驗證失敗。
僅出現密碼或金鑰驗證錯誤,不能證明來源限制失效;應判斷連線究竟在哪一層遭到拒絕。不要為了測試而暫時放寬規則。
若獨立登入失敗,保留現況並等待回復,不要連續追加允許規則。回復後查看計時器對應服務的日誌,再分析來源位址、連接埠及其他鏈的影響。確認驗收通過後,停止計時器:
sudo systemctl stop ops-firewall-rollback.timer
sudo systemctl is-active ops-firewall-rollback.timer
還應確認回復服務尚未啟動。若計時器剛好到期,必須重新讀取即時規則,不能只憑停止命令成功,就認定候選設定仍在生效。
五、永久設定與啟動驗收分開完成
執行中的規則通過驗收後,才處理啟動檔案。依照前面查到的服務設定確認實際載入路徑;Debian 12 常見的路徑是 /etc/nftables.conf,但自訂服務可能不同。
在規則穩定、沒有其他管理工具寫入的前提下,將完整的即時規則匯出為含有 flush ruleset 的檔案,執行 nft -c -f 檢查,並備份原啟動檔案後再替換。不能只保存專用表,否則重新啟動後可能遺漏原有的業務規則。
永久設定屬於第二次變更,應重新安排回復、載入完整啟動檔案,再重複獨立登入測試。不要把啟用服務、替換設定、重新啟動主機串成一條命令。重新啟動驗收應另外安排,並核對規則、允許來源的登入,以及業務連線是否正常。
最後記錄實際來源、SSH 連接埠、候選檔案、快照與驗證結果。可供提交的證據不是「命令執行成功」,而是新連線通過驗證、拒絕路徑符合預期、啟動設定能重現規則,三者同時成立。
常見問題
原有 SSH 工作階段正常,就能取消回復計時器嗎?
不能。連線狀態規則可能保留既有連線,必須從允許的管理來源建立獨立新連線並完成登入,才可取消回復計時器。
範例會一併關閉業務服務的連接埠嗎?
不會。範例只限制實際 SSH 連接埠的新連線,其餘連接埠仍受既有規則控制;這不是整台主機預設拒絕的完整防火牆基準。