多個租戶共用同一 Agent Sandbox容器版 叢集時,每個租戶通過 Team 映射到獨立的 Kubernetes Namespace,並持有各自的 API Key 作為訪問憑證。管理員和租戶可通過 HTTP 介面建立、查詢和吊銷 API Key。
概述
ack-sandbox-manager 提供一組相容 E2B 協議的 HTTP 介面,用於管理 API Key 與 Team。叢集管理員與租戶可通過這些介面以編程方式建立 API Key、查詢所屬 Team,並吊銷不再使用的 Key。
這些介面沒有配套的 E2B SDK,也沒有對應的 Kubernetes CRD,必須直接通過 HTTP 協議調用。
Team
Team 是 API Key 的授權邊界。在 Agent Sandbox容器版 中,Team 的身份由 Team 名稱唯一標識,且 Team 名稱直接映射到一個 Kubernetes Namespace。
內建的
adminTeam 是叢集級的,由管理員管理,其持有的 admin API Key 在ack-sandbox-manager啟動時一同初始化。其他任何 Team 名稱都必須對應一個已經存在的 Kubernetes Namespace。當租戶為 Team
foo建立 API Key 時,Namespacefoo必須預先存在,否則請求會返回錯誤。
Namespace 的唯一性已經提供了足夠的隔離邊界,因此 Team的唯一識別碼(UUID)僅作為展示用中繼資料保留,不參與鑒權和資源尋找。
API Key
API Key 是某個 Team 長期持有的訪問憑證。用戶端在調用 ack-sandbox-manager 任意介面時,都必須通過 X-API-KEY 要求標頭攜帶它。API Key 決定了:
調用方屬於哪個 Team。
調用方是普通租戶還是叢集管理員(即是否屬於
adminTeam)。調用方可訪問的 Sandbox 範圍,普通租戶只能訪問由自身 API Key 建立的 Sandbox,管理員可以訪問全部 Sandbox。
授權模型
角色 | 列出當前Team 的 Key | 列出 Teams | 為當前Team 建立 Key | 為其他 Team 建立 Key | 刪除當前Team 的 Key | 刪除 admin API Key |
管理員(admin Team) | 支援 | 支援(全部 Team) | 支援 | 支援 | 支援 | 不支援(被拒絕) |
普通租戶 | 支援 | 支援(僅自身所在 Team) | 支援 | 不支援 | 支援 | 不支援 |
內建的 admin API Key 在任何情況下都不能被刪除,以保護叢集的可控性。
準備工作
前往目的地組群組件管理,確認已安裝
ack-sandbox-manager且已升級至 v0.6.0 及以上版本。並在組件配置頁面進行如下配置:勾選Whether to enable E2B_API_KEY authentication,啟用API Key鑒權功能。
配置adminApiKey(管理員API Key)。
端到端流程樣本
安裝JSON處理器jq。
以下指令碼完整示範了從管理員初始化資源環境與租戶憑證,到租戶自主實現團隊密鑰全生命週期管理的端到端商務程序。
#!/usr/bin/env bash set -euo pipefail ADMIN_KEY="${ADMIN_KEY:?required}" # 支援兩種協議,樣本使用原生E2B協議地址 https://api.your.domain.com # 如需使用Agent Sandbox私人協議,將BASE_URL替換為 https://your.domain.com/kruise/api。 BASE_URL="${BASE_URL:-https://api.your.domain.com}" TEAM_NAMESPACE="team-a" # 1. 管理員在叢集中建立 Team 對應的 Namespace kubectl create ns "${TEAM_NAMESPACE}" # 2. 管理員為 team-a 建立租戶 Key,記錄返回的明文 key TENANT_KEY=$(curl -fsS -X POST \ -H "X-API-KEY: ${ADMIN_KEY}" \ -H "Content-Type: application/json" \ -d "{\"name\":\"ci-runner\",\"teamName\":\"${TEAM_NAMESPACE}\"}" \ "${BASE_URL}/api-keys" | jq -r '.key') # 3. 租戶列出所屬 Team 與現有 Key curl -fsS -H "X-API-KEY: ${TENANT_KEY}" "${BASE_URL}/teams" curl -fsS -H "X-API-KEY: ${TENANT_KEY}" "${BASE_URL}/api-keys" # 4. 租戶為所屬 Team 建立子 Key(無需 teamName 欄位) SUB_KEY_ID=$(curl -fsS -X POST \ -H "X-API-KEY: ${TENANT_KEY}" \ -H "Content-Type: application/json" \ -d '{"name":"my-sub-key"}' \ "${BASE_URL}/api-keys" | jq -r '.id') # 5. 租戶刪除所屬 Team 的子 Key curl -fsS -X DELETE \ -H "X-API-KEY: ${TENANT_KEY}" \ "${BASE_URL}/api-keys/${SUB_KEY_ID}"
介面參考
Team列表
擷取目前使用者可見的 Team。普通租戶只能看到自身所屬的 Team;管理員可以看到所有 Team。
請求樣本:
curl -fsS \
-H "X-API-KEY: ${E2B_API_KEY}" \
"https://api.your.domain.com/teams"import os
import requests
resp = requests.get(
"https://api.your.domain.com/teams",
headers={"X-API-KEY": os.environ["E2B_API_KEY"]},
timeout=10,
)
try:
resp.raise_for_status()
except requests.HTTPError:
print(resp.text)
raise
print(resp.json())
預期輸出:
[
{
"teamID": "550e8400-e29b-41d4-a716-446655449999",
"name": "admin",
"apiKey": "",
"isDefault": true
}
]響應體為 JSON 數組,每個元素的欄位說明如下表。
欄位 | 類型 | 說明 |
| string | Team 的 UUID,僅用於展示。 |
| string | Team 名稱,與 Kubernetes Namespace 一致。 |
| string | 該欄位始終返回Null 字元串,僅為相容 E2B SDK Schema 保留。如需擷取 API Key 明文,請使用 |
| boolean | 是否為調用方的預設 Team。 |
API Key列表
擷取當前調用方所屬 Team 下的所有 API Key。
請求樣本:
curl -fsS \
-H "X-API-KEY: ${E2B_API_KEY}" \
"https://api.your.domain.com/api-keys"import os
import requests
resp = requests.get(
"https://api.your.domain.com/api-keys",
headers={"X-API-KEY": os.environ["E2B_API_KEY"]},
timeout=10,
)
try:
resp.raise_for_status()
except requests.HTTPError:
print(resp.text)
raise
for key in resp.json():
print(key["id"], key["name"], key["mask"])
響應欄位說明:
欄位 | 類型 | 說明 |
| string | API Key 的 UUID,刪除時使用。 |
| string | Key 的可讀名稱。 |
| string | Key 的建立時間。 |
| string | 建立該 Key 的調用方 UUID。 |
| string | Key 最近一次使用時間,從未使用時為 |
建立 API Key
建立新的 API Key。響應體中的 key 欄位為剛剛產生的明文 Key。明文 key 欄位僅在建立時返回一次,後續無法再通過任何介面取回。請立即持久化儲存。
運行樣本前,請確保 team-a 命名空間已經存在。如調用方為普通租戶,請刪除請求體中的 teamName 欄位。
請求樣本:
curl -fsS -X POST \
-H "X-API-KEY: ${E2B_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"name": "ci-runner", "teamName": "team-a"}' \
"https://api.your.domain.com/api-keys"
import os
import requests
resp = requests.post(
"https://api.your.domain.com/api-keys",
headers={
"X-API-KEY": os.environ["E2B_API_KEY"],
"Content-Type": "application/json",
},
json={"name": "ci-runner", "teamName": "team-a"},
timeout=10,
)
try:
resp.raise_for_status()
except requests.HTTPError:
print(resp.text)
raise
created = resp.json()
# 請立刻持久化 created["key"]-後續無法再次擷取。
print(created["id"], created["key"])請求體欄位:
欄位 | 類型 | 是否必填 | 說明 |
| string | 是 | Key 的可讀名稱。 |
| string | 否 | 目標 Team 名稱,預設為調用者自身所屬的 Team。僅管理員可指向其他 Team,且必須指向一個已經存在的 Kubernetes Namespace。普通租戶必須省略此欄位。 |
響應欄位:
欄位 | 類型 | 說明 |
| string | 新 Key 的 UUID,後續刪除時使用。 |
| string | 明文 Key,僅返回一次,必須立即儲存。 |
| string | 與請求體一致的 Key 名稱。 |
| string | Key 實際歸屬的 Team UUID。 |
| string | Key 實際歸屬的 Team 名稱(即對應的 Kubernetes Namespace),用於校正調用方意圖。 |
刪除 API Key
通過 UUID 刪除指定的 API Key。普通租戶只能刪除當前Team 下的 Key;管理員可以刪除 admin API Key 以外的任意 Key。嘗試刪除內建 admin API Key 會返回 403 Forbidden。
請求樣本:
API_KEY_ID="<uuid-from-list-api-keys>"
curl -fsS -X DELETE \
-H "X-API-KEY: ${E2B_API_KEY}" \
"https://api.your.domain.com/api-keys/${API_KEY_ID}"import os
import requests
api_key_id = "<uuid-from-list-api-keys>"
resp = requests.delete(
f"https://api.your.domain.com/api-keys/{api_key_id}",
headers={"X-API-KEY": os.environ["E2B_API_KEY"]},
timeout=10,
)
# 成功時返回 204 No Content。
try:
resp.raise_for_status()
except requests.HTTPError:
print(resp.text)
raise
無效UUID 與不存在的 UUID 一律返回 404 Not Found,介面不區分這兩種情況。錯誤碼
HTTP 狀態代碼 | 情境 |
| 請求體不合法(JSON 解析失敗)或目標 Namespace 不存在。 |
| 未攜帶 |
| 非管理員對其他 Team 進行寫操作,或嘗試刪除 admin API Key。 |
| 指定 UUID 的 API Key 不存在,或路徑參數不是合法 UUID 字串。 |
| 伺服器內部錯誤,例如缺少必要欄位、Kubernetes API 不可用及持久化儲存不可達等。 |
錯誤響應體為 JSON,包含 code、message、request_id 三個欄位,便於定位問題:
{
"code": 403,
"message": "You are not allowed to create an API key for another team",
"request_id": "c42c84d8-8420-4194-b48d-5bc5c8fe4ed6"
}