# Groups 與 Access Control Policies NetBird 的存取控管基本單位。本文用目前部署的 4 個 peer(mbp-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 → All`,allow all protocols 所以剛部署完,所有 peer 彼此互通(就像你目前的狀態)。這對起步階段方便,但**不符合最小權限原則**。 Dashboard 的 Peers 頁面顯示 `Add Groups` 按鈕代表該 peer **沒有自訂 group**(它仍然自動在 `All` 裡)。 ## 何時要自訂 Group ### 典型情境 1. **角色分離**:個人裝置 vs 伺服器,不希望伺服器能主動戳個人裝置 2. **最小權限**:iPhone 只需能連特定服務,不需連所有 peer 3. **自動化註冊**:用 setup key 部署新機時自動分類(`ci-runners`、`edge-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 → servers` 跟 `servers → servers` 會切斷 Mac ↔ iPhone — 每個想互通的 group 組合都要一條明確 policy,包括 group 自己到自己。 刻意**不**建立 `servers → personal` 的 policy:伺服器被入侵時,攻擊者無法透過 NetBird 橫向移動到個人裝置。 (未來如果有多個使用者,可再拆 `timmy-devices`、`family-devices` 等。) ## Peer 可以同時屬於多個 group 常見用法: - `CT100`: in both `servers` and `jump-boxes`(對不同 policy 都有效) - `CT101`: in both `servers` and `rustdesk`(另一條政策只開 rustdesk port 給某個子集) Peer group 是集合運算,policy 匹配時用 OR:peer 只要在 policy 的 source group 任一個裡就算命中。 ## 怎麼建 Group ### 方式 A:UI(推薦起步) 1. Dashboard → **Access Control → Groups** → **Create Group** 2. 填 **Name**(例 `servers`) 3. 在 **Peers** 勾選要加入的 peer 4. **Create** 建完後去 **Access Control → Policies** 或 **Peers** 頁面都能再綁定 group。 ### 方式 B:API ```bash 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": ["", ""] }' \ 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": ["", ""] }' \ https://netbird.timmy.us.kg/api/groups | jq ``` ## 怎麼建 Policy ### 方式 A:UI 1. **Access Control → Policies** → **Add Policy** 2. **Name**:例 `personal-to-servers` 3. **Source Groups**:選 `personal` 4. **Destination Groups**:選 `servers` 5. **Protocol**:`All`(或 TCP/UDP/ICMP) 6. **Ports**:留空代表全部 7. **Bidirectional**:看需求 - 勾了 → 來回都允許(大多數家用場景這樣方便) - 不勾 → 只允許 source 主動連 destination 8. **Create** ### 方式 B:API ```bash 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": [""], "destinations": [""], "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: ```bash # 先找 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 '' \ https://netbird.timmy.us.kg/api/policies/ # 或直接刪除 curl -X DELETE -H "Authorization: Token $TOKEN" \ https://netbird.timmy.us.kg/api/policies/ ``` ## Setup Key 搭配 `auto_groups` 部署新 peer 時可以**一步到位自動加 group**: ```bash # 建一把 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": [""] }' \ https://netbird.timmy.us.kg/api/setup-keys | jq ``` 如此 CT100/CT101 這類部署可以省掉「註冊完再手動加 group」的步驟。 (之前 CT100/CT101 的 setup key 我建的時候 `auto_groups` 是空的 — 未來部署時記得帶上) ## Policy 細節補充 ### Port / Protocol 限制 ```yaml # 只允許 SSH (TCP/22) 從 personal 連到 servers rules: - protocol: tcp ports: ["22"] sources: [] destinations: [] ``` ### 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 端測試 ```bash # 從 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 方式 如果需要暫時恢復全互通: ```bash TOKEN= 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 用法