diff --git a/README.md b/README.md index 86fe101..f21fa74 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ | [QUICKSTART.md](./QUICKSTART.md) | 快速部署步驟(適合複製貼上執行) | | [netbird-selfhosted-setup.md](./netbird-selfhosted-setup.md) | 完整安裝指南(含原理說明) | | [npm-grpc-fix.md](./npm-grpc-fix.md) | 透過 Nginx Proxy Manager 對外公開時的 gRPC 修復 | +| [setup-keys-and-pat.md](./setup-keys-and-pat.md) | Setup Keys 概念、PAT 用法、以及 API 無法改名的繞道做法 | | [SUMMARY.md](./SUMMARY.md) | 部署摘要與疑難排解記錄 | ## 快速開始 diff --git a/SUMMARY.md b/SUMMARY.md index 8756971..4fd6730 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -81,6 +81,7 @@ reopen fd 8: permission denied | apt 的 crun 1.14.1 報 `unknown version specified` | crun 的 OCI spec 1.0.0 與 Docker 29.4 不相容 | 從 GitHub 下載 crun 1.20 | | 502 Bad Gateway(所有 API 端點) | netbird-server 下載 GeoLite2 中,尚未開始監聽 port 80 | 等待 2-5 分鐘 | | 手機 App:`server closed the stream without sending trailers` | 透過 NPM 對外公開時,NPM 用 `proxy_pass` 而非 `grpc_pass` 轉 gRPC;加上 Caddy `:80` 預設不支援 h2c | NPM 加 `advanced_config`(gRPC 路由)+ Caddy 全域啟用 `protocols h1 h2 h2c`;見 [npm-grpc-fix.md](./npm-grpc-fix.md) | +| `PUT /api/setup-keys/{id}` 回 200 但 `name` 沒改 | NetBird API 只更新 `auto_groups` 和 `revoked`,`name` 是 read-only | 改 `store.db` 的 `setup_keys.name` → 重啟 `netbird-server`;見 [setup-keys-and-pat.md](./setup-keys-and-pat.md) | ## 檔案位置 @@ -91,7 +92,8 @@ reopen fd 8: permission denied ├── QUICKSTART.md ← 快速部署步驟 ├── SUMMARY.md ← 本檔(部署摘要) ├── netbird-selfhosted-setup.md ← 完整安裝指南 -└── npm-grpc-fix.md ← 經 NPM 對外公開時的 gRPC 修復 +├── npm-grpc-fix.md ← 經 NPM 對外公開時的 gRPC 修復 +└── setup-keys-and-pat.md ← Setup Keys / PAT 操作與 API 限制繞道 ``` ### 遠端伺服器(192.168.42.127:/opt/netbird/) diff --git a/setup-keys-and-pat.md b/setup-keys-and-pat.md new file mode 100644 index 0000000..61525c2 --- /dev/null +++ b/setup-keys-and-pat.md @@ -0,0 +1,138 @@ +# Setup Keys 與 Personal Access Token (PAT) + +本文整理 NetBird Setup Keys 的概念、PAT 的使用、以及一個 API 實作限制的繞道方法。 + +## Setup Keys:用途 + +Setup Key 是 NetBird 的 **預先認證 token**,讓裝置可以**免互動登入**直接加入網路。 + +### 兩種註冊方式 + +| 方式 | 指令/流程 | 適用 | +|------|-----------|-------| +| SSO 登入(互動式) | App 開瀏覽器 → OAuth2 → 授權 | 手機、筆電等個人裝置 | +| Setup Key(非互動) | `netbird up --setup-key ` | 伺服器、容器、CI、IoT | + +### Key 的屬性 + +- **Reusable / One-off**:一次性或可重複使用 +- **Expires**:有效期限(可設定永不過期) +- **Peer Expiration**:加入的 peer 幾天後要重新認證 +- **Auto-assigned Groups**:自動加入指定群組 +- **Ephemeral**:peer 離線後自動清除(適合 CI / 短命容器) +- **Usage Limit**:被使用次數上限 + +### 為什麼不建議給手機用 + +NetBird UI 會顯示警告: + +> *Using setup keys for user devices is not recommended. SSO with MFA provides stronger security, proper user-device association, and periodic re-authentication.* + +- 沒有使用者歸屬,Activity Log 難追溯 +- 缺少 MFA +- 沒有週期性重新認證 +- token 形式是一串 UUID,外洩後任何人都能加入 + +**推薦做法**:個人裝置(手機、筆電)走 SSO;機器/自動化用 Setup Key。 + +## Personal Access Token (PAT):API 存取 + +要用 NetBird API 操作資源(list、revoke、建立 setup key 等),需要一把 PAT。 + +### 如何建立 PAT + +1. Dashboard → **Team → Users → 你自己 → Personal Access Tokens → Create Token** +2. 填: + - **Name**:易辨識的名稱(例:`Claude-API`、`admin-cli`、`ansible`) + - **Expires in**:`1` – `365` 天(**強制有期限**,無法永不過期) +3. 按 **Create Token** → Token 只會顯示**一次**,務必立刻複製保存 + +### PAT 為什麼強制有效期限 + +這是安全設計:就算 token 外洩,風險也有上限。建議做法: +- 用密碼管理器(1Password、Bitwarden 等)存放 +- 命名包含用途(方便日後識別哪把是哪把) +- 到期前 Dashboard 會有提醒 +- 長期用途(例如 IaC 腳本),排進年度維運任務重建 + +### PAT API 用法 + +```bash +TOKEN=nbp_xxxxxxxx + +# 列出所有 setup keys +curl -sf -H "Authorization: Token $TOKEN" \ + https://netbird.timmy.us.kg/api/setup-keys | jq + +# 查看單一 setup key +curl -sf -H "Authorization: Token $TOKEN" \ + https://netbird.timmy.us.kg/api/setup-keys/ | jq + +# Revoke 一把 setup key +curl -X PUT -H "Authorization: Token $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"...", "auto_groups":[], "revoked":true}' \ + https://netbird.timmy.us.kg/api/setup-keys/ +``` + +## 陷阱:API 無法更新 Setup Key 的名稱 + +### 症狀 + +對 `PUT /api/setup-keys/` 送出含 `"name":"新名字"` 的 payload,API 回 **HTTP 200** 且 `updated_at` 有更新,但 `name` 實際**沒變**。 + +### 原因 + +NetBird 的 setup-key update handler **只處理 `auto_groups` 和 `revoked`** — `name`、`type`、`expires` 這些欄位在 API 層被視為建立時固定,payload 裡的值會被忽略。 + +### 繞道做法:直接改 SQLite + +NetBird 伺服器用 SQLite (`/var/lib/netbird/store.db`) 存狀態。繞過 API 直接 UPDATE 資料表,再重啟 `netbird-server` 讓快取重載。 + +```bash +# 步驟 1:把 DB 從容器拷貝出來(容器內沒裝 sqlite3 CLI) +docker cp netbird-server:/var/lib/netbird/store.db /tmp/store.db + +# 步驟 2:改名 +sqlite3 /tmp/store.db \ + "UPDATE setup_keys SET name='iPhone-15-Pro' WHERE id='d7h3p5mfs58s73f6qdr0'" + +# 確認 +sqlite3 /tmp/store.db 'SELECT id, name FROM setup_keys' + +# 步驟 3:覆蓋回容器 + 重啟 server(讓記憶體快取失效) +docker cp /tmp/store.db netbird-server:/var/lib/netbird/store.db +cd /opt/netbird +docker compose restart netbird-server + +# 步驟 4:用 API 驗證 +curl -sf -H "Authorization: Token $TOKEN" \ + https://netbird.timmy.us.kg/api/setup-keys | jq '.[] | {id, name}' +``` + +重啟 `netbird-server` 會短暫中斷 gRPC 連線,**執行中的 peer 會自動重連**,約 5-10 秒內恢復。 + +### 替代做法 + +如果不想動 DB:**Revoke 舊 key,建立新 key 並命名正確**。缺點是已用那把 key 註冊的 peer 不受影響(peer 註冊成功後 key 已解耦),但任何依賴那把 key 的自動化腳本要更新。 + +## 其他可直接改 SQLite 的場景 + +NetBird 的 API 對某些欄位是 read-only,但 DB 是可以改的(風險自負): + +| 欄位 | API 可改? | DB 可改 | +|------|----------|--------| +| setup_keys.name | 否 | 是 | +| setup_keys.type | 否 | 是(但多半不該改) | +| setup_keys.expires | 否(建立時才能設) | 是 | +| peers.name | 是(`PUT /api/peers/{id}`) | 是 | +| users.role | 是 | 是 | + +**建議順序**:先試 API → 不行再看 DB schema → 確認欄位意義後再改 → 改完**必須重啟 netbird-server**。 + +## 安全提醒 + +- Setup Key 跟 PAT 都等同於長效憑證,存放要當密碼看待 +- PAT 失竊 → 立刻從 Dashboard revoke +- Setup Key 失竊 → 立刻 revoke,已註冊的 peer 不受影響但無法再用這把 key 註冊新 peer +- 不要把任何一種 token commit 進 git repo