Files
netbird-selfhosted/groups-and-policies.md
timmy e22ab219a5 docs: record actual groups/policies rollout + lesson learned
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.
2026-04-18 16:18:47 +08:00

12 KiB
Raw Permalink Blame History

Groups 與 Access Control Policies

NetBird 的存取控管基本單位。本文用目前部署的 4 個 peermbp-13-pro、iphone-15-pro、CT100、CT101為例說明 Groups 怎麼用。

Groups 是什麼

Group 是 peer 的具名集合。NetBird 不直接管「peer A 能不能連 peer B」而是

  1. 把 peer 丟進 group
  2. 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 初始化時自動建立:

  • All group — 所有 peer 自動加入
  • Default policy — All → Allallow all protocols

所以剛部署完,所有 peer 彼此互通(就像你目前的狀態)。這對起步階段方便,但不符合最小權限原則

Dashboard 的 Peers 頁面顯示 Add Groups 按鈕代表該 peer 沒有自訂 group(它仍然自動在 All 裡)。

何時要自訂 Group

典型情境

  1. 角色分離:個人裝置 vs 伺服器,不希望伺服器能主動戳個人裝置
  2. 最小權限iPhone 只需能連特定服務,不需連所有 peer
  3. 自動化註冊:用 setup key 部署新機時自動分類(ci-runnersedge-nodes 等)
  4. 分享給家人:一個 group 只給家人裝置policy 限制他們能看到的範圍
  5. 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 → serversservers → servers 會切斷 Mac ↔ iPhone — 每個想互通的 group 組合都要一條明確 policy包括 group 自己到自己。

刻意建立 servers → personal 的 policy伺服器被入侵時攻擊者無法透過 NetBird 橫向移動到個人裝置。

(未來如果有多個使用者,可再拆 timmy-devicesfamily-devices 等。)

Peer 可以同時屬於多個 group

常見用法:

  • CT100: in both servers and jump-boxes(對不同 policy 都有效)
  • CT101: in both servers and rustdesk(另一條政策只開 rustdesk port 給某個子集)

Peer group 是集合運算policy 匹配時用 ORpeer 只要在 policy 的 source group 任一個裡就算命中。

怎麼建 Group

方式 AUI推薦起步

  1. Dashboard → Access Control → GroupsCreate Group
  2. Name(例 servers
  3. Peers 勾選要加入的 peer
  4. Create

建完後去 Access Control → PoliciesPeers 頁面都能再綁定 group。

方式 BAPI

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

方式 AUI

  1. Access Control → PoliciesAdd Policy
  2. Name:例 personal-to-servers
  3. Source Groups:選 personal
  4. Destination Groups:選 servers
  5. ProtocolAll(或 TCP/UDP/ICMP
  6. Ports:留空代表全部
  7. Bidirectional:看需求
    • 勾了 → 來回都允許(大多數家用場景這樣方便)
    • 不勾 → 只允許 source 主動連 destination
  8. Create

方式 BAPI

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-serversservers-to-servers 等 policy 後,才關掉預設的 All → All,不然會暫時中斷連線。

UIAccess 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'

方式 BDashboard → 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 是必需的

最初設計只列了兩條新 policypersonal → serversservers → 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 用法