前幾日的部署中,我們將管理介面暫時連接既有的上游路由器。現在防火牆與管理網段已經完成,本篇會先說明 Linux 如何選擇路由,再把管理位址、預設閘道與 DNS 逐台切換到正式管理網路。接著建立跳板機,把外部 SSH 集中到單一受控入口,避免逐台公開內部主機。本文會依序處理安全區域、公鑰驗證、OpenSSH、主機防火牆、目的地 NAT 與 WAN 規則,最後以正向與負向測試完成驗證。
今天先完成先前保留的管理網路切換,再建立唯一公開的 SSH 管理入口。這兩項工作需要回答以下五個問題:
本文先介紹跳板機的用途,再分別說明 Linux 路由與安全切換方法,以及入站轉送、SSH 身分驗證與主機防護,最後才進入實作。本文只完成外部管理者到跳板機的第一段連線。跳板機能夠前往哪些內部主機,以及管理者在目標主機上擁有哪些權限,留到下一篇處理。
本文閱讀方式
- 完整理解技術與底層原理:依序閱讀全文。
- 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
- 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
- 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。
跳板機(Jump Host)是管理者連線到受管主機以前,必須先登入的中介主機。管理者先從自己的電腦建立第一段 SSH 連線,再由跳板機建立前往受管主機的第二段 SSH 連線。受管主機只需要接受來自跳板機的管理連線,不必直接開放給管理者所在的每一種來源網路。當跳板機位於網路邊界、經過特別強化並準備承受外部攻擊時,也常稱為堡壘主機(Bastion Host)。NIST 將 Bastion Host 定義為為了承受攻擊而特別設計與設定的專用主機。

圖(一)管理者先登入跳板機,再由跳板機建立前往受管主機的第二段 SSH 連線。
管理者如何抵達跳板機,取決於環境的網路設計。跳板機可以只供內部管理網路使用,也可以透過 VPN、專線或其他受控路徑連線。若必須從網際網路進入,才需要由邊界防火牆(位於外部網路與內部網路交界的防火牆)建立公開入口、執行網路位址轉換(NAT),並用過濾規則限制允許的流量。路由器、防火牆與 VPN 控制哪些來源可以抵達跳板機,跳板機上的 OpenSSH 驗證管理者身分,受管主機則繼續判斷管理者能否登入。這些元件各自負責不同位置,跳板機不會取代前方的網路控制,也不會自動取得所有內部權限。
假設一個環境有 8 台需要遠端管理的伺服器,全部直接公開 SSH,就會形成 8 個需要個別更新、限制來源、管理金鑰與監看 Log 的端點。改由跳板機承接外部管理後,公開 SSH 端點可由 8 個縮減為 1 個,其餘主機只接受內部管理路徑。當然入口集中也會讓跳板機成為高價值目標。實際上可依安全區域、正式與測試環境、管理團隊或權限範圍設置不同的跳板機,並優先強化、更新及監控這些管理入口。
馬克斯普朗克資訊研究所(Max Planck Institute for Informatics)與台夫特理工大學(Delft University of Technology)研究團隊在 2025 年 ACM 網際網路量測研討會(Internet Measurement Conference,IMC)發表的 SSH 長期量測研究中,利用 221 個蜜罐觀察約三年,共記錄超過 5.46 億個 SSH 工作階段,其中 2.58 億個工作階段曾嘗試登入但未成功。蜜罐會刻意模擬可受攻擊的服務,因此這些數字不能直接換算成一般伺服器的入侵率。它們仍清楚顯示,只要公開 SSH,便會持續收到大量自動掃描與登入嘗試。這也說明了保護跳板機與公開 SSH 入口的重要性。
將外部入口改用非標準連接埠,可以減少只掃描 TCP 22 的自動化雜訊。PasswordAuthentication no 會關閉 SSH 密碼驗證,KbdInteractiveAuthentication no 會關閉可能由 PAM 提供的密碼或 OTP 提示。PermitRootLogin no 則禁止直接以 root 身分登入。這些設定能切斷常見的登入路徑,但不能阻止完整的連接埠掃描、遭竊的私鑰、OpenSSH 漏洞或錯誤設定,因此仍須配合來源限制、公鑰保護、安全更新與持續監控。
把公開 SSH 入口收斂後,還能取得以下管理效果:
理解跳板機的角色後,接下來分別處理今天的兩項工作:
切換前,主機仍透過上游路由器所在網段接受日常管理。正式管理網段雖已預先建立,尚未承接管理路徑。現在要先理解 Linux 如何選擇路由,再逐台切換管理位址、DNS 與一般對外流量。Lab 部分會進行實際切換。完成後,管理流量便能集中通過防火牆,使用直接連線路由的叢集與儲存網路則不會跟著改走預設路由。
一台主機可以同時擁有多張網路介面、多個 IPv4 位址與多張路由表。Linux 先依路由政策資料庫(Routing Policy Database,RPDB)的規則選擇路由表,再從該表找出最符合目的位址的路由。
ip rule show 會依優先序列出 RPDB 規則。數字越小,規則越早處理。一般 Linux 主機預設包含:
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
local table 保存主機自己的位址與廣播位址,由核心自動維護。main table 保存一般路由,包括介面建立的直接連線路由、管理者或網路設定加入的靜態路由,以及常見的預設路由。default table 是編號 253 的保留路由表,預設通常沒有內容。它和 default via ... 這種預設路由不是同一件事。規則查詢某張路由表後,若沒有找到符合目的位址的路由,才會繼續處理下一條 ip rule。找到可用路由後便停止查找,取得下一跳、送出介面與偏好的來源位址。

圖(二)Linux 主機的路由政策規則、local/main 路由表與實際路由查詢結果。
接著逐一查看圖(二)中的規則。範例主機使用 10.77.30.11/24,查詢 local table 時可以看到:
local 10.77.30.11 dev eth0 proto kernel scope host src 10.77.30.11
broadcast 10.77.30.255 dev eth0 proto kernel scope link src 10.77.30.11
第一筆表示目的地就是本機的 10.77.30.11 時,封包留在本機處理。第二筆是 10.77.30.0/24 的廣播位址。ip route get 10.77.30.11 因此得到:
local 10.77.30.11 dev lo src 10.77.30.11
這裡顯示 dev lo,是因為封包已被判定交給本機網路堆疊,不需要真的從 eth0 送出。
main table 則包含:
default via 10.77.30.1 dev eth0 proto static
10.77.30.0/24 dev eth0 proto kernel scope link src 10.77.30.11
10.77.30.0/24 是介面設定 10.77.30.11/24 後由核心建立的直接連線路由,所以標示 proto kernel。default 等同 IPv4 的 0.0.0.0/0,由系統網路設定加入,因此這裡標示 proto static。static 描述路由的來源,default 描述它涵蓋的目的前綴。兩者可以同時出現在同一筆路由上。
在同一張路由表中,Linux 先使用最長前綴比對(Longest Prefix Match)。目的地若位於 10.77.30.0/24,/24 會比預設路由的 /0 更精確,因此直接從 eth0 送出。1.1.1.1 不屬於這個網段,只能符合 /0,所以 ip route get 1.1.1.1 顯示:
1.1.1.1 via 10.77.30.1 dev eth0 src 10.77.30.11
ip -4 route show table all 可以列出所有 IPv4 路由表的內容,ip route get <目的位址> 則讓核心實際計算使用的路由。後者比只看介面是否有 IP,更能證明封包會使用哪一個下一跳、介面與來源位址。
真正容易造成事故的設定包括:
完成管理網路的規劃與切換方法後,接著處理獨立的 SSH 入口工作:先決定跳板機所在的安全區域,再設定 SSH 身分驗證、OpenSSH 與主機防火牆。所有本機防護完成後,才評估是否需要建立網際網路公開入口。
入口收斂後,還要避免跳板機成為繞過區域間政策的捷徑。跳板機應放在獨立的安全區域,讓外部管理流量與一般服務流量分開,並讓它前往其他網段的連線繼續通過防火牆路由與檢查。這裡延續前篇的安全區域規劃,進一步把管理入口放入 Bastion DMZ。
跳板機同時也是高價值目標。本文採用以下設計原則:
跳板機只有一張連接獨立安全區域的網路介面時,前往其他網段的流量必須交給防火牆路由與檢查。若另外把管理或資料庫網路介面直接接到跳板機,流量可能繞過原本設計的跨區域規則,讓跳板機變成難以稽核的旁路。
SSH 公鑰驗證使用一組互相對應的私鑰與公鑰。私鑰留在管理者的電腦,公鑰則安裝到遠端帳號。登入時,用戶端使用私鑰簽署本次連線的驗證資料,伺服器再以已授權的公鑰驗證簽章。驗證成功代表用戶端當下能使用對應私鑰。私鑰不會傳送到伺服器。
SSH 連線會使用多種用途不同的金鑰:
| 金鑰 | 驗證對象 | 保存位置與用途 | 實際使用範例 |
|---|---|---|---|
| 使用者驗證金鑰(User Authentication Key) | 使用者 | 私鑰留在用戶端。公鑰放入遠端帳號的 authorized_keys。 |
管理者以 ~/.ssh/id_ed25519 登入跳板機,sshd 使用 authorized_keys 內的公鑰驗證簽章。 |
| 伺服器主機金鑰(Host Key) | SSH 伺服器 | 伺服器保存私鑰。用戶端將伺服器公鑰記錄在 known_hosts。 |
用戶端第一次連線時確認主機指紋並記錄公鑰。日後若主機金鑰改變,SSH 會提出警告。 |
| 工作階段金鑰(Session Key) | 本次連線 | 兩端臨時協商,用來持續加密本次連線的資料。 | 加密通道建立後,身分驗證封包、Shell 指令、輸出與檔案傳輸都會受到保護。連線結束後即不再使用。 |
| SSH CA 金鑰 | 使用者或主機憑證 | 用來簽發 SSH 憑證,適合集中管理大量帳號或主機。 | 組織使用 CA 私鑰簽發短效使用者憑證,伺服器只需信任 CA 公鑰,不必把每位管理者的公鑰逐一加入 authorized_keys。 |
使用者金鑰確認誰正在登入,主機金鑰確認連到哪一台伺服器,工作階段金鑰負責加密連線內容。SSH CA 則把大量使用者或主機的信任集中到同一個簽發來源。
使用者可以在命令列執行 ssh-keygen 產生一組私鑰與公鑰。-t 選項用來指定金鑰演算法。常見類型如下:
| 類型 | 建立指令 | 優點與限制 | 使用情境 |
|---|---|---|---|
| Ed25519 | ssh-keygen -t ed25519 |
金鑰短、簽章速度快。較舊的用戶端、伺服器或設備可能不支援。 | 現代 OpenSSH 的一般新部署。 |
| RSA | ssh-keygen -t rsa -b 3072 |
相容性廣,但金鑰與簽章較大。應使用 RSA-SHA2,避免已停用的 SHA-1 ssh-rsa 簽章。 |
必須連接不支援 Ed25519 的舊設備。 |
| ECDSA | ssh-keygen -t ecdsa |
金鑰短且速度快。使用 NIST 曲線,要確認兩端支援與組織的密碼政策。 | 既有環境已採用 ECDSA,或政策指定使用相應曲線。 |
| FIDO 硬體金鑰 | ssh-keygen -t ed25519-sk |
私密材料保留在硬體中,通常可要求觸碰確認。需要相容硬體與軟體,也要準備替代金鑰。 | 高權限管理帳號或希望降低私鑰檔案遭複製風險的環境。 |
| DSA | 不應建立 | ssh-dss 受限於較弱的 1024-bit DSA,現代 OpenSSH 預設停用。 |
只用於辨識及淘汰舊金鑰,不應為了相容性重新啟用。 |
Linux 核心不負責解析 SSH 金鑰。實際讀取工作由用戶端的 ssh 與伺服器的 sshd 完成:
用戶端
├─ ~/.ssh/id_ed25519 私鑰
├─ ~/.ssh/id_ed25519.pub 公鑰
└─ ~/.ssh/known_hosts 曾連線伺服器的主機公鑰
伺服器
└─ /home/<管理帳號>/.ssh/
└─ authorized_keys 每一行代表一把允許登入的公鑰
登入時,sshd 先依使用者名稱找到該帳號的家目錄,再讀取 .ssh/authorized_keys,逐行尋找用戶端提出的公鑰。公鑰通常由金鑰類型、Base64 編碼資料與用途註解組成:

圖(三)authorized_keys 內一筆 SSH 公鑰的三個主要欄位。
用途註解不參與驗證,只協助辨認金鑰。sshd 也會檢查家目錄、.ssh 與 authorized_keys 的所有權及權限。常見設定是 .ssh 使用 700、authorized_keys 使用 600,並由該帳號持有。
公鑰驗證開始前,SSH 傳輸層已完成金鑰交換並建立加密通道。接下來的使用者驗證封包都在這條通道內傳送:

圖(四)SSH 公鑰驗證的簡化封包往返流程。
前面的公鑰試探不是必要步驟。用戶端也可以直接送出公鑰與簽章。私鑰只在用戶端本機執行簽署,不會傳送到伺服器。
公開 SSH 服務至少要限制可登入帳號、驗證方式與不需要的轉送功能:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowAgentForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
MaxAuthTries 3
LoginGraceTime 30
AllowUsers <管理帳號>
| 設定 | 用途 |
|---|---|
PermitRootLogin no |
禁止直接使用 root 登入。 |
PasswordAuthentication no |
關閉 SSH 密碼驗證。 |
KbdInteractiveAuthentication no |
關閉鍵盤互動式驗證,包括透過 PAM 提供的密碼或 OTP。 |
PubkeyAuthentication yes |
允許使用公鑰驗證登入。 |
AllowAgentForwarding no |
禁止把用戶端的 SSH 驗證代理程式提供給遠端工作階段。 |
X11Forwarding no |
禁止透過 SSH 轉送 X11 圖形介面。 |
PermitTunnel no |
禁止透過 SSH 建立 TUN/TAP Tunnel。 |
GatewayPorts no |
讓遠端轉送建立的監聽 Port 只綁定伺服器的 Loopback 位址。 |
MaxAuthTries 3 |
每條連線最多允許三次驗證嘗試。 |
LoginGraceTime 30 |
三十秒內未完成驗證便中斷連線。 |
AllowUsers <管理帳號> |
只允許列出的帳號登入。 |
這組設定將登入方式收斂為指定帳號的公鑰驗證。未來若要加入 PAM 提供的 OTP,需要重新啟用鍵盤互動式驗證,並使用 AuthenticationMethods 明確要求公鑰與 OTP 都通過。
GatewayPorts no 不會關閉遠端轉送,只限制轉送出的 Port 監聽在哪個位址。ProxyJump 仍需要 TCP Forwarding,下一篇會再使用 AllowTcpForwarding 與 PermitOpen 限制轉送方向與目的地。
設定完成後先執行 sshd -t 檢查語法,再用 sshd -T 檢查展開後的有效值。套用設定前先保持 PVE Console 開啟。完成目的地 NAT 與 WAN 規則後,再由另一個 PowerShell 視窗驗證 opsadmin 金鑰登入。若驗證失敗,可透過 Console 回復 OpenSSH 設定。使用 Reload 使 sshd 套用新設定。
網路防火牆控制跨區域與外部進入的流量,跳板機上的主機防火牆則限制真正抵達本機的封包。OpenSSH 最後再驗證登入身分與可使用的 SSH 功能。三者分屬不同控制位置:
| 控制位置 | 負責範圍 |
|---|---|
| 網路防火牆 | 決定哪些來源可以進入 Bastion DMZ。 |
| 跳板機的主機防火牆 | 決定本機接受哪些通訊協定與服務連接埠。 |
| OpenSSH | 驗證帳號與金鑰,並限制轉送、Tunnel 等 SSH 功能。 |
跳板機的 Input Chain 應採預設丟棄,只接受已建立或相關連線、Loopback、必要的 ICMP 與 TCP 22。Forward Chain 採預設丟棄,避免主機成為路由器。Output Chain 可先依更新與內部管理需求盤點,再逐步收斂。
這種分工讓單一控制失誤不會直接移除全部保護。DNAT 指向錯誤主機、WAN 規則過寬、主機多開一個服務或 OpenSSH 設定偏離時,其他層仍可能阻止連線並留下紀錄。
跳板機的 OpenSSH 與主機防火牆完成後,才判斷管理者的第一段連線要從哪裡進入。本節只適用於需要從網際網路直接抵達跳板機的環境。內部管理網路或 VPN 路徑不需要另外公開 WAN 連接埠。
跳板機使用私有 IPv4 位址時,防火牆以 DNAT 將 WAN 的外部 SSH 連接埠轉送到跳板機:

圖(五)防火牆檢查來源並執行 DNAT,跳板機再由主機防火牆與 sshd 處理連線。
外部入口使用 TCP 45222,可以和防火牆本身或其他 SSH 服務的 TCP 22 分開,也能減少只掃描標準連接埠產生的雜訊。跳板機內部仍監聽標準 TCP 22。更換外部連接埠不能阻止完整的連接埠掃描,也不能取代來源限制、公鑰驗證與安全更新。
DNAT 決定封包要送到哪裡,WAN 過濾規則才決定是否允許通過。OPNsense 的手動 WAN 規則要比對轉換後的跳板機私有位址與 TCP 22。只有 DNAT 而沒有允許規則時,封包仍會被預設拒絕。
若公網 IPv4 位於上游路由器,還要先將外部連接埠轉送到防火牆 WAN,再由防火牆轉送到跳板機。完成後應從真正的外部網路測試完整路徑。
將 SSH 服務公開到網際網路會增加遭到掃描與嘗試登入的機會,因此設定完成後必須進行測試。只證明正確金鑰能登入,無法證明 root、密碼與錯誤金鑰已被拒絕。公開入口完成後至少要確認:
| 測試 | 預期結果 | 主要觀察位置 |
|---|---|---|
| 正確的管理帳號與金鑰 | 成功 | OpenSSH Log、防火牆 State |
| 錯誤金鑰 | 失敗 | OpenSSH Log |
| 密碼登入 | 失敗 | SSH 用戶端與 OpenSSH Log |
| root 登入 | 失敗 | SSH 用戶端與 OpenSSH Log |
| 未開放的 WAN 連接埠 | 失敗 | 防火牆 Log |
| 跳板機上的非 SSH 服務 | 失敗 | 主機防火牆 Ruleset/Counter |
規則修改後,既有連線可能繼續使用舊 State。重新測試前應只清除與本次外部 SSH 連接埠有關的 State,避免清除全部連線而影響其他服務。
這次實作要驗證兩件正文中的核心敘述:Linux 會依路由表選擇封包出口,而且切換管理出口不應改變 Corosync 與 Ceph 的直接連線路徑。SSH 入口由 WAN 規則、目的地 NAT、主機防火牆與 OpenSSH 共同限制。本次以成功與失敗結果作為驗證依據,單次操作時間不列入效能指標。
GitHub 詳細實作文件:Day 15|切換 PVE 出口並部署 jump01
45222 轉送到 jump01 的 TCP 22。建立 PVE_NODES Host Alias,內容為 10.77.10.11、10.77.10.12、10.77.10.13。這三個位址都從 OPNsense 的 LAN 介面進入,因此規則建立在 LAN,順序如下:
PVE_NODES to OPNsense DNS
PVE_NODES ping OPNsense gateway
PVE_NODES outbound NTP
未來明確允許的 PVE 管理或監控流量
Block PVE_NODES to routed internal networks
PVE_NODES HTTP HTTPS updates
Block other PVE_NODES egress
原本 Default allow LAN to any
先允許必要服務,再阻擋其他內部與外部流量。最後一條 Block other PVE_NODES egress 位於原本 LAN Default Allow 上方,確保 PVE 不會落入較廣泛的允許規則。其他尚未盤點的 LAN 管理主機則不會在本次變更中一起斷線。
切換前先備份 /etc/network/interfaces,並記錄 ip route、pvecm status、chronyc tracking 與 ceph -s。三台節點已在 VLAN-aware vmbr1 上建立 Host VLAN 介面 vmbr1.10,這次只調整閘道:
| 節點 | Management 位址 | 新預設閘道 | DNS |
|---|---|---|---|
| pve01 | 10.77.10.11/24 |
10.77.10.1 |
10.77.10.1 |
| pve02 | 10.77.10.12/24 |
10.77.10.1 |
10.77.10.1 |
| pve03 | 10.77.10.13/24 |
10.77.10.1 |
10.77.10.1 |

圖(六)pve01 的 vmbr1.10 使用 10.77.10.11/24,並將預設閘道設為 10.77.10.1。
原 vmbr0 的 192.168.0.x/24 位址暫時保留,但移除 Gateway。每次只 Apply 一台,再執行:
ip route
ip route get 1.1.1.1
ping -c 3 10.77.10.1
getent hosts download.proxmox.com
curl -4I https://download.proxmox.com/
apt update
chronyc tracking
pvecm status
ceph -s

圖(七)路由查詢與連線測試顯示外部流量經 vmbr1.10 送往 10.77.10.1。

圖(八)APT 更新與 Chrony 狀態確認一般對外連線及時間同步均可正常運作。

圖(九)切換 pve01 後,PVE Cluster 仍維持三個節點並具備 Quorum。

圖(十)管理出口切換後,Ceph Cluster 仍維持 HEALTH_OK。
預期 ip route get 1.1.1.1 顯示經 10.77.10.1 與 vmbr1.10 送出。確認 OPNsense Live View、Web UI、APT、時間同步、Cluster 與 Ceph 全部正常後,才切換下一台。
圖(七)確認一般對外流量已改走 OPNsense。圖(九)、圖(十)則確認 Quorum 與 Ceph 同時維持正常。因此這組結果能驗證管理出口已完成切換,且沒有連帶中斷既有的叢集與儲存路徑。
若切換失敗,從 L0 Console 暫時恢復舊出口:
ip route replace default via 192.168.0.1 dev vmbr0
這條指令只修改目前執行中的路由。永久回復仍要移除 vmbr1.10 Gateway、恢復 vmbr0 Gateway,並核對變更前備份。
從 Debian Template 建立 Full Clone,使用 VMID 231、1 vCPU、1 GB RAM 與至少 8 GiB Disk。網卡連到 vmbr1 並設定 VLAN Tag 50,Cloud-Init 設定:
IP:10.77.50.11/24
Gateway:10.77.50.1
DNS:10.77.50.1

圖(十一)jump01 的虛擬硬體與唯一一張 VLAN 50 網卡。

圖(十二)jump01 的 Cloud-Init DNS 與固定網路參數。帳號與驗證欄位保留建置時的畫面狀態。
這張圖只用來驗證 DNS 與固定網路參數。畫面中的 SSH public key: none 與 Password 是建置當時留下的狀態。後續會另外建立 opsadmin 與專用 Ed25519 金鑰,不以這組密碼作為正式登入方式。
jump01 只保留這張 Bastion DMZ vNIC。完成更新後安裝 OpenSSH、Fail2ban、auditd 與 nftables,確認主機名稱、位址、預設路由與 Cloud-Init 狀態。本日的安全驗證範圍為 OpenSSH 與 nftables。Fail2ban 與 auditd 先完成安裝,相關規則留待後續設定。
在 Windows PowerShell 產生 jump01 專用金鑰:
$Jump01KeyDirectory = Join-Path $env:USERPROFILE '.ssh\ithome_jump01_ed25519'
$Jump01Key = Join-Path $Jump01KeyDirectory 'id_ed25519'
New-Item -ItemType Directory -Force -Path $Jump01KeyDirectory | Out-Null
ssh-keygen -t ed25519 -a 100 -f $Jump01Key -C 'opsadmin@jump01'
ssh-keygen -lf "${Jump01Key}.pub"
Get-Content "${Jump01Key}.pub" | Set-Clipboard
這組命令會在 Windows 使用者的 .ssh\ithome_jump01_ed25519 目錄建立一組專用 Ed25519 金鑰。可以調整儲存目錄、檔名與金鑰演算法,但不要覆蓋仍在使用的既有私鑰。
沒有 .pub 的檔案是私鑰,只留在管理電腦。將 .pub 的完整一行放入 jump01 的 /home/opsadmin/.ssh/authorized_keys,並設定目錄 700、檔案 600 與正確擁有者。
套用 OpenSSH 強化設定以前,先保持 PVE Console 開啟。依實作文件建立設定後執行:
sudo sshd -t && echo 'sshd_config syntax: OK'
sudo systemctl reload ssh
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|allowusers|allowagentforwarding|x11forwarding|permittunnel|gatewayports|maxauthtries|logingracetime'
sudo stat -c '%U:%G %a %n' /home/opsadmin/.ssh /home/opsadmin/.ssh/authorized_keys

圖(十三)OpenSSH 設定語法正確,root、密碼及鍵盤互動式驗證均已關閉,公鑰驗證已啟用。opsadmin 的 .ssh 與 authorized_keys 權限分別為 700、600。
畫面中的 alice、bob 是後續多人管理實作加入的帳號,不是 Day15 建立的帳號。這張圖在本篇只用來確認 opsadmin、驗證方式與檔案權限。
Input Chain 只允許 established,related、Loopback、ICMP 與 TCP 22。Forward Chain 使用 policy drop。Output Chain 暫時使用 policy accept。載入前先以 nft -c 檢查語法,再啟用 nftables 並列出有效 Ruleset。
要確認 jump01 沒有成為跨 VLAN 路由器,而且本機只監聽預期的 SSH 入口,執行:
sudo sysctl net.ipv4.ip_forward
sudo nft list chain inet filter input
sudo nft list chain inet filter forward
sudo ss -lntp | grep ':22 '

圖(十四)IPv4 Forwarding 已關閉。nftables 的 Input Chain 採預設丟棄並允許 TCP 22,Forward Chain 採預設丟棄,sshd 則監聽 IPv4 與 IPv6 的 TCP 22。
畫面中的 TCP 9100 允許規則是後續監控實作加入,不屬於 Day15 的初始規則。
在 OPNsense 建立:
Destination NAT
WAN address:45222
→ 10.77.50.11:22
WAN Pass Rule
Source:any(有固定管理來源時改用 Alias)
Destination:BASTION_HOST/10.77.50.11
Destination Port:22
Log:Enabled

圖(十五)Destination NAT 將防火牆 WAN TCP 45222 轉送至 10.77.50.11:22。

圖(十六)WAN Pass Rule 比對轉換後的 BASTION_HOST 與 SSH 連接埠。
本 Lab 的 Windows 測試端與 fw01 WAN 位於同一網段,因此這條 WAN 規則要停用 reply-to,並在重新測試前刪除 TCP 45222 的舊 State。正式網際網路或 Multi-WAN 環境的回程路徑不同,不應直接套用這項設定。
若上游路由器持有公網 IPv4,再將上游 TCP 45222 轉送到 fw01 WAN IP,最後使用行動網路或其他真正外部連線完成驗證。
從 WAN 側的 Windows PowerShell 執行:
$Jump01Key = Join-Path $env:USERPROFILE '.ssh\ithome_jump01_ed25519\id_ed25519'
$Fw01WanIp = Read-Host '請輸入 fw01 目前顯示的 WAN IP'
ssh -i $Jump01Key -p 45222 -o PasswordAuthentication=no "opsadmin@$Fw01WanIp"

圖(十七)正確的管理帳號與專用私鑰可透過 fw01 WAN TCP 45222 登入 jump01。
圖(十七)確認正向路徑成立,接著再以四種不符合政策的條件執行負向測試。

圖(十八)root、密碼及錯誤私鑰均被 SSH 拒絕,未開放的 TCP 45223 也顯示 TcpTestSucceeded: False。
圖(十七)與圖(十八)合計檢查五種條件:正確的 opsadmin 金鑰成功一次,其餘四種不符合政策的條件全部失敗。這組正向與負向結果驗證 WAN 側只有符合帳號、金鑰與連接埠政策的連線能進入 jump01。
| 正文敘述 | 驗證方式 | 目前結果 |
|---|---|---|
| Linux 依路由表選擇出口 | ip route get 1.1.1.1 |
經 vmbr1.10、下一跳 10.77.10.1 |
| 管理出口切換不應中斷專用叢集路徑 | pvecm status、ceph -s |
三個節點維持 Quorum,Ceph 為 HEALTH_OK |
| jump01 只連接 Bastion DMZ | PVE Hardware 與 Cloud-Init | 一張 VLAN 50 vNIC,位址為 10.77.50.11/24 |
| OpenSSH 與 nftables 共同限制本機入口 | sshd -T、檔案權限、sysctl、nftables Chain 與監聽 Socket |
SSH 與 nftables 符合預期,IPv4 Forwarding 為 0 |
| WAN 規則與目的地 NAT 缺一不可 | Destination NAT、WAN 規則與實際 SSH 連線 | 設定已建立,圖(十七)的連線已經通過完整路徑 |
| 身分驗證要同時測試允許與拒絕 | 正確金鑰、root、密碼、錯誤金鑰與未開放 Port | 一項允許成功,四項拒絕全部符合預期 |
jump01,並建立專用的 Ed25519 管理金鑰。fw01 WAN:45222 轉送到 jump01:22。下一篇會在已經安全對外的 jump01 上加入多人管理模型,拆開「可以登入跳板機」、「可以轉送到哪些目標」與「目標主機允許執行哪些操作」,再使用 ProxyJump、無 Shell 帳號與 PermitOpen 實作權限隔離。