Document the operational patterns established while adding CT100 and CT101 as peers via Docker, plus incidental fixes: - Docker compose template for headless peer containers - One-off setup key naming convention and API creation - Peer rename via API PUT (works, unlike setup keys) - Mac daemon kickstart via 'sudo launchctl kickstart -k system/netbird' (service name is 'netbird', not 'io.netbird.client') - UPNP auto-port-mapping discovery in NetBird client - CT100 PermitRootLogin drop-in override (first-match-wins sshd quirk)
7.7 KiB
Peer 部署與維運操作
記錄新增 peer、改名、daemon 維運等實際操作的 know-how。
在 LXC/VM 裡透過 Docker 部署 NetBird 客戶端
適合 headless 伺服器(沒有 GUI、沒有真人 SSO 登入流程)。本 NetBird 部署已在 CT100、CT101 用此方法上線。
前置
- 目標主機已裝 Docker
- 有可用的 PAT(見
setup-keys-and-pat.md) - 對目標 NetBird management server 可連線
步驟
1. 為這台機器建一把 one-off setup key
TOKEN=<你的 PAT>
curl -sf -X POST -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "CT100-SSH-JumpBox",
"type": "one-off",
"expires_in": 86400,
"revoked": false,
"auto_groups": [],
"usage_limit": 1,
"ephemeral": false
}' \
https://netbird.timmy.us.kg/api/setup-keys | jq
命名慣例:<主機名>-<用途>(例 CT100-SSH-JumpBox、CT101-RustDesk),Dashboard 上一眼看得出這把 key 給誰。
type: one-off+usage_limit: 1— 用一次就失效,最小攻擊面expires_in: 86400— 24 小時內沒用就過期ephemeral: false— 此 peer 離線後不自動清除(伺服器要持續存在)
把回傳的 key 欄位(UUID)存起來,下一步要用。
2. 在目標主機寫 docker-compose.yml
services:
netbird:
image: netbirdio/netbird:latest
container_name: netbird
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- SYS_RESOURCE
environment:
- NB_SETUP_KEY=<上一步拿到的 key UUID>
- NB_MANAGEMENT_URL=https://netbird.timmy.us.kg:443
- NB_HOSTNAME=<主機名,例 CT100>
volumes:
- /opt/netbird/data:/var/lib/netbird
labels:
- com.centurylinklabs.watchtower.enable=true
放在 /opt/netbird/docker-compose.yml(跟 NetBird server 部署在 .127 的路徑一致,維運直覺)。
關鍵設定:
network_mode: host— NetBird 需要能直接看到 host 的網路介面來做 ICEcap_add: [NET_ADMIN, SYS_RESOURCE]— 建 WireGuard 介面必要NB_HOSTNAME— peer 註冊後的顯示名稱(日後可用 API 再改)- Watchtower label — 讓 Watchtower 自動跟 upstream 更新
3. 啟動
cd /opt/netbird
docker compose pull
docker compose up -d
# 驗證
sleep 10
docker exec netbird netbird status
正常輸出應該有 Management: Connected、Relays: 2/2 Available、Peers count: N/N Connected。
LXC 容器內的 TUN 問題
理論上:NetBird 需要 /dev/net/tun 來建立 WireGuard kernel 介面。
實際上:在非特權 LXC 容器(如 Proxmox 的 unprivileged: 1)/dev/net/tun 不存在,但 NetBird client 容器透過 network_mode: host + cap_add: NET_ADMIN 能跑起來,並顯示 Interface type: Kernel(推測是透過 host namespace 繞過)。
如果未來遇到 TUN 不可用的情境:
- 選項 A:在 PVE CT config 加
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file,optional 0 0(需重啟 CT) - 選項 B:切 userspace WireGuard(參考 Tailscale 的
--tun=userspace-networking作法,但 NetBird 沒有這個開關,需要手動 patch)
Peer 改名(API 做得到,setup key 做不到)
對比:
| 資源 | API PUT name |
其他方式 |
|---|---|---|
| setup_keys | 不生效(見 setup-keys-and-pat.md) |
改 store.db + 重啟 server |
| peers | 生效,連 FQDN / dns_label 都自動同步 | — |
用法
TOKEN=<你的 PAT>
# 列出 peer IDs
curl -sf -H "Authorization: Token $TOKEN" \
https://netbird.timmy.us.kg/api/peers | jq '.[] | {id, name, hostname, ip}'
# 改名
curl -X PUT -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "mbp-13-pro",
"ssh_enabled": false,
"login_expiration_enabled": false,
"inactivity_expiration_enabled": false,
"approval_required": false
}' \
https://netbird.timmy.us.kg/api/peers/<PEER_ID>
注意:PUT 需要帶完整的 peer 設定(不只 name),ssh_enabled 等欄位若不帶會用預設值覆蓋。先 GET 看看這個 peer 現況,再帶相同值 + 新的 name。
命名慣例
改名後的 dns_label 會是 <name>.netbird.selfhosted,建議:
- 全小寫、英數與連字號、不用空格/中文
- 語意清楚:
mbp-13-pro、iphone-15-pro、ct100、ct101 - 避免
.local等 mDNS 後綴(macOS 預設 hostname 會自動帶,改掉)
Mac daemon 卡住:launchctl 重啟
症狀
netbird status 顯示 Management: Disconnected, reason: rpc error: context canceled 或類似 RPC 錯誤。Signal 可能還是 Connected,但 peers count 歸零。
UI 上 Disconnect → Connect 通常可以恢復,但 daemon 真的卡死時 UI 也不回應。
解法
直接重啟 launchd service:
sudo launchctl kickstart -k system/netbird
⚠️ 服務名稱是 netbird,不是 io.netbird.client、com.netbird.daemon 等常見猜測。
確認方式:
ls /Library/LaunchDaemons/ | grep -i netbird
# → netbird.plist
cat /Library/LaunchDaemons/netbird.plist | grep -A 1 '<key>Label</key>'
# → <string>netbird</string>
launchd 語法 system/<Label> 所以完整 domain 是 system/netbird。
重啟後驗證
netbird status
# Management: Connected
# Peers count: N/N Connected ← 應該是 N/N 而非 0/0
其他平台
Linux systemd:
sudo systemctl restart netbird
Windows:
Restart-Service -Name "Netbird"
Docker 容器(本文部署的方式):
docker restart netbird
意外發現:NetBird 內建 UPNP 自動打洞
觀察 CT101 的啟動日誌:
client/internal/portforward/manager.go:162: discovered NAT gateway: UPNP (IG2-IP2)
client/internal/portforward/manager.go:204: created port mapping: 51820 -> 10935 via UPNP (IG2-IP2) (external IP: 125.229.110.50)
NetBird client 啟動時會主動向 NAT gateway 發 UPNP/IGD 請求,自動 map 一個 public port 到自己的 51820。
好處:
- 不需要手動在路由器上加 DNAT 也能讓 peer 做 P2P inbound
- 等於 NetBird 自己處理 port forwarding
前提:
- 路由器支援 UPNP / NAT-PMP 且開啟
- OpenWrt 通常需裝
miniupnpd套件並在 firewall zone 允許
本部署的 OpenWrt 有啟用 UPNP,所以 CT101 才能自動打這個洞。如果未來發現某台 peer 一直走 Relay、查 log 沒看到 UPNP 訊息,先檢查閘道器有沒有開 UPNP。
CT100 部署時意外踩的雷
CT100 原本 root SSH 密碼登入完全被擋(Permission denied),不是 root 密碼錯,而是 sshd config 有個較早的 drop-in 設了 PermitRootLogin prohibit-password。
診斷
pct exec 100 -- sshd -T | grep -iE 'permitrootlogin|passwordauthentication'
# permitrootlogin without-password ← 擋 root 密碼登入
# passwordauthentication yes
grep -l -r PermitRootLogin /etc/ssh/ 只找到 /etc/ssh/sshd_config,但該檔內有兩個 PermitRootLogin 指令(先 prohibit-password 後 yes),OpenSSH 是 first-match-wins,所以前者有效。
修復
覆蓋寫一個更晚載入的 drop-in:
pct exec 100 -- bash -c 'echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/99-allow-root.conf'
# 驗證
pct exec 100 -- sshd -T | grep permitrootlogin
# permitrootlogin yes
不需重啟 sshd:CT100 用 ssh.socket 做 systemd socket activation,每個新連線都 fork 新 sshd 重讀設定。
密碼也要重設
CT100 原本的 root 密碼忘了,從 PVE host 繞過:
ssh root@192.168.42.39
pct exec 100 -- bash -c "echo 'root:25915525' | chpasswd"