複数のテナントが同じ Agent Sandbox クラスターを共有する場合、各テナントはチームを介して独立した Kubernetes 名前空間にマッピングされ、アクセス資格情報として独自の API キーを保持します。 管理者とテナントは、HTTP インターフェースを介して API キーを作成、クエリ、および失効させることができます。
概要
ack-sandbox-manager は、API キーとチームを管理するための E2B 互換の HTTP インターフェースセットを提供します。クラスター管理者とテナントは、これらのインターフェースを使用して、プログラムで API キーを作成し、チームをクエリし、未使用のキーを失効させることができます。
これらのインターフェースには、対応する E2B SDK や Kubernetes カスタムリソース定義 (CRD) はありません。HTTP 経由で直接呼び出す必要があります。
チーム
チームとは、API キーの認可境界です。Agent Sandbox では、チームは Kubernetes 名前空間に直接マッピングされる名前によって一意に識別されます。
組み込みの
adminチームはクラスターレベルであり、管理者によって管理されます。その admin API キーは、ack-sandbox-managerの起動時に初期化されます。その他のチーム名は、既存の Kubernetes Namespace に対応する必要があります。テナントがチーム
fooの API キーを作成する場合、Namespacefooが既に存在している必要があります。存在しない場合、リクエストはエラーを返します。
Namespace の一意性は、すでに十分な分離境界を提供しています。そのため、チームの一意の識別子 (UUID) は表示用のメタデータとしてのみ保持され、認証やリソース検索には使用されません。
API キー
API キーは、チームが保持する長期的なアクセス認証情報です。クライアントが ack-sandbox-manager のインターフェースを呼び出す際は、X-API-KEY リクエストヘッダーにキーを含める必要があります。API キーは次の内容を決定します:
呼び出し元が所属するチーム。
呼び出し元が通常テナントかクラスター管理者か、つまり呼び出し元が
adminチームに所属しているか。呼び出し元がアクセスできる Sandbox の範囲。通常テナントは自身の API キーで作成した Sandbox のみにアクセスできますが、管理者はすべての Sandbox にアクセスできます。
認可モデル
役割 | 現在のチームのキーのリスト表示 | チームのリスト表示 | 現在のチームのキーの作成 | 別のチームのキーの作成 | 現在のチームのキーの削除 | admin API キーの削除 |
管理者 (admin チーム) | サポート | サポート (すべてのチーム) | サポート | サポート | サポート | サポート対象外 (拒否) |
通常テナント | サポート | サポート (呼び出し元のチームのみ) | サポート | サポート対象外 | サポート | サポート対象外 |
組み込みの admin API キーは、いかなる状況でも削除できません。これにより、クラスターの管理性が保護されます。
前提条件
対象のクラスターの アドオン管理 に移動し、
ack-sandbox-managerがインストールされ、v0.6.0 以降にアップグレードされていることを確認します。その後、アドオンの 設定 ページで次の設定を構成します:[E2B_API_KEY 認証を有効にするかどうか] を選択して、API キー認証を有効化します。
[adminApiKey] (管理者 API キー) を設定します。
エンドツーエンドの例
JSON プロセッサー jq をインストールします。
次のスクリプトは、管理者がリソース環境とテナント認証情報を初期化してから、テナントがチームキーのライフサイクル全体を独立して管理するまでの、完全なエンドツーエンドのプロセスを示しています:
#!/usr/bin/env bash set -euo pipefail ADMIN_KEY="${ADMIN_KEY:?required}" # 2 つのプロトコルがサポートされています。この例では、ネイティブ 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. クラスター内で、管理者がチームの Namespace を作成します kubectl create ns "${TEAM_NAMESPACE}" # 2. 管理者が team-a のテナントキーを作成し、返されたプレーンテキストキーを記録します 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. テナントが自身のチームと既存のキーをリスト表示します curl -fsS -H "X-API-KEY: ${TENANT_KEY}" "${BASE_URL}/teams" curl -fsS -H "X-API-KEY: ${TENANT_KEY}" "${BASE_URL}/api-keys" # 4. テナントが 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. テナントが自身のチームのサブキーを削除します curl -fsS -X DELETE \ -H "X-API-KEY: ${TENANT_KEY}" \ "${BASE_URL}/api-keys/${SUB_KEY_ID}"
API リファレンス
チームのリスト表示
現在のユーザーに表示可能なチームを取得します。通常テナントは自身が所属するチームのみを表示でき、管理者はすべてのチームを表示できます。
リクエストの例:
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 | チームの UUID。表示のみに使用されます。 |
| string | チーム名。Kubernetes Namespace と同じです。 |
| string | このフィールドは常に空の文字列を返します。E2B SDK スキーマとの互換性のためにのみ保持されています。プレーンテキストキーを取得するには、 |
| boolean | これが呼び出し元のデフォルトチームかどうか。 |
API キーのリスト表示
現在の呼び出し元のチームに属するすべての API キーを取得します。
リクエストの例:
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 キーの UUID。キーを削除する際に使用されます。 |
| string | API キーの人間が判読可能な名前。 |
| string | API キーが作成された時刻。 |
| string | API キーを作成した呼び出し元の UUID。 |
| string | API キーが最後に使用された時刻。キーが一度も使用されていない場合、値は |
API キーの作成
新しい API キーを作成します。レスポンスボディの key フィールドには、新しく生成されたプレーンテキストキーが含まれます。プレーンテキスト key フィールドは、キーの作成時に一度だけ返され、その後はどのインターフェースを通じても取得できません。直ちに保存してください。
例を実行する前に、team-a Namespace が既に存在することを確認してください。呼び出し元が通常テナントの場合は、リクエストボディから 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 | はい | API キーの人間が判読可能な名前。 |
| string | いいえ | 対象チームの名前。デフォルトは呼び出し元自身のチームです。管理者のみが別のチームを指定でき、そのチームは既存の Kubernetes Namespace に対応している必要があります。通常テナントは、このフィールドを省略する必要があります。 |
レスポンスフィールド:
フィールド | タイプ | 説明 |
| string | 新しい API キーの UUID。後で削除する際に使用されます。 |
| string | プレーンテキストキー。一度だけ返されるため、直ちに保存する必要があります。 |
| string | API キー名。リクエストボディの値と同じです。 |
| string | API キーを実際に所有するチームの UUID。 |
| string | API キーを実際に所有するチームの名前、つまり対応する Kubernetes Namespace です。これは呼び出し元の意図を確認するために使用されます。 |
API キーの削除
UUID で指定された API キーを削除します。通常テナントは、現在のチームのキーのみを削除できます。管理者は、admin API キーを除く任意のキーを削除できます。組み込みの admin API キーを削除しようとすると、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 を返します。インターフェースはこれら 2 つのケースを区別しません。エラーコード
HTTP ステータスコード | シナリオ |
| リクエストボディが無効 (JSON 解析に失敗)、または対象の Namespace が存在しません。 |
|
|
| 管理者以外のユーザーが別のチームに対して書き込み操作を実行しようとした、または admin API キーを削除しようとしました。 |
| 指定された UUID の API キーが存在しない、またはパスパラメーターが有効な 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"
}