# NetBird STUN Port 衝突:改用 3479/udp ## 情境 家用 infra 同時跑 **Headscale (`192.168.42.126`)** 跟 **NetBird (`192.168.42.127`)** 兩套 mesh VPN。兩者都想用標準 STUN port **`3478/udp`**,但單一 WAN port 只能 DNAT 到一個內網 host。 Headscale 比較早部署,已經占用了 WAN `:3478/udp → .126`。NetBird 因此必須改用其他 port 對外。 ## 決策 採用「**NetBird 全面改用 3479/udp**」: - 內部 netbird-server 的 STUN 監聽改為 `3479` - Docker 容器對外 expose `3479:3479/udp` - OpenWrt 新增 DNAT `WAN :3479/udp → 192.168.42.127:3479` - 客戶端收到的 STUN URL 會是 `stun:netbird.timmy.us.kg:3479` Headscale 那條 `3478/udp → .126` 完全不動。 ## 實施步驟 ### 1. 修改 `/opt/netbird/config.yaml` ```yaml server: # ... stunPorts: - 3479 # 原本是 3478 ``` ### 2. 修改 `/opt/netbird/docker-compose.yml` ```yaml netbird-server: # ... ports: - "3479:3479/udp" # 原本是 "3478:3478/udp" ``` ### 3. 重新建立容器 ```bash cd /opt/netbird docker compose up -d ``` `docker compose up -d` 會偵測到 port 變更、重建 `netbird-server` 容器。其他兩個(caddy、dashboard)不動。 ### 4. OpenWrt 加 DNAT 在 `192.168.42.10` 上加 UCI redirect(詳見 `42_10` repo): ``` name: NetBird-STUN src: wan → dest: lan src_dport: 3479 → dest_ip: 192.168.42.127, dest_port: 3479 proto: udp target: DNAT ``` ### 5. 驗證 外部 UDP 連通性: ```bash nc -zvu -w 3 125.229.110.50 3479 # Connection to 125.229.110.50 port 3479 [udp/...] succeeded! ``` 客戶端 STUN 正確指向新 port: ```bash netbird status --detail | grep stun: # [stun:netbird.timmy.us.kg:3479] is Available ``` ## 既有客戶端要重連 已連線的 NetBird 客戶端會在啟動時向 management server 拿一次 STUN URL,**變更後舊客戶端仍握著舊 URL**。每台已註冊裝置需: ``` Disconnect → Connect ``` 或重啟 App / daemon 讓它重新抓設定。沒重連前仍會走 Relay,不會斷線但無法 P2P。 ## 疑難排解 | 症狀 | 可能原因 | |------|---------| | `[stun:...:3479] is Checking...` 長時間沒變 Available | OpenWrt DNAT 未生效或 NetBird 容器沒在新 port 監聽 | | Available 但 `Connection type: Relayed` 不變 | 客戶端沒重連 / 兩邊都在嚴格 NAT 後 / iOS 省電模式阻擋 ICE | | iPhone App 重連後仍是 Relay | 檢查 iOS 的 Low Data Mode 或 WiFi/行動網路切換;P2P 對稱 NAT 時無解,保持 Relay 正常運作 | ## 相關檔案 - `/opt/netbird/config.yaml`(伺服器) - `/opt/netbird/docker-compose.yml`(伺服器) - `42_10` repo README(OpenWrt port 表,3479 在這裡記錄) ## 為什麼不關掉 Headscale? 使用者仍在使用 Headscale,沒有遷移計畫。兩套 overlay 網路並存是有意的安排。