前一篇已經把外部 SSH 收斂到跳板機,解決管理者如何安全抵達內部網路入口的問題。當多位管理者共用這個入口時,還要繼續區分每個人的身分、可轉送的目的地,以及登入目標主機後能執行的操作。本篇會先拆解 ProxyJump 的連線過程,再使用個人帳號、個別金鑰、無 Shell 的轉送帳號、PermitOpen、防火牆規則與目標主機權限,建立可以驗證的多人管理邊界。
今天要讓多人共用同一台跳板機,同時維持各自獨立的管理範圍。需要回答以下五個問題:
本文先說明 ProxyJump 的連線與 SSH 通道(Channel),再整理用戶端與伺服器端各自能控制的範圍,接著建立完整授權模型,最後才進入實驗環境(Lab)。
本文閱讀方式
- 完整理解技術與底層原理:依序閱讀全文。
- 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
- 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
- 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。
最直覺的跳板方式,是先登入跳板機取得 Shell,再從跳板機執行第二次 ssh:

圖(一)手動跳板需要先登入跳板機,再從跳板機發起第二段 SSH 連線。
這種方式會讓第二次 SSH 由跳板機上的用戶端程式發起。若第二段採用公鑰驗證,通常還要在跳板機準備認證資料,或透過 SSH 驗證代理程式轉送(Agent Forwarding),暫時讓跳板機使用管理者電腦上的驗證代理程式。多人共用時,跳板機也會成為可執行一般命令、保存連線設定及接觸內部網路的工作環境,權限邊界較難縮小。
ProxyJump 是 OpenSSH 用戶端提供的跳板連線功能。管理者只要指定跳板機與最終目標,SSH 用戶端便會先連上跳板機,要求跳板機建立前往目標主機的 TCP 轉送,再透過這條通道完成目標主機的 SSH 連線。使用者操作時看起來像是直接登入目標主機,不需要先取得跳板機 Shell,也不需要在跳板機上再次執行 ssh。
目標主機的帳號、私鑰與連線設定仍由管理者電腦上的 SSH 用戶端使用,跳板機只負責轉送連線資料。這能避免把目標主機私鑰存放在多人共用的跳板機,也讓 ssh、scp 與 sftp 繼續使用管理者電腦上的設定與金鑰。
ProxyJump 將連線方式整理成以下路徑:

圖(二)ProxyJump 由用戶端先連上跳板機,再經 TCP 轉送連向目標主機,使用者不必在跳板機取得 Shell。
一次性連線可以使用 -J:
ssh -J <跳板帳號>@<跳板機>:<跳板機連接埠> <目標帳號>@<目標主機>
經常使用的路徑則適合寫入用戶端的 ~/.ssh/config,再以主機別名連線。無論採用哪一種寫法,這些設定都只決定連線路徑。多人共用所需的強制限制,仍要由伺服器端與網路端執行。
OpenSSH 也允許用逗號依序列出多台跳板機,例如 ssh -J jump1,jump2 target。本系列只有一層跳板,因此不再展開多層路徑。完成 ~/.ssh/config 後,scp 與 sftp 等 OpenSSH 用戶端程式也能使用相同的目標主機別名,是否允許檔案傳輸仍由最終目標主機決定。
前一節說明 ProxyJump 的用途與操作方式,接著從連線建立順序觀察兩條 SSH 連線的分工。連線 A 終止於跳板機。連線 B 的兩端則是管理者電腦與目標主機,跳板機只轉送連線 B 的資料。

圖(三)連線 A 終止於跳板機,連線 B 則經過轉送通道連接管理者電腦與目標主機。
RFC 4254 將互動式登入與 TCP 轉送定義為不同的 SSH 通道。Shell、命令及 SFTP 子系統使用工作階段通道(session)。用戶端要求 SSH 伺服器連向指定主機與連接埠時,使用直接 TCP 轉送通道(direct-tcpip)。多個通道可以共用同一條加密的 SSH 連線,但伺服器仍能分別允許或拒絕各種通道。
| 所在連線 | 使用的通道 | 用途 |
|---|---|---|
| SSH 連線 A:管理者電腦與跳板機之間 | 直接 TCP 轉送通道(direct-tcpip) |
請跳板機連向目標主機 TCP 22,並轉送 SSH 連線 B 的加密資料。 |
| SSH 連線 B:管理者電腦與目標主機之間 | 工作階段通道(session) |
通過目標主機驗證後,用來開啟 Shell、執行命令或使用 SFTP。 |
因為轉送與開啟 Shell 是兩種可以分別控制的功能,跳板帳號可以只取得「通過跳板機前往目標主機」的權限,不取得「登入跳板機執行命令」的權限。這樣能減少使用者在跳板機讀取檔案、修改設定或執行其他網路工具的機會。限制是轉送帳號無法直接登入跳板機排查問題。維運跳板機時需要另外建立權限受控的管理帳號。
兩條 SSH 連線也會分別驗證主機金鑰與使用者金鑰。用來登入目標主機的私鑰不需要複製到跳板機,管理者也不必把 ssh-agent 轉送給跳板機。登入跳板機與目標主機的使用者私鑰都可以留在管理者電腦上。
這一節設定的是管理者電腦上的 OpenSSH 用戶端。將 ProxyJump 寫入 ~/.ssh/config 後,用戶端便知道連線特定目標時,要先使用哪一台跳板機。
假設一名管理者要透過公開位址為 203.0.113.10:45222 的跳板機,連線到內部位址為 10.20.30.40 的應用程式主機。兩台主機分別使用不同的登入私鑰:
Host company-jump
HostName 203.0.113.10
Port 45222
User opsadmin
IdentityFile ~/.ssh/opsadmin_bastion
IdentitiesOnly yes
Host internal-app
HostName 10.20.30.40
User opsadmin
IdentityFile ~/.ssh/opsadmin_target
IdentitiesOnly yes
ProxyJump company-jump
ForwardAgent no
設定完成後只要執行:
ssh internal-app
它的連線路徑等價於以下命令:
ssh -J opsadmin@203.0.113.10:45222 opsadmin@10.20.30.40
company-jump 與 internal-app 是主機別名,兩個 IdentityFile 分別指定跳板機與目標主機的私鑰。IdentitiesOnly 限制用戶端只嘗試指定金鑰。ForwardAgent no 不會把管理者電腦的 SSH 驗證代理程式提供給遠端主機,可避免遠端主機借用代理程式中已載入的金鑰發起其他 SSH 驗證。
上一節修改的是管理者電腦上的 SSH 用戶端設定。這一節修改的是跳板機上的 OpenSSH 伺服器設定。管理者要將下列限制加入跳板機的 sshd_config 或 sshd_config.d 設定檔,由跳板機的 sshd 強制執行,使用者無法從自己的電腦繞過。
跳板機可以依照使用者或群組,讓轉送帳號與負責維運跳板機的管理帳號套用不同政策。轉送帳號只保留公鑰驗證及必要的本機 TCP 轉送(Local TCP Forwarding),再關閉 Shell、子系統(Subsystem)、X11 與驗證代理程式轉送。跳板機本身的維護工作則使用另一個權限受控的管理帳號。
Match User <轉送帳號>
AuthenticationMethods publickey
AllowTcpForwarding local
PermitOpen <授權目標位址>:22
PermitTTY no
MaxSessions 0
X11Forwarding no
AllowAgentForwarding no
各項設定分別處理不同能力:
| 設定 | 本篇用途 |
|---|---|
AuthenticationMethods publickey |
要求該帳號完成公鑰驗證。 |
AllowTcpForwarding local |
只允許從 SSH 用戶端角度發起的本機/直接 TCP 轉送(Local/Direct TCP Forwarding),不開放遠端轉送(Remote Forwarding)。 |
PermitOpen host:port |
把 TCP 轉送的目的地限制為指定主機與連接埠。可用空白列出多個目的地。 |
PermitTTY no |
禁止配置虛擬終端,但單獨使用時仍不足以禁止所有非互動命令。 |
MaxSessions 0 |
禁止 Shell、登入與 SFTP 等子系統工作階段,同時保留純轉送。 |
X11Forwarding no |
禁止 X11 圖形介面轉送。 |
AllowAgentForwarding no |
禁止把用戶端的驗證代理程式(Authentication Agent)轉送到該帳號的工作階段。 |
PermitOpen 會比對 SSH 用戶端要求的目的主機與連接埠。OpenSSH 不會替這個欄位進行名稱解析或一般模式比對,因此設定中的 IP、主機名稱與用戶端實際提出的字串要一致。它只限制由這條 SSH 連線建立的 TCP 轉送,不是完整的作業系統沙箱(Sandbox)。
OpenSSH 官方文件也特別提醒:如果使用者仍能取得跳板機 Shell,只關閉或限制 SSH 轉送並不能阻止他執行其他網路工具。完整設計因此還要同時做到:
MaxSessions 0 從 SSH 協定層禁止 Shell、命令與子系統通道。修改 sshd_config 後,先以 sshd -t 驗證語法,再使用以下命令查看特定使用者經過所有 Match 區塊後的有效設定:
sudo sshd -t
sudo sshd -T -C user=<使用者>,host=<跳板機>,addr=<來源位址> |
grep -E 'authenticationmethods|allowtcpforwarding|permitopen|permittty|maxsessions|allowagentforwarding'
確認輸出符合預期後才重新載入 OpenSSH。
多人隔離不能只依賴 PermitOpen。一條完整的管理路徑會依序經過網路防火牆、跳板機 sshd 與目標主機,每一處都應執行自己能判斷的條件:

圖(四)完整管理路徑由網路防火牆、跳板機與目標主機分別執行各自能判斷的限制。
跳板機與目標主機位於不同 VLAN,流量會經過網路防火牆。防火牆看到的是跳板機發往目標主機的新 TCP 連線,通常無法直接知道這條連線屬於哪位 SSH 使用者,因此適合限制「整台跳板機最多可以接近哪些受管主機」。PermitOpen 位於已經驗證個人帳號的 sshd 中,適合限制「這位使用者可以要求轉送到哪裡」。目標主機最後驗證自己的帳號與金鑰,並以群組、檔案權限及 sudoers 決定登入後能做甚麼。
假設 Alice 只能管理應用程式主機,Bob 只能管理監控主機,政策可以整理成:
| 使用者 | 跳板機允許轉送 | 目標主機允許登入 | 目標作業系統權限 |
|---|---|---|---|
| Alice | 應用程式主機的 TCP 22 | 只安裝 Alice 的目標主機公鑰 | 只授予應用程式維運權限 |
| Bob | 監控主機的 TCP 22 | 只安裝 Bob 的目標主機公鑰 | 只授予監控維運權限 |
| 跳板機管理者 | 不受一般轉送帳號規則限制 | 依維運需求另行授權 | 只管理跳板機本身 |
用戶端的 ~/.ssh/config 不列入這張授權交集,因為它只是方便使用的連線設定。網路別名(Alias)也不應加入整個內部網段,否則防火牆層會失去縮小可達範圍的作用。
一條 ProxyJump 路徑會經過跳板機與目標主機,使用者也要在兩台主機分別完成驗證。每位管理者因此使用自己的帳號,並為兩個驗證位置各建立一組 SSH 金鑰。這兩組都是使用者驗證金鑰,差別在用途與安裝位置。
| 金鑰用途 | 公鑰安裝位置 | 通過驗證後取得的權限 |
|---|---|---|
| 跳板機登入金鑰 | 個人在跳板機帳號的 authorized_keys | 完成跳板帳號驗證,並依 PermitOpen 規則要求跳板機連向核准目標。 |
| 目標主機登入金鑰 | 個人在目標主機帳號的 authorized_keys | 登入該目標主機,取得帳號被授予的系統權限。 |
兩把私鑰都留在管理者電腦。跳板機只保存跳板機登入公鑰,目標主機只保存目標主機登入公鑰,因此其中一層的認證資料需要更換時,不必連帶更換另一層。
實際撤銷方式取決於要收回哪一項權限:
共用帳號會讓 Shell、檔案與 sudo 紀錄都顯示相同的作業系統身分。若連私鑰也共用,SSH 驗證紀錄中的金鑰指紋也相同,更難追查實際操作者。記錄帳號擁有者、金鑰指紋、授權目標、建立日期與撤銷日期,才能讓每次權限變更都有資料可查。
只成功登入一台主機,只能證明其中一條路徑可用,不能證明多人隔離成立。測試至少要包含:
Alice → Alice 的目標主機 應成功
Alice → Bob 的目標主機 應遭跳板機拒絕
Alice → 跳板機 Shell/SFTP 應失敗
Bob → Bob 的目標主機 應成功
Bob → Alice 的目標主機 應遭跳板機拒絕
Bob → 跳板機 Shell/SFTP 應失敗
跳板機管理帳號 → 跳板機 Shell 應依既有政策成功
不同錯誤代表不同執行點:
administratively prohibited 或 open failed:常見於跳板機拒絕該轉送目的地。Permission denied (publickey):已抵達某一台 SSH 伺服器,但帳號或金鑰驗證失敗。ssh -vv 會開啟第二級詳細偵錯輸出,顯示用戶端讀取哪些設定、如何連上跳板機、嘗試哪些金鑰,以及轉送要求在哪一步成功或遭拒。測試時應同時比對用戶端輸出、跳板機與目標主機的 SSH 紀錄,以及防火牆紀錄,才能判斷拒絕發生在網路、轉送政策或目標主機登入驗證。
本次讓 Alice 與 Bob 共用 jump01,但使用不同帳號與金鑰。Alice 只允許前往 app01,Bob 只允許前往 monitor01。測試會同時檢查合法路徑、交叉越權與跳板機 Shell,確認允許與拒絕都發生在預期位置。
GitHub 實作文件:Day 16|ProxyJump 與多人權限隔離
依照實作文件建立 app01(VMID 211,10.77.20.31)與 monitor01(VMID 251,10.77.20.51),兩台主機在本日只提供 OpenSSH。管理者電腦為每個人分別建立跳板機與目標主機金鑰,私鑰全部留在管理者電腦:
| 使用者 | jump01 的帳號與金鑰 | 核准目標的帳號與金鑰 |
|---|---|---|
| Alice | alice/alice_bastion |
app01 的 alice/alice_target |
| Bob | bob/bob_bastion |
monitor01 的 bob/bob_target |
jump01 上的 Alice 與 Bob 使用 /usr/sbin/nologin,只承載 TCP 轉送。目標主機上的同名帳號才具有一般 Shell,而且不加入 sudo 群組。各主機分別以自己的 authorized_keys 驗證公鑰,因此跳板機與目標主機是兩個獨立授權點。
在 jump01 的 30-bastion-users.conf 以 Match User 分開設定兩位使用者:
Match User alice
AuthenticationMethods publickey
AllowTcpForwarding local
PermitOpen 10.77.20.31:22
PermitTTY no
MaxSessions 0
X11Forwarding no
AllowAgentForwarding no
Match User bob
AuthenticationMethods publickey
AllowTcpForwarding local
PermitOpen 10.77.20.51:22
PermitTTY no
MaxSessions 0
X11Forwarding no
AllowAgentForwarding no
保留既有管理連線,先執行 sudo sshd -t,確認語法正確後才重新載入服務。接著展開兩個帳號的有效設定:
sudo sshd -T -C user=alice,host=jump01,addr=192.168.0.14 |
grep -E 'authenticationmethods|allowtcpforwarding|permitopen|permittty|maxsessions|allowagentforwarding'
sudo sshd -T -C user=bob,host=jump01,addr=192.168.0.14 |
grep -E 'authenticationmethods|allowtcpforwarding|permitopen|permittty|maxsessions|allowagentforwarding'

圖(五)Alice 只允許轉送到 10.77.20.31:22,Bob 只允許轉送到 10.77.20.51:22。兩個帳號的 PermitTTY 為 no、MaxSessions 為 0。
將 app01 與 monitor01 加入既有的 BASTION_TARGETS,再於 LAB_INTERNAL 建立來源為 BASTION_HOST、目的地為 BASTION_TARGETS、目的連接埠為 TCP 22 的通過規則。

圖(六)BASTION_TARGETS 集中保存六個預先規劃的目標位址,以及本日加入的 app01 與 monitor01。

圖(七)ALLOW_BASTION_TO_AUTHORIZED_SSH 位於內部網段封鎖規則之前,只允許 BASTION_HOST 前往 BASTION_TARGETS 的 SSH 流量。
OPNsense 限制整台 jump01 最多可以接近哪些主機,PermitOpen 再限制個別使用者能要求轉送到哪一台主機。目標主機最後仍會驗證自己的帳號與公鑰。
Windows 的 OpenSSH 設定分別指定跳板機金鑰、目標主機金鑰與 ProxyJump。依照實作文件,先以 Alice 的主機別名登入:
ssh app01-via-jump
登入後在 app01 執行:
hostnamectl --static
whoami
id
exit
回到 Windows PowerShell,再以 Bob 的主機別名登入:
ssh monitor01-via-jump
登入後在 monitor01 執行:
hostnamectl --static
whoami
id
exit
Alice 應顯示 app01 與 alice,Bob 應顯示 monitor01 與 bob。這不只檢查 TCP 22 可達,也確認兩段 SSH 驗證後取得的是預期目標身分。

圖(八)Alice 透過 ProxyJump 登入 app01 後取得 alice 身分,Bob 登入 monitor01 後取得 bob 身分。
將 ProxyJump 使用者改連到另一人的目標,並保留詳細輸出中的拒絕訊息:
ssh -vv -o BatchMode=yes -o ConnectTimeout=10 -J iron-jump-alice `
-i "$env:USERPROFILE\.ssh\ithome_day16\alice_target" `
alice@10.77.20.51
ssh -vv -o BatchMode=yes -o ConnectTimeout=10 -J iron-jump-bob `
-i "$env:USERPROFILE\.ssh\ithome_day16\bob_target" `
bob@10.77.20.31

圖(九)Bob 要求轉送到未授權的 app01 時,jump01 回覆 administratively prohibited,表示要求在抵達目標主機前已被 PermitOpen 拒絕。
兩張負向測試截圖中的 ithome_day17 是系列天數調整前建立的本機資料夾名稱,不影響既有測試結果。本文的示範路徑統一使用 ithome_day16。

圖(十)Alice 要求轉送到未授權的 monitor01 時,同樣由 jump01 回覆 administratively prohibited。兩個方向的交叉越權均在抵達目標主機前遭到拒絕。
最後直接登入兩個跳板帳號:
ssh -o BatchMode=yes -o ConnectTimeout=10 iron-jump-alice
ssh -o BatchMode=yes -o ConnectTimeout=10 iron-jump-bob
兩次都應在要求工作階段通道時遭到拒絕並關閉連線。這項結果要與驗證三合併判讀:ProxyJump 可以使用 direct-tcpip 通道抵達核准目標,但 Alice 與 Bob 不能在 jump01 建立 Shell 工作階段。

圖(十一)Alice 與 Bob 直接登入 jump01 時,工作階段通道均開啟失敗並關閉連線。
| 正文敘述 | 驗證方式 | 目前結果 |
|---|---|---|
Match User 可以為每位使用者套用不同限制 |
sshd -T -C 展開有效設定 |
Alice 與 Bob 的 PermitOpen 目的地不同,且都禁止 TTY 與 Shell 工作階段 |
| 網路層與 SSH 使用者層負責不同範圍 | OPNsense Alias/Rule 與 PermitOpen |
防火牆限制跳板機可接近的主機,PermitOpen 限制個人可要求的目的地 |
| ProxyJump 會在目標主機再次驗證身分 | 兩條合法路徑回傳 hostname、whoami 與 id |
Alice 取得 app01 的 alice 身分,Bob 取得 monitor01 的 bob 身分 |
| 個人限制可以阻擋交叉越權 | Alice、Bob 交換目標執行 ssh -vv |
兩個方向均由 jump01 以 administratively prohibited 拒絕 |
| 轉送帳號不提供 jump01 Shell | 直接登入 iron-jump-alice 與 iron-jump-bob |
兩個帳號均無法開啟工作階段通道,連線隨後關閉 |
正式環境應由身分管理流程建立與撤銷個人帳號及金鑰,保存帳號擁有者、金鑰指紋、核准目標、建立日期與撤銷日期,並定期重做正向與負向測試。受管主機數量增加後,可評估使用短效 SSH 憑證集中管理信任。跳板機本身仍應持續更新、監控登入紀錄,並限制管理來源。
Match User、PermitOpen 與 MaxSessions 0 限制個人可轉送的目標與可建立的 SSH 通道。jump01 可接近的 SSH 目標,再由目標主機驗證個人帳號與公鑰。下一篇會建立 OpenVPN 遠端存取,說明 PKI、伺服器/用戶端憑證(Server/Client Certificate)、隧道網段(Tunnel Network)、分流隧道(Split Tunnel)、DNS 與群組防火牆政策,讓不同使用者透過 VPN 抵達各自被授權的私有服務。