第 19 章

ngrok TCP 通道

Woow EMQX add-on 專屬的 ngrok TCP 通道:ngrok_enabled、authtoken、固定 TCP 位址、公開 1883 的風險,以及用 Cloudflare Tunnel 的替代。

為什麼要學這個

家裡的 EMQX 預設只接受區域網路的連線,外部裝置根本進不來。但只要你想「人在外面也能訂閱或發布」——例如出門要看客廳的感測器、想遠端把玄關的燈關掉——就需要在內網與外面世界之間開一條路。Woow EMQX 從 add-on 5.9.0 版本開始內建 ngrok,能用一行設定把 raw MQTT(連接埠 1883)變成公開的 TCP 通道。

如果你沒想清楚 authtoken、固定位址與暴露風險,等於把 broker 的入口直接掀給整個網際網路。讀完這章,你會懂:ngrok 三個選項各自控制什麼、公開網址要去哪一行 Log 找、什麼情況該改走 Cloudflare Tunnel,以及上線前必須先完成的保護動作。

核心概念

ngrok是內網穿透(tunnel)服務:它在你的機器上跑一個 agent,與 ngrok 雲端建立連線,外部流量就能經由它轉回到本地連接埠。Woow add-on 用這個做法,把 broker 的 1883 轉成一條公開的 TCP 位址,外部 MQTT 用戶端連這條位址即可發布或訂閱。

先分清楚兩層版本:add-on 版本5.9.0 只新增內建 ngrok TCP 通道,底下的核心仍是EMQX 5.8.9。你調整 ngrok_enabled、ngrok_authtoken 這類選項,修改的是 add-on 層,不會改變 EMQX 5.8.9 原有的驗證、ACL 或監控行為。

ngrok 通道有它自己的安全含義:把 1883 開到公網之後,任何能連到那條位址的人都能嘗試連線。EMQX 若尚未設定 Authentication,會允許所有 client 連線;所以「先設好驗證與 ACL,再決定是否開啟 ngrok」,是本章的黃金規範。

名詞對照

英文中文一句話
ngrok內網穿透服務把本地連接埠變成公開 TCP 端點
TCP TunnelTCP 通道直接轉發 raw MQTT(1883)的隧道
Authtoken授權碼你的 ngrok 帳號授權,啟用時必填
Reserved Address固定位址向 ngrok 保留的 TCP 位址,重啟後不變
Public URL公開網址ngrok 產生的外部端點,印在 Log
Cloudflare TunnelCloudflare 隧道替代方案,特別適合 WebSocket(8083)

其中 authtoken 是秘密值:它等同 ngrok 帳號權限。請不要把自己的 token 寫進 HTML 或貼進 log,教學一律以佔位符(YOUR_TOKEN、<你的 ngrok authtoken>)表示。

動手做

  1. 先檢查驗證狀態

    開啟 ngrok 前,先去 EMQX Dashboard 的 Access Control → Authentication 建立一組使用者,並在 Authorization 設好最小權限。至少要確保公開後不會讓網際匿名連線。

  2. 開啟 add-on 的 ngrok 設定

    到 Home Assistant:設定 → 附加元件 → Woow EMQX → 設定(Configuration)分頁,找到 ngrok 三個選項,把 ngrok_enabled 改成 true。

  3. 填入 authtoken(必要)

    在 ngrok_authtoken 貼上你的 ngrok 授權碼(此處以 <你的 ngrok authtoken> 代替)。若你已在 ngrok 帳號保留固定 TCP 位址,就填進 ngrok_tcp_addr;否則留空。

  4. 重啟並確認 Log

    回到概覽分頁按「重新啟動」。等 add-on 上線後,打開 Log 找一行形如 >>> MQTT ngrok: <public_url>,那就是外部 MQTT client 要連的公開位址。

add-on 的 config.yaml 把這三個選項的 schema 定義為:ngrok_enabled 是 bool(預設 false)、ngrok_authtoken 是 password?、ngrok_tcp_addr 是 str?。

ngrok 的三個選項

ngrok 功能由這三個選項控制。掌握型態與預設值,才知道自己到底開放了多少風險。

選項型態預設作用
ngrok_enabledboolfalse啟停 ngrok TCP 1883 通道
ngrok_authtokenpassword空帳號授權,開啟時必填
ngrok_tcp_addrstring空指定保留位址以取得固定端點

執行行為很直接:ngrok_enabled 為 false 時,ngrok service 直接 idle;啟用了但 authtoken 沒填則記一則錯誤並保持 idle(EMQX 本體照常運行)。authtoken 有值之後,才執行 ngrok tcp 1883,若填了 ngrok_tcp_addr 則是 ngrok tcp 1883 --remote-addr=<位址>。

若只想偶爾讓外部連一下,最單純就是 authtoken 有值、其餘留空,讓 ngrok 指派臨時位址。

公開位址與 Log

啟用 ngrok 時 add-on 會跑第二個服務 ngrok-announce:它每兩秒查一次 ngrok agent 的本地 API(http://127.0.0.1:4040/api/tunnels),最長查找約 120 秒,取得第一個隧道的 public URL 後印進 add-on Log。

所以「看公開網址」的位置是:Home Assistant → 設定 → 附加元件 → Woow EMQX → Log。若啟動後一時找不到,可能它還在等 ngrok 上線,最多約兩分鐘。若超過仍沒有,再去查看 ngrok service 自己的 log(常見原因是 authtoken 沒填好)。

這條公開 URL 很有實際作用:外部裝置的 MQTT client 設定必須用它(含端口),而不是你內網的 Home Assistant 位址。

公開 1883 的風險與替代

公開 raw MQTT 不是無腦把埠推出去。看完以下幾點再決定:

  • 任何人都能嘗試連線:ngrok 位址本身就是公開的,等於把 broker 的 1883 入口送到整個網際網路。Authentication 沒設好時,EMQX 會放行匿名 client。
  • TLS 需另行處理:ngrok TCP 通道是原始 TCP,不會自動為整條連線加密。若要加密,請改用或另開 MQTTS(8883)式的監聽。
  • 臨時位址不持久:不填固定位址時,ngrok 自動指派的端點在每次重啟都可能變號。
  • 維持最小暴露:若只是偶爾需要,可只在特定時段開、用完就關;長期對外,務必先完成驗證、ACL 與必要的 TLS。

如果你的需求是 WebSocket 而不是 raw TCP,就接不上:add-on 的 ngrok 只涵蓋 1883(raw MQTT),8083(MQTT over WebSocket)不在 ngrok 範圍,此時請改用 Cloudflare Tunnel。

故障排除

  • 已啟用但 Log 沒出現 URL:先確認 ngrok_enabled 是 true、authtoken 有填;再看 Log 是否有「已啟用但 authtoken 為空」的錯誤。若都正確仍沒有,重啟 add-on 或查看 ngrok service 自己的 log。
  • 只看到「ngrok 未啟用」:代表設定沒有生效。回到設定分頁檢查 ngrok_enabled,重新啟動後再看 Log。
  • 外部 client 連不上:確認是用 ngrok 印出的完整網址與端口,而不是內網位址;並確認已在 EMQX 建立帳號、ACL 允許該主題。位址若常變,就改用固定位址(ngrok_tcp_addr)。
  • 發現公網上的任何 client 都能連進 1883:先把 ngrok 關掉,回到 EMQX 設好 Authentication 與 Authorization,再考慮要不要重新公開。匿名可連的公開 broker 是很明顯的警訊。

常見問題

一定要用 ngrok 才能讓外網連 EMQX 嗎

不是。ngrok 只是 add-on 內建最方便的方法;VPN、Cloudflare Tunnel 或其他反向代理一樣能轉到 1883。若你本來就有 VPN 或內網穿透,優先沿用,能減少把 broker 推到公網。

自動位址每次重啟真的都會變嗎

很可能。ngrok 自動指派的臨時 TCP 端點在重開後常會換號,所以希望固定的公開端點時,就到 ngrok 帳號保留並填進 ngrok_tcp_addr,如此重啟後同一個位址可以維持。

8083(MQTT over WebSocket)能用 ngrok 嗎

不行。add-on 的 ngrok 只處理 1883 raw MQTT;要對外提供 WebSocket 就改用 Cloudflare Tunnel,這是官方 CHANGELOG 與 README 指明的替代方向。

公開之前至少要完成哪些設定

至少要能拒絕匿名連線:在 EMQX 的 Access Control → Authentication 建立驗證(建議 Password-Based → Built-in Database),再以 Authorization 設定最小權限。若要加密,則考慮 MQTTS(8883)Listener。

官方來源