Applied the segmentation plan. Added an 'execution log' section with real IDs, verification results, and a rollback snippet. Key lesson corrected in the main plan: the original design missed personal<->personal connectivity (Mac<->iPhone would have broken when Default was disabled). Added personal-internal policy and a note that every desired group pair — including a group with itself — needs an explicit policy, because NetBird defaults to deny when there is no matching policy.
12 KiB
Groups 與 Access Control Policies
NetBird 的存取控管基本單位。本文用目前部署的 4 個 peer(mbp-13-pro、iphone-15-pro、CT100、CT101)為例說明 Groups 怎麼用。
Groups 是什麼
Group 是 peer 的具名集合。NetBird 不直接管「peer A 能不能連 peer B」,而是:
- 把 peer 丟進 group
- 用 Policy 定義「group X → group Y 的流量允許/禁止」
這讓存取控管的規模隨服務/角色擴展,而不是隨 peer 數量爆炸。
Groups 在 NetBird 裡出現的 4 個地方
| 地方 | 用途 |
|---|---|
| Access Control → Policies | 決定 group A 能不能連 group B、哪些 port/protocol |
Setup Keys 的 auto_groups |
用此 key 註冊的 peer 自動進指定 group |
| Network Routes | 哪個 group 能透過某個 peer 走一條路由(例:透過 CT100 當 exit node) |
| DNS / Nameservers | 哪個 group 套用特定 DNS 設定 |
這四個功能都以 group 當接口,換句話說 — group 設計對了,整個存取模型就清楚了。
預設:All group 與 Default policy
NetBird 初始化時自動建立:
Allgroup — 所有 peer 自動加入Defaultpolicy —All → All,allow all protocols
所以剛部署完,所有 peer 彼此互通(就像你目前的狀態)。這對起步階段方便,但不符合最小權限原則。
Dashboard 的 Peers 頁面顯示 Add Groups 按鈕代表該 peer 沒有自訂 group(它仍然自動在 All 裡)。
何時要自訂 Group
典型情境
- 角色分離:個人裝置 vs 伺服器,不希望伺服器能主動戳個人裝置
- 最小權限:iPhone 只需能連特定服務,不需連所有 peer
- 自動化註冊:用 setup key 部署新機時自動分類(
ci-runners、edge-nodes等) - 分享給家人:一個 group 只給家人裝置,policy 限制他們能看到的範圍
- Exit Node 授權:只有特定 group 能把流量導去某 peer
本部署的分組(已套用,詳見文末執行紀錄)
4 個 peer 分成 2 個自訂 group:
| Group | 成員 | 用途 |
|---|---|---|
servers |
CT100、CT101 | 家裡 headless 服務 |
personal |
mbp-13-pro、iphone-15-pro | Timmy 的個人裝置 |
對應的 Policy:
| # | From | To | Bidirectional | 目的 |
|---|---|---|---|---|
| 1 | personal |
servers |
否 | 個人裝置能連伺服器 |
| 2 | servers |
servers |
是 | 伺服器彼此互連(例如未來備份、日誌集中) |
| 3 | personal |
personal |
是 | Mac ↔ iPhone 互通(不能漏,見下方教訓) |
| 4 | All → All (Default) |
— | — | 停用(保留可快速 rollback) |
⚠️ 教訓:personal-internal policy 是必需的。原本只設 personal → servers 跟 servers → servers 會切斷 Mac ↔ iPhone — 每個想互通的 group 組合都要一條明確 policy,包括 group 自己到自己。
刻意不建立 servers → personal 的 policy:伺服器被入侵時,攻擊者無法透過 NetBird 橫向移動到個人裝置。
(未來如果有多個使用者,可再拆 timmy-devices、family-devices 等。)
Peer 可以同時屬於多個 group
常見用法:
CT100: in bothserversandjump-boxes(對不同 policy 都有效)CT101: in bothserversandrustdesk(另一條政策只開 rustdesk port 給某個子集)
Peer group 是集合運算,policy 匹配時用 OR:peer 只要在 policy 的 source group 任一個裡就算命中。
怎麼建 Group
方式 A:UI(推薦起步)
- Dashboard → Access Control → Groups → Create Group
- 填 Name(例
servers) - 在 Peers 勾選要加入的 peer
- Create
建完後去 Access Control → Policies 或 Peers 頁面都能再綁定 group。
方式 B:API
TOKEN=<你的 PAT>
# 先查目前 peer 清單拿 ID
curl -sf -H "Authorization: Token $TOKEN" \
https://netbird.timmy.us.kg/api/peers | jq '.[] | {id, name}'
# 建 servers group,同時塞兩個 peer
curl -sf -X POST -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "servers",
"peers": ["<CT100-peer-id>", "<CT101-peer-id>"]
}' \
https://netbird.timmy.us.kg/api/groups | jq
# 建 personal group
curl -sf -X POST -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "personal",
"peers": ["<Mac-peer-id>", "<iPhone-peer-id>"]
}' \
https://netbird.timmy.us.kg/api/groups | jq
怎麼建 Policy
方式 A:UI
- Access Control → Policies → Add Policy
- Name:例
personal-to-servers - Source Groups:選
personal - Destination Groups:選
servers - Protocol:
All(或 TCP/UDP/ICMP) - Ports:留空代表全部
- Bidirectional:看需求
- 勾了 → 來回都允許(大多數家用場景這樣方便)
- 不勾 → 只允許 source 主動連 destination
- Create
方式 B:API
curl -sf -X POST -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "personal-to-servers",
"enabled": true,
"description": "Personal devices can access servers",
"rules": [{
"name": "personal-to-servers",
"enabled": true,
"action": "accept",
"protocol": "all",
"sources": ["<personal-group-id>"],
"destinations": ["<servers-group-id>"],
"bidirectional": false
}]
}' \
https://netbird.timmy.us.kg/api/policies | jq
刪除預設 Default policy
當你建立好新的 personal-to-servers、servers-to-servers 等 policy 後,才關掉預設的 All → All,不然會暫時中斷連線。
UI:Access Control → Policies → Default → 右邊三點 → Delete 或 toggle Disabled。
API:
# 先找 policy ID
curl -sf -H "Authorization: Token $TOKEN" \
https://netbird.timmy.us.kg/api/policies | jq '.[] | {id, name, enabled}'
# 停用(較安全,出問題可立刻打開)
curl -sf -X PUT -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '<GET 出來的內容,改 "enabled": false>' \
https://netbird.timmy.us.kg/api/policies/<POLICY_ID>
# 或直接刪除
curl -X DELETE -H "Authorization: Token $TOKEN" \
https://netbird.timmy.us.kg/api/policies/<POLICY_ID>
Setup Key 搭配 auto_groups
部署新 peer 時可以一步到位自動加 group:
# 建一把 key,註冊進來的 peer 自動進 servers group
curl -sf -X POST -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "CT102-whatever",
"type": "one-off",
"expires_in": 86400,
"usage_limit": 1,
"ephemeral": false,
"auto_groups": ["<servers-group-id>"]
}' \
https://netbird.timmy.us.kg/api/setup-keys | jq
如此 CT100/CT101 這類部署可以省掉「註冊完再手動加 group」的步驟。
(之前 CT100/CT101 的 setup key 我建的時候 auto_groups 是空的 — 未來部署時記得帶上)
Policy 細節補充
Port / Protocol 限制
# 只允許 SSH (TCP/22) 從 personal 連到 servers
rules:
- protocol: tcp
ports: ["22"]
sources: [<personal-group-id>]
destinations: [<servers-group-id>]
Bidirectional 的真正含義
bidirectional: true— A → B 與 B → A 都允許bidirectional: false— 只允許 source 端主動 initiate,但回應封包 NetBird 會自動放行(stateful,不用特別設)
家用場景建議預設 false,只在真的需要雙向 initiate 時才開。
Posture Checks(進階)
Policy 可綁 Posture Check — 要求 peer 滿足某些條件(OS 版本、地理位置、已登入 SSO 等)才能走這條 policy。進階情境可參考 NetBird docs。
驗證 Policy 生效
方式 A:從 peer 端測試
# 從 Mac ping iPhone (現在可以) → 建立 personal-only policy 後,應該還是可以
ping 100.71.63.111
# 從 CT100 ping Mac (現在可以) → 刪掉 All→All 後,應該不通
ssh root@192.168.42.100 'docker exec netbird netbird status --detail | grep mbp-13-pro -A 3'
方式 B:Dashboard → Control Center
視覺化顯示每個 peer 的 policy 連線關係,可以一眼看出誰能連誰。
整套搬遷步驟建議
漸進式,不中斷當前連線:
Phase 1: 建 groups
- Create group "servers" with CT100, CT101
- Create group "personal" with Mac, iPhone
Phase 2: 建新 policies(先不刪預設)
- Policy "personal-to-servers": personal → servers (unidirectional)
- Policy "servers-internal": servers → servers (bidirectional)
- Policy "personal-internal": personal → personal (bidirectional)
- 此時 All→All 跟新 policy 並存,功能不變
Phase 3: 驗證新 policy 生效
- 關掉 All→All(先 disable 不 delete)
- 實測:Mac→CT100 通、CT100→Mac **不通**、Mac↔iPhone 通
Phase 4: 確認無誤
- 刪除 Default policy(如果想)
- 日後新 peer 記得 setup key 的 auto_groups 要填對
出問題 rollback:把 Default policy 重新啟用即可恢復原狀。
執行紀錄:2026-04-18 套用到本部署
實際在本部署上執行此遷移的結果,供日後回溯。
Groups
| Name | ID | Peers |
|---|---|---|
All |
d7h3kf6fs58s73f6qbj0 |
4(系統預設) |
servers |
d7hjr5fvjtnc738j3reg |
CT100, CT101 |
personal |
d7hjr5fvjtnc738j3rfg |
mbp-13-pro, iphone-15-pro |
Policies
| Name | ID | Source → Destination | Bidirectional | Enabled |
|---|---|---|---|---|
personal-to-servers |
d7hjr8fvjtnc738j3rh0 |
personal → servers | false | ✓ |
servers-internal |
d7hjr8fvjtnc738j3ri0 |
servers → servers | true | ✓ |
personal-internal |
d7hjrenvjtnc738j3rj0 |
personal → personal | true | ✓ |
Default |
d7h3kf6fs58s73f6qbjg |
All → All | true | false(停用,保留) |
計畫外的發現:personal-internal 是必需的
最初設計只列了兩條新 policy(personal → servers、servers → servers),停掉 Default 之後會切斷 Mac ↔ iPhone。補上 personal-internal 才維持個人裝置互通。
規則:每個想互通的 group 組合都需要明確 policy,沒有則拒絕。包括 group 自己到自己。
驗證結果
| 連線 | Policy | 結果 |
|---|---|---|
| Mac → CT100 | personal-to-servers | 通 (32ms) |
| Mac → iPhone | personal-internal | 通 (8ms) |
| CT100 → CT101 | servers-internal | 通 (<1ms, LAN 直連) |
| CT100 → Mac | ✗(無 policy) | 100% loss |
| CT101 → iPhone | ✗(無 policy) | 100% loss |
產生的安全邊界:伺服器被攻破時無法透過 NetBird 橫向連到個人裝置。
Rollback 方式
如果需要暫時恢復全互通:
TOKEN=<your PAT>
DEFAULT_PID="d7h3kf6fs58s73f6qbjg"
ALL_GID="d7h3kf6fs58s73f6qbj0"
curl -X PUT -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d "{
\"name\":\"Default\",
\"description\":\"This is a default rule that allows connections between all the resources\",
\"enabled\":true,
\"rules\":[{
\"name\":\"Default\",
\"enabled\":true,
\"action\":\"accept\",
\"protocol\":\"all\",
\"bidirectional\":true,
\"sources\":[\"$ALL_GID\"],
\"destinations\":[\"$ALL_GID\"]
}]
}" \
https://netbird.timmy.us.kg/api/policies/$DEFAULT_PID
相關檔案與文件
setup-keys-and-pat.md— PAT 與 setup key 建立(建 group 需要 PAT)client-troubleshooting.md— 如果改 policy 後 peer 突然連不到,先查這裡peer-deployment-ops.md— 新部署 peer 時的 setup key + auto_groups 用法