Document two lessons from the RTSP-camera-via-subnet-route diagnosis: 1. Existing NetBird clients don't automatically re-apply new route config; daemons stuck in old state need explicit restart per platform (launchctl kickstart / App reconnect / compose restart). Shows up as 'Networks: -' on status output and no entry in the system routing table. 2. Some iOS apps (notably certain RTSP players) don't use the system VPN route and bypass NetBird entirely even when route is correctly installed. Safari test distinguishes 'route broken' from 'app doesn't respect VPN'.
215 lines
7.5 KiB
Markdown
215 lines
7.5 KiB
Markdown
# Network Routes(Subnet Router)
|
||
|
||
讓 NetBird peers 透過某個「帶路人」存取 LAN 上**沒裝 NetBird 的**裝置(印表機、NAS、監視器、OpenWrt 介面等)。
|
||
|
||
## 概念
|
||
|
||
NetBird 的 peer 彼此在 `100.64.0.0/10` 的 overlay 網路互通。LAN 裡的印表機、NAS 沒 NetBird 客戶端,overlay 網路看不到它們。
|
||
|
||
**Subnet Router** 的作法:
|
||
|
||
1. 挑一個在家裡的 peer 當「帶路人」(Routing Peer)
|
||
2. 在 NetBird 後台設定「`192.168.42.0/24` 這段要透過這個 peer 走」
|
||
3. 在帶路人上開啟 IP forwarding + masquerade
|
||
4. 遠端 peer 的路由表拿到 `192.168.42.0/24 → <Routing Peer overlay IP>`,LAN 存取自動轉送到帶路人,由帶路人 NAT 進 LAN
|
||
|
||
```
|
||
Mac (外面咖啡店)
|
||
│
|
||
│ NetBird tunnel
|
||
▼
|
||
CT100 (家裡 192.168.42.100, 帶路人)
|
||
│
|
||
│ Masquerade (src NAT → 192.168.42.100)
|
||
▼
|
||
192.168.42.10 ← OpenWrt
|
||
192.168.42.40 ← Proxmox
|
||
192.168.42.X ← NAS / 印表機 / 任何 LAN 裝置
|
||
```
|
||
|
||
## 本部署的 Route
|
||
|
||
| 項目 | 值 |
|
||
|------|-----|
|
||
| Network ID | `home-lan` |
|
||
| CIDR | `192.168.42.0/24` |
|
||
| Routing Peer | `CT100` (`ct100.netbird.selfhosted`, LAN IP `192.168.42.100`) |
|
||
| Distribution Groups | `personal` — Mac、iPhone 的路由表會拿到這條 route |
|
||
| Access Control Groups | `personal`, `servers` |
|
||
| Masquerade | 開 |
|
||
| Metric | 9999(低優先度,不影響直連 peer 的流量) |
|
||
|
||
### 為何選 CT100 而不是 CT124
|
||
|
||
- **CT100** 的 Tailscale 跑 userspace 模式,沒 iptables-legacy 規則,不會跟 NetBird 的 route iptables 打架
|
||
- **CT124** 的 Tailscale 是 kernel-mode,有個既有的 `ts-input / ts-forward` DROP 100.64.0.0/10 非 tailscale0 規則(見 `peer-deployment-ops.md`)— 當 routing peer 的話 masquerade/forwarding 鏈更容易出狀況
|
||
|
||
CT100 的 SSH Jump Box 角色也更符合「gateway 型」peer 的語意。
|
||
|
||
## 部署步驟
|
||
|
||
### 1. Routing Peer 上開啟 IP forwarding
|
||
|
||
```bash
|
||
ssh root@192.168.42.100
|
||
|
||
# 即時
|
||
sysctl -w net.ipv4.ip_forward=1
|
||
|
||
# 持久化
|
||
cat > /etc/sysctl.d/99-netbird-route.conf << 'EOF'
|
||
net.ipv4.ip_forward=1
|
||
EOF
|
||
sysctl -p /etc/sysctl.d/99-netbird-route.conf
|
||
```
|
||
|
||
NetBird client 容器本身會 handle masquerade 相關 iptables 規則(前提:容器有 `NET_ADMIN` cap,且 `masquerade: true` 設在 route 上)。
|
||
|
||
### 2. 建 Network Route(API)
|
||
|
||
```bash
|
||
TOKEN=<你的 PAT>
|
||
CT100_PEER_ID="<從 GET /api/peers 查>"
|
||
PERSONAL_GID="<personal group id>"
|
||
SERVERS_GID="<servers group id>"
|
||
|
||
curl -X POST -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
|
||
-d "{
|
||
\"description\": \"Home LAN via CT100 jump box\",
|
||
\"network_id\": \"home-lan\",
|
||
\"network\": \"192.168.42.0/24\",
|
||
\"peer\": \"$CT100_PEER_ID\",
|
||
\"masquerade\": true,
|
||
\"metric\": 9999,
|
||
\"enabled\": true,
|
||
\"groups\": [\"$PERSONAL_GID\"],
|
||
\"access_control_groups\": [\"$PERSONAL_GID\", \"$SERVERS_GID\"]
|
||
}" \
|
||
https://netbird.timmy.us.kg/api/routes | jq
|
||
```
|
||
|
||
欄位說明:
|
||
- `peer` — 帶路人 peer ID(單一 peer;若要 HA 可用 `peer_groups`)
|
||
- `groups` — **路由分發群組**:這些群組的 peers 會在路由表拿到 `192.168.42.0/24 → 100.x.x.x(routing peer)`
|
||
- `access_control_groups` — **存取控管群組**:誰能使用這條 route
|
||
- `masquerade` — SNAT 給回應封包找得到路
|
||
- `metric` — 優先度,值越大越低優先;直連 peer 的路由 metric 小,不會被 subnet route 蓋掉
|
||
|
||
### 3. UI 操作(替代方式)
|
||
|
||
Dashboard → **Network Routes** → **Add Route**
|
||
- Network CIDR: `192.168.42.0/24`
|
||
- Network Identifier: `home-lan`
|
||
- Routing Peer: 選 CT100
|
||
- Distribution Groups: 選 `personal`
|
||
- Access Control Groups: 選 `personal`, `servers`
|
||
- Masquerade: **on**
|
||
- Metric: `9999`
|
||
|
||
## 驗證
|
||
|
||
### 從遠端 peer 確認路由表
|
||
|
||
macOS:
|
||
```bash
|
||
netstat -rn | grep 192.168.42
|
||
# 192.168.42 link#39 UCS utun20 ← NetBird 介面
|
||
```
|
||
|
||
Linux:
|
||
```bash
|
||
ip route | grep 192.168.42
|
||
# 192.168.42.0/24 dev wt0 scope link
|
||
```
|
||
|
||
### 測試 ping LAN 裝置
|
||
|
||
```bash
|
||
ping 192.168.42.10 # OpenWrt
|
||
ping 192.168.42.40 # pve-03
|
||
```
|
||
|
||
延遲應該相當於「到帶路人的 NetBird 延遲 + 帶路人到 LAN 設備的延遲」,本部署約 20ms。
|
||
|
||
## 排錯
|
||
|
||
### 症狀:route 建好、routing peer 運作正常,但舊客戶端連不到 LAN
|
||
|
||
**典型現象**:
|
||
- `netbird status --detail` 顯示 routing peer 有 `Networks: 192.168.42.0/24`
|
||
- 但 client 本機 `netstat -rn | grep 192.168.42` 沒有這條 route
|
||
- 或「有些 peer 可以連、有些不行」
|
||
|
||
**原因**:NetBird client 在 daemon 啟動時才建立完整 network map,之後 route 政策變更會 push 到 client,但**既有的連線狀態若卡住,沒辦法套用新 route**。
|
||
|
||
**修復(各平台)**:
|
||
- **macOS**:
|
||
```bash
|
||
sudo launchctl kickstart -k system/netbird
|
||
```
|
||
- **iOS**: App 內 Disconnect → 等 5 秒 → Connect
|
||
- **Linux systemd**: `sudo systemctl restart netbird`
|
||
- **Docker 容器**: `docker compose -f /opt/netbird/docker-compose.yml restart`
|
||
|
||
**本部署的實例(2026-04-18)**:subnet route 剛建好時測試通過,但 daemon 一直沒重啟的裝置(iPhone、CT124)後來出現 `Status: Connecting` 假象,route 也沒進 routing table。全裝置 kickstart / reconnect 後恢復。
|
||
|
||
### iOS RTSP / 特定 app 不走 NetBird route
|
||
|
||
iPhone 上**某些 app** 不吃系統 VPN 路由(特別是直接用 socket API 的 RTSP 播放器),即使 NetBird route 設好也連不到。
|
||
|
||
**排查步驟:**
|
||
|
||
1. 在 iPhone Safari 打開 `http://<目標 LAN IP>/`
|
||
2. 如果能通 → NetBird route 正常,問題在那個 app 對 VPN 的支援
|
||
3. 如果不能通 → iPhone NetBird daemon 要重連(見上一節)
|
||
|
||
**常見解法**:
|
||
- 換一個支援 VPN 的 RTSP app(VLC 通常 OK)
|
||
- 或讓 app 用 FQDN 而非 IP(例如 `rtsp://192-168-42-150.netbird.selfhosted:554/stream1` 如果 NetBird DNS 有設)
|
||
|
||
### 路由表拿到但 ping 不通
|
||
|
||
通常是**帶路人的 IP forwarding 沒開**。再檢查:
|
||
|
||
```bash
|
||
ssh <routing peer>
|
||
sysctl net.ipv4.ip_forward
|
||
# 應為 1
|
||
```
|
||
|
||
### Masquerade 相關
|
||
|
||
NetBird client 會自動設定 iptables MASQUERADE 規則。如果 LAN 設備看到源 IP 是 `100.x.x.x`(NetBird overlay)而不是 `192.168.42.100`,表示 masquerade 沒生效 — 檢查 route 設定中 `masquerade: true`,且容器有 `NET_ADMIN` cap。
|
||
|
||
### 帶路人是 LXC 容器時
|
||
|
||
有些 LXC CT(非特權、沒 `NET_ADMIN`)不能改 iptables / MASQUERADE。確認 CT config 有:
|
||
|
||
```
|
||
features: nesting=1
|
||
lxc.cgroup.devices.allow: a # 或其他合適設定
|
||
```
|
||
|
||
本部署的 CT100 就是這樣配置,沒問題。
|
||
|
||
### 遇到 Tailscale 干擾
|
||
|
||
如果帶路人同時跑 Tailscale kernel-mode,`iptables-legacy` 的 `ts-forward` 會擋掉往 tailscale0 外的 forward。見 `peer-deployment-ops.md` 的 Tailscale 衝突章節。
|
||
|
||
**本部署避開這問題的方式:選 CT100(Tailscale userspace)當 routing peer**,不選 CT124。
|
||
|
||
## 安全考量
|
||
|
||
**Routing Peer 被攻破 = 攻擊者能透過 overlay 打進家裡所有 LAN 設備。**
|
||
|
||
降低風險:
|
||
- 帶路人只開最小必要服務(本部署 CT100 就是純 jump box,表面積小)
|
||
- 透過 `access_control_groups` 限制哪些 group 能走這條 route
|
||
- 考慮把 route 拆細:例如 `192.168.42.10/32`(只 OpenWrt)、`192.168.42.40/30`(只 PVE 節點)各自一條 route,不要整段開
|
||
- 未來可加上 port-specific 的 policy(例如 personal → home-lan 只開 22/443)
|
||
|
||
## 相關文件
|
||
|
||
- `groups-and-policies.md` — Network Route 用 group 控管存取
|
||
- `peer-deployment-ops.md` — Tailscale/NetBird iptables 衝突(選 routing peer 的考量)
|