Files
netbird-selfhosted/network-routes.md
Timmy ae9ce6fcc2 docs: fix NetBird exit node UI claim — checkbox not radio
Mac app actually lets you tick both 0.0.0.0/0 routes at once; kernel picks
one by insertion order (undefined), not load-balanced. Warn against it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:40:48 +08:00

12 KiB
Raw Permalink Blame History

Network RoutesSubnet 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

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 capmasquerade: true 設在 route 上)。

2. 建 Network RouteAPI

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 RoutesAdd 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:

netstat -rn | grep 192.168.42
# 192.168.42          link#39     UCS   utun20   ← NetBird 介面

Linux:

ip route | grep 192.168.42
# 192.168.42.0/24 dev wt0 scope link

測試 ping LAN 裝置

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:
    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-18subnet 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 appVLC 通常 OK
  • 或讓 app 用 FQDN 而非 IP例如 rtsp://192-168-42-150.netbird.selfhosted:554/stream1 如果 NetBird DNS 有設)

路由表拿到但 ping 不通

通常是帶路人的 IP forwarding 沒開。再檢查:

ssh <routing peer>
sysctl net.ipv4.ip_forward
# 應為 1

Masquerade 相關

NetBird client 會自動設定 iptables MASQUERADE 規則。如果 LAN 設備看到源 IP 是 100.x.x.xNetBird 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-modeiptables-legacyts-forward 會擋掉往 tailscale0 外的 forward。見 peer-deployment-ops.md 的 Tailscale 衝突章節。

本部署避開這問題的方式:選 CT100Tailscale userspace當 routing peer,不選 CT124。

Exit Nodes0.0.0.0/0 路由)

Exit node 是 subnet router 的特例CIDR 改成 0.0.0.0/0,讓選擇此路由的 peer 把所有網際網路流量經由該 routing peer 出去(等同 Tailscale 的 Exit Node、WireGuard 的 AllowedIPs = 0.0.0.0/0)。

本部署的 Exit Nodes

Network ID Routing Peer 出口 IP 用途
exit-ct100 CT100 (ct100.netbird.selfhosted) HiNet 125.229.110.50 (TW) 家用、回家連 LAN 順便
exit-virmach-lax virmach-lax (virmach-lax.netbird.selfhosted) VirMach 141.11.93.252 (US LAX) 美國 IP、bypass 地理限制

兩條設定一致(只差 peer

  • CIDR: 0.0.0.0/0
  • Distribution Groups: personal — 只推給 Mac/iPhone。不推給 servers,避免 CT101 拿到 0.0.0.0/0 繞回自己或另一個 server
  • Access Control Groups: personal, servers
  • Masquerade: on
  • Metric: 9999與 home-lan 一致,僅限兜底)

建立方式API

跟 home-lan 流程一樣(需 routing peer 先開 IP forwarding只是 network0.0.0.0/0network_id 改成識別用名稱:

TOKEN=<你的 PAT>
CT100_PEER_ID="<從 GET /api/peers 查>"
VIRMACH_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\": \"Exit node via CT100 (home, TW IP)\",
    \"network_id\": \"exit-ct100\",
    \"network\": \"0.0.0.0/0\",
    \"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

# 同樣的 payload 把 network_id 換成 exit-virmach-lax、peer 換成 VIRMACH_PEER_ID 再打一次

Client 端exit node 是 opt-in

關鍵差異subnet route如 home-lan收到就自動進路由表0.0.0.0/0 不一樣 — client 收到後不會自動啟用,使用者要明確 select 才會把預設路由切過去:

macOS / Linux CLI

netbird routes list
# 看到兩條 exit-* 初始都是 "Not selected"

netbird routes select exit-virmach-lax       # 切到美國出口
netbird routes select exit-ct100             # 或切回家裡出口
netbird routes deselect exit-virmach-lax     # 取消,回成直連上網

macOS App / iPhone Apptray / App → Exit Node → 勾選要用的那條,或按 Deselect All 不走 exit node。

⚠️ UI 是 checkbox 不是 radio button,可以同時勾兩條 — 但兩條都是 0.0.0.0/0 metric 9999kernel 只會用其中一條當 default route依插入順序不會負載均衡。實際出口用哪個是 undefined建議只勾一條Deselect All

驗證出口 IP

# 選 exit-virmach-lax 後
curl -4 ifconfig.me
# → 141.11.93.252

# 選 exit-ct100 後
curl -4 ifconfig.me
# → 125.229.110.50

# deselect 或 None
curl -4 ifconfig.me
# → 本地網路的出口 IP咖啡店的、機場的等

看不到 exit route 的排查

Client 已連線但 netbird routes list 沒有 exit-*

  1. Daemon 啟動早於 route 建立 → 重啟 daemonpeer-deployment-ops.md 的 launchctl / systemctl 段)
  2. 該 peer 不在 route 的 groupsdistribution裡 — 目前只 personal 拿得到,servers 看不到
  3. Policy 擋住 — access_control_groups 必須包含 client 所在 group

額外風險(相對於 subnet router

  • Exit peer 看得到所有流量的元資料HTTPS 內容仍 E2E 加密,但 DNS 查詢、SNI、流量時序全部對 routing peer 可見
  • virmach-lax 是第三方 VPSVirMach 營運者理論上可在 host 上 tcpdump wt0。比 CT100自家硬體多一層信任假設。敏感操作銀行、公司 VPN仍建議走 Noneexit-ct100
  • 不要讓 servers group 拿到 0.0.0.0/0:伺服器自己繞 exit node 出網會產生 loop 或不必要延遲

安全考量

Routing Peer 被攻破 = 攻擊者能透過 overlay 打進家裡所有 LAN 設備。

降低風險:

  • 帶路人只開最小必要服務(本部署 CT100 就是純 jump box表面積小
  • 透過 access_control_groups 限制哪些 group 能走這條 route
  • 考慮把 route 拆細:例如 192.168.42.10/32(只 OpenWrt192.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 的考量)