MediaMTX 開 Port 風險與 VPN 收斂架構筆記

本筆記說明推流伺服器端的網路邊界防禦。內容有三項:評估 MediaMTX 開放公網 port 的潛在威脅;用 VPN 收斂架構,完全關閉對外開放的 RTMP/SRT port;使用動態 IP 白名單 Hot Reload 機制。VPN 收斂架構的意思是:把對外暴露的 port 縮減到單一 VPN 通道。

管理工具的底層使用零信任環境下的密碼學實作。Cloudflare Tunnel 信任邊界和 ECDH 應用層加密技術,請參閱 應用層端對端加密筆記。

1. 現況架構與網路拓撲

目前系統對公網暴露的 port 如下:

Port 網路協議 實際用途
443 Xray VLESS Reality 供中國大陸現場通過 GFW 使用的代理出口(保持獨立)
1935 RTMP(明碼) 台灣現場戶外推流 → 暴露在公網的 MediaMTX Ingest 入口
8890 SRT(特徵明顯) 備用的高丟包現場推流 → 暴露在公網的 MediaMTX Ingest 入口

鏈路一:台灣戶外直接推流鏈(本次安全重構的核心)

flowchart TD
A["手機 PrismLive / Moblin"] -->|未收斂拓撲| E["MediaMTX 公網直接暴露
公網 IP:1935/8890
暴露主要攻擊面"] E --> F["OBS 本機拉流"]

鏈路二:中國大陸現場科學上網鏈(完全獨立,不經過 MediaMTX)

flowchart LR
A["中國現場 iPhone"] --> B["Shadowrocket VLESS Reality"]
B --> C["伺服器 Xray:443 出口"]
C --> D["跨境直推 Twitch 伺服器"]
          
架構邊界釐清:鏈路二中,Xray 只作為網路層的科學上網代理工具。流量直接轉發到境外直播平台。MediaMTX 完全不參與鏈路二的資料處理。

常見誤解釐清:兩條推流鏈是否有交集?

鏈路一(台灣戶外) 鏈路二(中國大陸現場)
推流來源 台灣手機 中國大陸現場 iPhone
推流目的地 本機 MediaMTX Twitch(直接)
經過 MediaMTX 是 否
科學上網工具 WireGuard(台灣境內不需要科學上網) Shadowrocket + VLESS+Reality
主要安全風險 1935/8890 port 暴露 Xray 被 GFW 識別或封鎖
本頁涵蓋範圍 是 否(見 VPN 代理筆記)
常見誤解:「中國大陸現場用 Shadowrocket 連上伺服器,是推流進 MediaMTX 嗎?」不是。在這個情境中,Shadowrocket 只是科學上網工具。Shadowrocket 讓中國大陸現場的流量繞過 GFW。PrismLive 的推流目標是 Twitch。Xray 只提供出口。MediaMTX 完全不參與。如果未來要讓中國大陸現場推流進 MediaMTX(例如需要轉播),那是完全不同的架構。這種架構必須對中國 IP 開放 MediaMTX 的認證,還必須解決 GFW 封鎖 RTMP 的問題。複雜度遠高於現在的方案,需要另外規劃。

2. MediaMTX 面臨的安全問題

在安全威脅建模中,網路邊界防禦遵循「三階段風險模型」。必須注意:開放公網 port 必然引發探測。隱蔽不等於安全。

階段 1
全球背景掃描
全網自動化探測。無法避免
→
階段 2
主動探測識別
依據協議特徵識別服務身份
→
階段 3
精準目標攻擊
觸發漏洞、盜用或消耗資源

核心概念:VPN 收斂架構的核心價值是降低「攻擊面(Attack Surface)」。VPN 收斂架構把對外暴露點從多個服務 port 縮減到單一高強度加密通道。VPN 收斂架構不提高 MediaMTX 應用程式本身的程式碼安全性。

暴露 port 階段 1 背景掃描 階段 2 協議識別 階段 3 攻擊面風險評估
443 Reality 必然命中 極難看穿 (偽裝真實 TLS 1.3) 低風險,具備探測防禦
1935 RTMP 必然命中 秒級識別 (無任何偽裝機制) 高風險。容易遭到突發串流覆蓋和拒絕服務
8890 SRT 必然命中 秒級識別 (握手特徵固定) 高風險。未授權探測和頻寬消耗

用詞說明:Reality 的「極難看穿」的意思是:Reality 的設計目標是大幅提高主動探測和協議識別的難度。這不表示絕對不可識別。沒有任何協議能做這種保證。同樣,「必然命中」的意思是:掃描發現只是時間問題。這不表示每次掃描都會立即命中。

3. MediaMTX 加密現況檢查

檢查伺服器上 mediamtx.yml 核心傳輸層設定:

rtmpEncryption: "no"        # RTMP 明文傳輸
rtspEncryption: "no"        # RTSP 明文傳輸
srtReadPassphrase:          # 未設定加密金鑰
srtPublishPassphrase:       # 未設定加密金鑰
hlsEncryption: false        # HLS 切片未啟用靜態加密

釐清一個容易誤解的地方:「沒加密」不表示這些協議沒有封包格式。SRT 本身有自己的傳輸層機制(ARQ 重傳、FEC 前向糾錯、封包序號等)。這些機制不受上述設定影響,一直在運作。沒設 passphrase 的意思是:SRT 沒有啟用內建的 AES Payload Encryption。也就是說,媒體「內容」沒有加密。這和傳輸層機制無關。RTMP 同樣有完整的協議框架(chunk、message 格式等)。RTMP 只是沒有使用 TLS(RTMPS),所以內容以明碼傳輸。

結論:根據目前設定,RTMP、RTSP、SRT 和 HLS 的媒體內容都沒有啟用加密。(WebRTC 不在本次設定檢查範圍。協議規格強制要求 WebRTC 的媒體加密,不受人為設定影響。)

4. MediaMTX 認證漏洞缺陷

原設定檔中發布端權限的宣告有嚴重的安全隱患:

authInternalUsers:
  - user: any
    pass: 
    ips: []
    permissions:
      - action: publish
        path: live/key1
風險判定:在目前設定下,任何來源都可以直接 publish 指定的 path。這等於沒有有效的發布端存取控制。未授權者可以輕易用暴力掃描探測,並惡意覆蓋目前的串流。

5. 方案 A:VPN 收斂架構(攻擊面最小化)

要阻止全球自動化掃描器對 1935 和 8890 port 的持續探測,最有效的方法是在邊界路由器和防火牆上,完全關閉這些 port 的公網對外映射。

%%{init: {"themeVariables": {}}}%%
flowchart LR
A["手機直播端"] -->|WireGuard UDP 隧道| B["VPN Gateway"]
B -->|虛擬內網網卡 10.0.0.1| C["MediaMTX:1935/8890"]
          

封包解封裝流程:VPN Server 是隧道終點。VPN Server 會解封裝 VPN 封包,所以可以看到封包承載的 RTMP/SRT 流量。Internet 公網上的第三方探測器只能捕獲符合密碼學隨機分布的 UDP 封包。因此探測器看不到直播服務的特徵指紋。

6. Hysteria2 vs WireGuard 選型

VPN 收斂架構用於現場推流時,選型邏輯和一般科學上網不同:

台灣本地固定網路環境 → 首選 WireGuard

  • 核心優勢:協議非常輕量,封裝開銷很低,在核心態執行。
  • 手機端生態:官方客戶端非常成熟,支援 Split Tunnel 路由分流。

飯店/會場 Wi-Fi 等高阻礙環境 → 備選 Hysteria2

  • 核心優勢:基於 QUIC。Brutal 壅塞控制演算法可以對抗戶外弱網的高丟包。
  • 網路偽裝:模擬標準 HTTP/3 流量,可以穿透企業級防火牆對 UDP 的限制。

7. 方案 B:動態 IP 白名單工具

如果現場環境不方便安裝 VPN 客戶端,就必須開啟公網 port。這時,必須使用動態白名單工具,把暴露範圍控制到最小。

動態白名單工具基於 Node.js 執行。工具透過精準的文字標記(Marker),動態定位並更新 mediamtx.yml 內的 ips 授權清單。更新會觸發 MediaMTX 的 Hot Reload(熱加載) 機制。

# mediamtx.yml 內的動態標記範例
authInternalUsers:
  - user: liveu_streamer
    pass: $2b$10$BvR... (bcrypt雜湊)
    ips: ["220.135.x.x"] # @dynamic-ip-pool: production-field
    permissions:
      - action: publish
Hot Reload 行為特性:MediaMTX 核心偵測到設定檔變更時,會熱更新發布端的 IP 信任清單。這個過程不會中斷目前正在播放的其他路徑串流。因此維運可用性高。

8. 方案比較:VPN 收斂 vs IP 白名單自動化

面向 方案 A:VPN 收斂(第 5 節) 方案 B:IP 白名單自動化(第 7 節)
1935/8890 對外可見性 完全關閉。掃描器連 handshake 都無法完成 仍然開放,仍會被掃描發現(階段 1 無法避免)
RTMP/SRT 協議是否仍沒有保護 對 Internet 而言只看得到 VPN 流量,RTMP/SRT 封包只存在 VPN 隧道內部 是。協議本身仍無偽裝、無加密。只有 IP 白名單阻擋未授權來源
客戶端設定成本 必須安裝 VPN client,並保持連線 只需要在瀏覽器登入並點擊一次。不需要安裝額外的 App
架設成本 必須架設 WireGuard/Hysteria2 server 主要是部署 Cloudflare Tunnel
動態 IP 的處理 不受影響(使用 VPN 虛擬 IP,和公網 IP 無關) 必須由工具主動更新,否則白名單會失效
適合場景 長期、固定授權使用者的日常使用 需要輕量化、使用者不想安裝 VPN、臨時開放的場景

是否可以兩者疊加?

可以,而且這樣更穩健:以 VPN 收斂作為主要防線(1935/8890 完全不對公網開放)。IP 白名單工具作為備援,或給不方便安裝 VPN 的臨時使用者使用。如果某些場景需要暫時開放 1935/8890 給未安裝 VPN 的使用者測試,白名單機制可以把「開放範圍」限制到最小,而不是完全不設防。

9. 五層縱深防禦與最終架構

健全的系統不依賴單一防線。本架構用多層獨立的防線,建立標準的縱深防禦(Defense in Depth)體系:

graph TD
    L1["【第一層:VPN 網路邊界】
公網 1935/8890 完全關閉,未授權封包在邊界直接丟棄。"] L2["【第二層:動態 IP 白名單】
只允許信任的現場出口公網 IP 建立連線。"] L3["【第三層:發布端強認證】
MediaMTX 驗證強密碼,拒絕 any 匿名 publish 行為。"] L4["【第四層:連線速率限制】
限制單一 IP 的握手頻率,阻擋憑證碰撞和資源消耗。"] L5["【第五層:自動封鎖】
Fail2Ban 監控日誌,連續認證失敗時,直接在核心層封鎖來源。"] L1 --> L2 --> L3 --> L4 --> L5

整體整合架構圖

graph TD
    subgraph TW_Field ["════════ 台灣現場戶外 ════════"]
        Client["手機 / 攝影機推流端"] -->|只允許連到虛擬內網 IP 10.0.0.1| WG["[第一層: WireGuard 加密隧道]"]
    end

    subgraph Cloud_Boundary ["════════ 雲端防禦邊界 ════════"]
        WG -->|解封裝 VPN 流量| IPTables["[第二層: iptables 網路層動態白名單]"]
    end

    subgraph MMTX_Defense ["════════ MediaMTX 縱深防禦體系 (第三至五層) ════════"]
        MMTX["MediaMTX 核心五層縱深"]
        B1["- 監聽綁定: 10.0.0.1 (公網對外不可見)"]
        B2["- 發布控制: authInternalUsers 密碼校驗"]
        B3["- 常駐程式: Fail2Ban 即時日誌審查"]
        MMTX --> B1 --> B2 --> B3
    end

    OBS["OBS Studio 控制台"]
    IPTables --> MMTX
    B3 -->|內網迴圈拉流| OBS
    
    Platforms["Twitch / YouTube 平台"]
    OBS -->|安全轉發| Platforms
          
對外暴露面總結:VPN 收斂前,對外暴露 3 個 port:443(Reality)、1935(RTMP,無 TLS,可識別)和 8890(SRT,未啟用 Payload Encryption,可識別)。其中 2 個是明碼且無偽裝。VPN 收斂後,只剩 443(Reality)和 WireGuard port(UDP,加密)。1935/8890 完全不對公網開放。備援切換時,可以臨時對推流來源 IP 開放 1935。推流完畢後必須立即關閉。管理介面不佔用新的 port,而是走 Cloudflare Tunnel。這種方式的 payload 加密最完整,但 metadata 對 Cloudflare 仍然可見。中國大陸現場走 443 Xray VLESS Reality 的獨立鏈路。這條鏈路維持不變,不受本節架構調整影響。

10. 邊界安全檢查清單

MediaMTX 認證(立即處理)

  • 檢查 mediamtx.yml,確認 user: any 佔位符已完全移除。
  • 確認 srtPublishPassphrase / srtReadPassphrase 已填入密碼,啟用 AES Payload Encryption。
  • 確認 SRT 和 RTMP 的發布密碼都使用複雜的純英數字組合。建議不要使用特殊字元,避免設備解析異常。
  • 評估是否加裝 fail2ban 或連線速率限制。

VPN 收斂架構(方案 A)

  • 架設 WireGuard server(固定網路日常推流用)。
  • WireGuard 設定 split tunnel,AllowedIPs 只涵蓋 MediaMTX 網段。
  • MediaMTX 監聽介面 bind 到 VPN 虛擬網卡 IP。如果在 Windows 上開機綁定失敗,改用防火牆規則限制來源網段。
  • 推流 App 網址改成 VPN 內網 IP(不是公網 IP/DDNS)。
  • 確認新的 WireGuard port 和既有的 Hysteria2(如果也用 UDP 443)沒有 port 衝突。
  • 在 Router 上移除 1935 和 8890 的 port forward 規則。
  • 保留現有 Hysteria2 server 一組 client 設定,作為戶外/不可控網路的備援。
  • 確認常駐程式已正確設定 Restart=always,防止極端網路衝擊導致邊界服務崩潰。

IP 白名單自動更新工具(方案 B)

  • mediamtx.yml 對應行加上 # @dynamic-ip-pool: 名稱 標記。
  • MEDIAMTX_YML_PATH 環境變數指向正確路徑。
  • 用 add-user.js 建立帳號,確認密碼以 bcrypt 雜湊儲存到 users.json。
  • server-ecdh-keys.json 不可以加入任何 git repo 或雲端備份。
  • 服務只監聽 127.0.0.1,沒有直接 port forward。
  • 透過 Cloudflare Tunnel 對外開放,確認前後端在同一個 Express app。
  • 如果日後把 cloudflared 和 Node.js 拆到不同主機,必須補上 HTTPS。
  • 確認登入速率限制生效(15 分鐘內最多 8 次)。

長期維護

  • 定期檢查 MediaMTX、Xray、WireGuard、Node.js 依賴套件的版本更新。
  • 不使用的服務和 port forward 規則,建議立即關閉。這樣可以避免遺忘造成意外暴露。
  • 改設定檔後,如果沒有生效,先確認是不是欄位本身不支援 hot reload(MediaMTX 只有少數欄位不支援),而不是程式邏輯錯誤。