docs: document STUN port change to 3479

NetBird's default STUN port 3478/udp conflicts with Headscale on
192.168.42.126, which DNAT'd it first. Reconfigure NetBird to use
3479/udp end-to-end (server config + docker-compose + OpenWrt DNAT).

Updates server config:
- /opt/netbird/config.yaml: stunPorts: [3479]
- /opt/netbird/docker-compose.yml: 3479:3479/udp

OpenWrt DNAT rule added separately (42_10 repo commit 9cc6141).
This commit is contained in:
2026-04-18 10:24:50 +08:00
parent fc9d72e12d
commit 31a874f3be
4 changed files with 109 additions and 2 deletions

View File

@@ -17,6 +17,7 @@
| [netbird-selfhosted-setup.md](./netbird-selfhosted-setup.md) | 完整安裝指南(含原理說明) | | [netbird-selfhosted-setup.md](./netbird-selfhosted-setup.md) | 完整安裝指南(含原理說明) |
| [npm-grpc-fix.md](./npm-grpc-fix.md) | 透過 Nginx Proxy Manager 對外公開時的 gRPC 修復 | | [npm-grpc-fix.md](./npm-grpc-fix.md) | 透過 Nginx Proxy Manager 對外公開時的 gRPC 修復 |
| [setup-keys-and-pat.md](./setup-keys-and-pat.md) | Setup Keys 概念、PAT 用法、以及 API 無法改名的繞道做法 | | [setup-keys-and-pat.md](./setup-keys-and-pat.md) | Setup Keys 概念、PAT 用法、以及 API 無法改名的繞道做法 |
| [stun-port-conflict.md](./stun-port-conflict.md) | STUN 3478 被 Headscale 佔用 → NetBird 改用 3479 的解法 |
| [SUMMARY.md](./SUMMARY.md) | 部署摘要與疑難排解記錄 | | [SUMMARY.md](./SUMMARY.md) | 部署摘要與疑難排解記錄 |
## 快速開始 ## 快速開始
@@ -51,6 +52,7 @@ netbird up --management-url http://192.168.42.127
|------|-----| |------|-----|
| 主機 | `192.168.42.127` | | 主機 | `192.168.42.127` |
| 系統 | Ubuntu 24.04 LTS | | 系統 | Ubuntu 24.04 LTS |
| STUN port | **3479/udp** (非標準 — 3478 被 Headscale 佔用) |
| 容器類型 | Proxmox LXC (非特權) | | 容器類型 | Proxmox LXC (非特權) |
| Docker | 29.4.0 | | Docker | 29.4.0 |
| OCI Runtime | crun 1.20 | | OCI Runtime | crun 1.20 |

View File

@@ -82,6 +82,7 @@ reopen fd 8: permission denied
| 502 Bad Gateway所有 API 端點) | netbird-server 下載 GeoLite2 中,尚未開始監聽 port 80 | 等待 2-5 分鐘 | | 502 Bad Gateway所有 API 端點) | netbird-server 下載 GeoLite2 中,尚未開始監聽 port 80 | 等待 2-5 分鐘 |
| 手機 App`server closed the stream without sending trailers` | 透過 NPM 對外公開時NPM 用 `proxy_pass` 而非 `grpc_pass` 轉 gRPC加上 Caddy `:80` 預設不支援 h2c | NPM 加 `advanced_config`gRPC 路由)+ Caddy 全域啟用 `protocols h1 h2 h2c`;見 [npm-grpc-fix.md](./npm-grpc-fix.md) | | 手機 App`server closed the stream without sending trailers` | 透過 NPM 對外公開時NPM 用 `proxy_pass` 而非 `grpc_pass` 轉 gRPC加上 Caddy `:80` 預設不支援 h2c | NPM 加 `advanced_config`gRPC 路由)+ Caddy 全域啟用 `protocols h1 h2 h2c`;見 [npm-grpc-fix.md](./npm-grpc-fix.md) |
| `PUT /api/setup-keys/{id}` 回 200 但 `name` 沒改 | NetBird API 只更新 `auto_groups``revoked``name` 是 read-only | 改 `store.db``setup_keys.name` → 重啟 `netbird-server`;見 [setup-keys-and-pat.md](./setup-keys-and-pat.md) | | `PUT /api/setup-keys/{id}` 回 200 但 `name` 沒改 | NetBird API 只更新 `auto_groups``revoked``name` 是 read-only | 改 `store.db``setup_keys.name` → 重啟 `netbird-server`;見 [setup-keys-and-pat.md](./setup-keys-and-pat.md) |
| peer 之間一直走 Relay`[stun:...:3478] is Checking...` | STUN port 3478 被 Headscale (`.126`) 搶先佔用 DNAT | NetBird 改用 3479/udpserver config + OpenWrt DNAT見 [stun-port-conflict.md](./stun-port-conflict.md) |
## 檔案位置 ## 檔案位置
@@ -93,7 +94,8 @@ reopen fd 8: permission denied
├── SUMMARY.md ← 本檔(部署摘要) ├── SUMMARY.md ← 本檔(部署摘要)
├── netbird-selfhosted-setup.md ← 完整安裝指南 ├── netbird-selfhosted-setup.md ← 完整安裝指南
├── npm-grpc-fix.md ← 經 NPM 對外公開時的 gRPC 修復 ├── npm-grpc-fix.md ← 經 NPM 對外公開時的 gRPC 修復
── setup-keys-and-pat.md ← Setup Keys / PAT 操作與 API 限制繞道 ── setup-keys-and-pat.md ← Setup Keys / PAT 操作與 API 限制繞道
└── stun-port-conflict.md ← STUN 改用 3479 (3478 被 Headscale 佔用)
``` ```
### 遠端伺服器192.168.42.127:/opt/netbird/ ### 遠端伺服器192.168.42.127:/opt/netbird/

View File

@@ -269,7 +269,7 @@ Client Request (:80)
| 端口 | 協定 | 用途 | | 端口 | 協定 | 用途 |
|------|------|------| |------|------|------|
| 80 | TCP | Dashboard + API + Signal + Relay | | 80 | TCP | Dashboard + API + Signal + Relay |
| 3478 | UDP | STUN (NAT 穿透) | | 3478 | UDP | STUN (NAT 穿透)**本部署改用 3479見 [stun-port-conflict.md](./stun-port-conflict.md)** |
## 常用維運指令 ## 常用維運指令

103
stun-port-conflict.md Normal file
View File

@@ -0,0 +1,103 @@
# 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 READMEOpenWrt port 表3479 在這裡記錄)
## 為什麼不關掉 Headscale
使用者仍在使用 Headscale沒有遷移計畫。兩套 overlay 網路並存是有意的安排。