在 Linux / Windows 建立 TCP Port 重導跳板
| | | 0 | |
有 AI 之後排除電腦疑難雜症的速度愈來愈快,但漸漸覺得自己愈來愈置身事外,像是水電壞了花錢找人來修,師傅沒三兩下說弄好了收 300 Token,我回了一聲「哦」,船過水無痕,怎麼壞的不知、怎麼修好的不知,什麼知識也沒留下。對連換燈泡都不想自己動手的人來說,這真是美極了,但我大半輩子沈迷於搞懂運作原理以及把東西修好的樂趣,一時無法適應這種空虛感。在戒掉這種「惡習」之前,我就繼續維持處理完問題寫篇筆記的習慣吧。
最近在弄一個實驗環境,靠一堆古董裝置:22 歲的 Buffallo WHR-G54S 無線路由器、10 歲的 AC66U、8 歲迷你電腦 N3150,一群老兵搞出一個雙網段拓樸。

實際狀況複雜得多,為方便說明簡化如下。兩個網段各自有路由器,彼此不互通:
- LAN 2 (192.168.2.0/24,Router 192.168.2.254):我的 Windows 客戶端 192.168.2.168。
- LAN 1 (192.168.1.0/24,Router 192.168.1.254):目標主機 RHEL 192.168.1.100,提供 SSH (22) 與 VNC (5901)。

我要從 192.168.2.168 連到 192.168.1.100,最簡單的解法是在 192.168.1.254 跟 192.168.2.254 設定靜態路由,讓兩個網段直接互通,但因為一些考量未採用。我這次分別嘗試用網段間的雙網卡主機(一台 Linux 一台 Windows)成功建立跳板,簡單做個記錄。
| 跳板 | LAN 2 端 | LAN 1 端 | 轉發方式 |
|---|---|---|---|
| 跳板 A:Debian 12 | 192.168.2.12 | 192.168.1.12 | rinetd |
| 跳板 B:Windows | 192.168.2.11 | 192.168.1.11 | netsh portproxy (IP Helper) |
目標是讓 192.168.2.168 透過任一台跳板連上 192.168.1.100 的 SSH 與 VNC。由於跳板本身 22 port 可能被自己的 sshd 佔用,統一把 RHEL 的 SSH 對應到跳板的 2222,VNC 維持 5901:
| 客戶端連線目標 | 轉發至 |
|---|---|
| 192.168.2.12:2222 或 192.168.2.11:2222 | 192.168.1.100:22 |
| 192.168.2.12:5901 或 192.168.2.11:5901 | 192.168.1.100:5901 |
問了 AI,Linux 與 Windows 都有三種做法,用 SSH Proxy Jump ssh -J、作業系統層 NAT 以及額外服務。SSH 跳板最安全,但若開放給多人存取需額外管理 SSH 帳號或金鑰;NAT 效能最好但設定複雜;最後我選擇用 Linux rinetd 及 Windows 的 netsh interface portproxy 搞定。理由是客戶端可直接連線、不必每次開啟 SSH 通道,且轉發程式會另外從跳板的 LAN 1 網卡建立一條連往 RHEL 的 TCP 連線,RHEL 的回應自然回到跳板,不用像 DNAT 要處理 masquerade 避免 RHEL 把回應送往預設閘道 192.168.1.254,形成非對稱路由而失敗。但相對地,rinetd 及 netsh interface portproxy 做法的缺點則是 RHEL 端看到的來源 IP 是跳板機,無法分辨真實來源,不利於 Logging 及管控。
| 做法 | Linux | Windows | 特性 |
|---|---|---|---|
| SSH 跳板 | ssh -J / ProxyJump | OpenSSH Server | 加密,但每次要開通道 |
| User-space TCP 轉發 | rinetd / socat | netsh interface portproxy | 常駐、設定最簡單、不需處理回程路由 |
| 核心層 NAT | nftables DNAT | RRAS NAT | 效能最好,需開 IP 轉發與 masquerade,設定繁瑣 |
跳板 A:Debian 12 + rinetd
我的 Linux 版本是 Debian 12 (bookworm) 使用 sudo apt install rinetd 安裝後,編輯 /etc/rinetd.conf,轉發規則格式為 bindaddress bindport connectaddress connectport [options...],port 預設為 TCP,可加 /udp 或 /tcp 指定。(參考)
# 可限定來源 IP (放在第一條轉發規則之前,即為全域規則)
# IP 可使用 ?、* 萬用字元
allow 192.168.2.168
# bindaddress bindport connectaddress connectport
192.168.2.12 2222 192.168.1.100 22
192.168.2.12 5901 192.168.1.100 5901
logfile /var/log/rinetd.log
設定後重啟服務。
# 也可用 `kill -HUP` 重新載入設定,不會中斷既有連線。
sudo systemctl restart rinetd
sudo systemctl enable rinetd
# 確認有在監聽
sudo ss -lntp | grep rinetd
跳板 B:Windows + netsh portproxy
Windows 內建的 netsh interface portproxy 功能與 rinetd 相當:監聽指定的 IP 與 port,收到連線後另外建立一條 TCP 連線轉送到目標。
portproxy 依賴 IP Helper (iphlpsvc) 服務,先確認服務已執行。
以系統管理員身分開啟 PowerShell:
Get-Service iphlpsvc
Set-Service iphlpsvc -StartupType Automatic
Start-Service iphlpsvc
接著設定規則:
# 指定聽 LAN 2 端的 192.168.2.11,避免用 `0.0.0.0` 連 LAN 1 端也一起監聽
netsh interface portproxy add v4tov4 listenaddress=192.168.2.11 listenport=2222 connectaddress=192.168.1.100 connectport=22
netsh interface portproxy add v4tov4 listenaddress=192.168.2.11 listenport=5901 connectaddress=192.168.1.100 connectport=5901
# 移除規則
# netsh interface portproxy delete v4tov4 listenaddress=192.168.2.11 listenport=2222
# 全部清除
# netsh interface portproxy reset
# 註:轉發規則存在登錄檔 (`HKLM\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp`)
# 查看設定
netsh interface portproxy show all
# 檢查是否成功監聽
netstat -ano | findstr :2222
Windows 預設防火牆會封鎖輸入,設定開放 2222、5901,若來源 IP 有限且固定可加上限制只對特定 IP 開放:
New-NetFirewallRule -DisplayName "PortProxy to RHEL" -Direction Inbound -Protocol TCP -LocalPort 2222,5901 -RemoteAddress 192.168.2.168 -Action Allow
設定完成,從 192.168.2.* 網段可使用以下指令連上 192.168.1.100 SSH Server:
# 經跳板 A
ssh -p 2222 [email protected]
# 經跳板 B
ssh -p 2222 [email protected]
VNC Viewer 則可使用 192.168.2.12:5901 或 192.168.2.11:5901 連上 192.168.1.100。註:VNC 為明文傳輸,並不算安全,本案例為實驗環境較單純尚可容忍。有個替代做法是先 ssh -p 2222 -L 5901:localhost:5901 [email protected] 用 SSH 建立 Tunnel,VNC Viwer 再改連 localhost:5901,如此更安全且可省掉一條 5901 轉送規則。
Comments
Be the first to post a comment