AIエージェントに生成させたコードをそのまま実行する構成が当たり前になり、「どう隔離するか」を扱うOSSは一気に増えた。しかし隔離した後に必ず出てくる問題——そのサンドボックスの中で動くClaude CodeやgitやcurlにAPIキーをどう渡すのか——を正面から実装しているものは少ない。Alibaba発のOpenSandbox(Apache-2.0・★12,726)は、この「隔離した後の鍵と出口」に固有の答えを持っている。

OpenSandboxのKubernetesデプロイ実演(出典: 公式Kubernetesガイド 同梱の deploy-example.gif をmp4化)

30秒でわかるOpenSandbox

正体:AIエージェント用の汎用サンドボックス基盤。Python/Java/TS/C#/Goの5言語SDK+osb CLI+MCPサーバーを持ち、DockerとKubernetesの両方で動く
他と違う点:隔離そのものではなく「サンドボックスに本物の鍵を置かない」Credential Vault。中には偽の値を渡し、外向きHTTPSの途中でサイドカーが本物の認証ヘッダを注入する
実測した保証:この機能は条件が揃わないと起動を拒否する。本記事ではソースのReady()に環境変数を1つずつ与え、4つの拒否理由を実際に取り出した
落とし穴pip install opensandbox-mcp は現状そのままでは起動しない(依存のmcpが2.0系に解決されるため)。回避策も実測済み

隔離方式そのものの比較——MicroVMかコンテナか——を先に押さえたい場合は、AIエージェントフレームワーク比較2026|LangGraph・CrewAI・Dify等9種をStar数・実コードで検証でエージェント基盤全体の地図を確認してから戻ってくると位置づけが掴みやすい。


OpenSandboxとは何か——「実行層の標準化」を狙うAlibaba発OSS

OpenSandboxは、AIアプリケーション向けの汎用サンドボックスプラットフォームである。コーディングエージェント、GUIエージェント、エージェント評価、AIコード実行、強化学習(RL)トレーニングといった用途を想定し、統一されたサンドボックスAPIとDocker/Kubernetesランタイムを提供する。

OpenSandboxの主要な数字:GitHubスター12.7k、公式SDK 5言語、MCPツール19本
OpenSandboxの規模。スター数は2026-08-15時点のGitHub API実測値

出自について1点整理しておく。リポジトリは opensandbox-group/OpenSandbox にあるが、Go SDKのモジュールパスは github.com/alibaba/OpenSandbox/...、JavaのgroupIdは com.alibaba.opensandbox、npmは @alibaba-group/opensandbox と、配布物の名前空間はAlibaba側を指している。これはフォークや別プロジェクトではない——github.com/alibaba/OpenSandbox に対してHTTPリクエストを投げると opensandbox-group/OpenSandbox へリダイレクトされる。組織名の移動であり、Alibaba Group発のプロジェクトが独立したorg名に移った形だ。ソースコードのライセンスヘッダも “Copyright 2026 Alibaba Group Holding Ltd.” で統一されている。

プロジェクトはApache License 2.0で公開され、CNCF Landscapeに掲載、OpenSSF Best Practicesバッジも取得している。リリースイメージはDocker Hub・GitHub Container Registry・Alibaba Cloud Container Registryの3か所に公開され、タグ付きリリースはCosignでキーレス署名され、provenance attestationも付く。

何を提供するのか

OpenSandboxが担う範囲は「隔離環境そのもの」だけではない。README上の機能は7つに整理されているが、実際に触ってみると次の3層に分かれている。

プロトコル層:サンドボックスのライフサイクルAPIと実行APIをOpenAPIで定義(specs/)。独自のサンドボックスランタイムを実装して差し替えられる
ランタイム層:Docker(ローカル向け)とKubernetes(大規模分散スケジューリング向け)。下位の隔離にはgVisor・Kata Containers・Firecracker microVMを選択できる
周辺の運用層:ingressゲートウェイによる流入制御、per-sandboxのegress制御、そしてCredential Vault

この3層目——特にegressとCredential Vault——が、他のサンドボックスOSSと比べたときの明確な差になっている。CubeSandbox解説:60ms起動MicroVMでAIエージェントを安全実行するTencentの設計思想で扱ったCubeSandboxがMicroVMの起動速度と密度で勝負しているのに対し、OpenSandboxは起動速度の数字を前面に出さず、「入ってきた後」「出ていくとき」の制御に実装の重心を置いている。同じ「サンドボックス」というカテゴリでも設計の力点が違う。


OpenSandboxのインストールと最初のサンドボックス

必要なのはDocker(ローカル実行に必須)とPython 3.10以上だけだ。サーバーの設定ファイルを生成して起動する。

# サンドボックスサーバーの設定を生成して起動(Docker ランタイム)
uvx opensandbox-server init-config ~/.sandbox.toml --example docker
uvx opensandbox-server

# 別ターミナル:Python SDK と Code Interpreter を入れる
pip install opensandbox
uv pip install opensandbox-code-interpreter

CLIを使う場合は osb を入れて接続先を設定する。osb はサンドボックスの作成・コマンド実行・ファイル操作・診断・egressポリシー管理を1つのコマンドにまとめたもので、config / sandbox / command / file / egress / diagnostics / devops / skills のサブコマンドを持つ。

pip install opensandbox-cli

osb config init
osb config set connection.domain localhost:8080
osb config set connection.protocol http
osb config set connection.api_key <your-api-key>

osb sandbox create --image python:3.12 --timeout 30m -o json
osb command run <sandbox-id> -o raw -- python -c "print(1 + 1)"

接続先が無いときの挙動を先に確認しておく

サーバーを立てる前に osb sandbox list を実行すると、終了コード1で Network connectivity error: [Errno 61] Connection refused を返す。エラーは整形されたパネルで表示され、スタックトレースは出ない。CI等で叩くときはこの終了コード1を「サーバー未到達」の判定に使える(検証環境:macOS 15 / Python 3.14 / opensandbox-cli 0.1.1)。

osb skills は少し毛色が違い、AIコーディングツール側に「OpenSandboxの操作手順」をスキルとして書き込むためのサブコマンドだ。サーバー未接続でも osb skills list は動き、同梱スキル5本(sandbox-troubleshooting / sandbox-lifecycle / command-execution / file-operations / network-egress)とインストール先が一覧される。Claude Code向けにはプロジェクト単位で .claude/skills/<skill-name>.md、グローバルには ~/.claude/skills/ に1ファイルずつ配置する方式だ。osb skills install <skill> --target codex --scope project のように非対話でも入れられるため、CI等で環境を揃える用途にも使える。


Credential Vault——サンドボックスに本物の鍵を置かない

ここからが本題である。

AIエージェントをサンドボックスに閉じ込めても、そのエージェントが外部APIを叩く以上、どこかにAPIキーが要る。素直に実装すると ANTHROPIC_API_KEY のような環境変数をサンドボックスに渡すことになるが、これは隔離の意味をかなり削ってしまう。サンドボックス内で動いているのは「信頼できないコード」あるいは「プロンプトインジェクションを受けうるエージェント」だからだ。環境変数に本物の鍵があれば、env を打つだけで抜ける。

APIキーの置き場所の比較:素のサンドボックスは環境変数に本物を渡すが、Credential Vaultはサンドボックスに偽の値だけを渡す
Credential Vaultが変えるのは「鍵の置き場所」そのもの

OpenSandboxのCredential Vaultは、この前提を反転させる。本物の認証情報はホスト側SDKからegressサイドカーに書き込まれ、サンドボックスのプロセスには偽の値または空の値しか渡らない。 サンドボックス内のClaude Code・git・curl・パッケージマネージャ・モデルAPIクライアントが外向きHTTPSリクエストを出すと、サイドカーがそのリクエストをCredential Vaultのバインディングと突き合わせ、通過する瞬間に認証ヘッダを注入する。

Credential Vaultのリクエストフロー図
Credential Vaultのリクエストフロー(出典: 公式 Credential Vault ガイド、Apache-2.0)

公式ガイドが説明する流れは次の通りだ。

・ライフサイクルサーバーがサンドボックスにegressサイドカーを付与する
・SDKが認証情報とバインディングをサイドカーのCredential Vault APIへ書き込む
・サンドボックスのプロセスは偽または空の認証情報環境変数で起動する
・サンドボックスがHTTPSリクエストを出すと、サイドカー内の透過MITMがリクエストのメタデータを検査する
・スキーム・ホスト・ポート・メソッド・パスに対してバインディングがちょうど1つだけ一致した場合に、設定された認証ヘッダとスコープ付きプレースホルダ置換を注入する
・秘密の値はvaultのレスポンスとレスポンスヘッダから伏字化される

注入方式(auth ルール)は5種類ある。bearerAuthorization: Bearer <値>)、basic(base64済みの username:password)、apiKey(指定ヘッダ名に値を入れる)、customHeaders(複数ヘッダをそれぞれ別の認証情報で埋める)、passthrough(ヘッダを注入せず、パス・クエリ・ボディのプレースホルダ置換のみ行う)。バインディングが一致しなかったリクエストは、そのまま素通しで転送される。

設計上重要なのは、MITMプロセスが使う「有効なvault」がサイドカー内部のUnixドメインソケットで提供されている点だ。公式ガイドは、サンドボックスのワークロードが通常のサーバープロキシ経路からこの状態を取得することはできない、と明記している。

pause/resumeで消える——運用上いちばん刺さる制約

Credential Vaultのエントリはegressサイドカーのプロセスメモリ上にしかなく、サンドボックスのルートファイルシステムにもPodテンプレートにも含まれない。Kubernetesのpauseはスナップショット取得後にPodを削除するため、resumeで作られる新しいサイドカーのvaultは空で始まる。resume後に信頼できるクライアントから認証情報とバインディングを再投入するまで、鍵の注入は復活しない。 Dockerのpause/unpauseはプロセスを維持するが、サイドカーの置換・再起動が起きれば同じく再投入が要る。元のvaultリクエスト(または秘密管理システムへの参照)は、サンドボックスの外側の信頼できるコントロールプレーンに保持しておく必要がある。

もう1つ、既知の非対応構成がある。Credential Vaultはegressサイドカーの透過リダイレクトとMITM経路に依存するため、同じネットワーク名前空間にIstio/Envoyのような透過型サービスメッシュサイドカーが注入されていると、両者が外向きトラフィックの横取りを取り合う。OpenSandboxはこの組み合わせを現時点でサポートしていない。運用としては、Credential Vaultが必要なサンドボックスPodではメッシュ注入を止めるか、メッシュを使うPodではcredentialProxyを有効化しないか、のどちらかを選ぶことになる。


「拒否される条件」を実測する——Ready()の4つの門

Credential Vaultのようなセキュリティ機能は、正常系が動くことより、条件が欠けたときに黙って弱い状態で動き出さないことのほうが重要だ。fail-open(条件が欠けても通してしまう)なら、保証は事実上無い。

OpenSandboxの実装は、この点をソースで確認できる。components/egress/pkg/credentialvault/vault.goReady() が起動前の関門になっており、条件を満たさない場合はそれぞれ別のエラーを返す。そこで実際に、この関数へ環境変数を1つずつ与えて何が返るかを取り出した(Go 1.26.5 / main HEAD 8212561)。

Credential Vaultの4つの拒否条件:APIトークン、透過mitmproxy、上流TLS検証、dns+nftモード
Ready()が返す4つの拒否理由。1つでも欠ければCredential Vaultは起動しない

実測結果は次の通りだった。

与えた条件 Ready() の返り値
egress API認証トークンなし credential vault requires egress API auth token
すべて未設定(=既定状態) credential vault requires transparent mitmproxy
透過mitmproxyのみ有効 credential vault requires dns+nft egress enforcement
+上流TLS検証を無効化 credential vault rejects insecure upstream TLS mode
+モードを dns(DNSのみ) credential vault requires dns+nft egress enforcement
+モードを dns+nft <nil>(通過)

読み取れることが3つある。

1つめ:既定状態は「動かない」側に倒れている。 components/egress/pkg/constants/mode.goParseEgressMode は、環境変数が空文字のときトークン dns だけを立てて返す。つまり何も設定しなければDNSのみのモードと解釈され、Credential Vaultはそれを拒否する。公式ガイドも理由を明記している——DNSのみのポリシーは、IPアドレスへ直接接続されると迂回できてしまうためだ。設定を忘れた結果として「鍵が注入されないまま素通しで動く」ことが起きない構造になっている。

2つめ:TLS検証の無効化は独立した拒否理由になっている。 OPENSANDBOX_EGRESS_MITMPROXY_SSL_INSECURE を立てると、モードの条件より先にこの検査で弾かれる(ソース上の並び順がそうなっている)。上流のTLSを検証しないMITMに本物の鍵を注入させない、という判断だ。

3つめ:これは机上の読み取りではなくテストで担保されている。 リポジトリ同梱の TestCredentialVaultWriteRejectsDNSOnlyEnforcement は、dns-onlyの状態でCredential Vaultへの書き込みAPIを叩き、HTTP 412 Precondition Failed とレスポンスボディに dns+nft が含まれることを検証している。手元で components/egress のテストを走らせたところ、このテストを含む TestCredentialVault* 10本すべてがパスした。

# Credential Vault のfail-closed挙動を自分で確かめる
git clone --depth 1 https://github.com/opensandbox-group/OpenSandbox.git
cd OpenSandbox/components/egress
go test . -run 'TestCredentialVault' -v

同じ拒否はサーバー側にも独立して実装されている。server/tests/test_validators.pytest_rejects_dns_only_mode は、リクエスト検証の段階でdns-onlyを弾き、エラーメッセージに dns+nft を含めることを確認している。サイドカー側とサーバー側の2層が、同じ構成をそれぞれ独立に拒否する——これがOpenSandboxのCredential Vaultで最も評価できる部分だ。

なお、バインディングを使う際のegressポリシーは defaultAction="deny" が要求される。デフォルト許可は後方互換のため当面受け付けられるものの、credential vault: default-allow egress policy is deprecated and may allow credential destination bypass という警告がログに出る。こちらは拒否ではなく警告に留まっている点は、設計の線引きとして把握しておきたい。

ポリシー自体の形は素直なJSONで、テストフィクスチャで使われている最小構成は次のようになっている。

{ "defaultAction": "deny",
  "egress": [ { "action": "allow", "target": "code.example.com" } ] }

既定を拒否に倒し、認証情報のバインディングが参照するホストだけを明示的に許可する——という運用になる。公式ガイドはバインディング側のパス一致も意図的に狭くすることを勧めており、たとえばAnthropic APIなら /v1/* のように限定する。ここを緩くすると、許可済みホストの別エンドポイントへ鍵が付いて飛ぶ余地が残るためだ。あわせて、バインディングが存在するのにegressポリシーが無い場合は credential vault bindings require an egress policy で弾かれる。「鍵の注入先」と「通信の許可先」を必ずセットで宣言させる設計になっている。

自分の構成を確認するコマンド

# egressの動作モードが dns+nft か(空・dns のみなら Credential Vault は起動しない)
echo "${OPENSANDBOX_EGRESS_MODE:-<unset→dns-onlyとして扱われる>}"

# 上流TLS検証を切っていないか(true なら拒否される)
echo "${OPENSANDBOX_EGRESS_MITMPROXY_SSL_INSECURE:-<unset=OK>}"

# 稼働中サンドボックスのegressポリシーを確認(defaultAction が deny か)
osb egress get <sandbox-id> -o json

隔離ランタイムの選択——gVisor / Kata / Firecracker

OpenSandbox自体は隔離機構を新規実装していない。下位のセキュアコンテナランタイムを選択する設計で、公式ガイド docs/guides/secure-container.md に比較表がある。

ランタイム 隔離方式 起動時間 オーバーヘッド 向いている用途
gVisor ユーザー空間カーネル(syscall傍受) 約10-50ms 約50MB 低オーバーヘッドの汎用ワークロード
Kata(QEMU) QEMUハイパーバイザによる完全VM 約500ms 約20-50MB 互換性と隔離を最大化したい場合
Kata(Firecracker) Firecracker microVM 約125ms 約5MB 高密度・最小フットプリント
Kata(Cloud Hypervisor) Cloud Hypervisor 約200ms 約10-20MB 性能と隔離のバランス

設定は "gvisor" / "kata"(既定はQEMU)/ "firecracker" を指定する形で、Dockerモードでは docker_runtime = "runsc" のようにOCIランタイム名を直接書く。Kata(Cloud Hypervisor)も選べるため、起動時間とメモリのどちらを優先するかで4択から選ぶ構成だ。ネットワーク側は、流入をingressゲートウェイが統一的に受けてルーティングし、流出はサンドボックスごとのegress制御が担う——という具合に、入口と出口で別コンポーネントに分かれている。Credential Vaultが出口側(egress)の実装として載っているのは、この分担があるためだ。

この表は、OpenSandboxが「隔離の強さ」ではなく「隔離の上に載る運用機能」で勝負していることを裏返しに示している。microVMそのものを深掘りしたい場合は、Firecrackerを軸にした構成の記事のほうが解像度が高い。用途で言えば、1台のマシンでエージェントごとに使い捨て環境を作りたいならmicroVM系、複数言語のSDKからK8sクラスタ上のサンドボックス群を統一APIで扱いたいならOpenSandbox、という住み分けになる。


MCPサーバーとosb CLI——実測でわかった常駐コストと落とし穴

OpenSandboxはMCPサーバーも提供しており、Claude CodeやCursorからサンドボックスの作成・コマンド実行・テキストファイル操作を呼べる。

落とし穴:pip install opensandbox-mcp はそのままでは起動しない

READMEの手順どおりに入れて実行すると、現状では起動に失敗する。

ModuleNotFoundError: No module named 'mcp.server.fastmcp'

原因を切り分けた。opensandbox-mcp 0.1.1 の依存は mcp とだけ書かれておりバージョン範囲が指定されていない。そのためpipは最新の mcp 2.0.0 を選ぶが、2.0系では mcp/server/ ディレクトリそのものが無くなり mcpserver / lowlevel へ構成が変わっている。opensandbox_mcp/server.pyfrom mcp.server.fastmcp import Context, FastMCP を実行するため、import時点で落ちる。

同じPython・同じ opensandbox-mcp 0.1.1 のまま mcp だけを1.29.0に落として再実行したところ、サーバーは正常に起動し ListToolsRequest を処理した。パッケージ側の依存指定の問題であり、環境固有の事象ではない。

# 回避策:mcp を 1.x に固定してから使う
pip install opensandbox-mcp "mcp<2"
opensandbox-mcp --domain localhost:8080 --protocol http

常駐コスト:ツール19本で47,800バイト

MCPサーバーはstdioで直接ハンドシェイクして中身を測れる。initializenotifications/initializedtools/list を流し込み、返ってきたツール定義のバイト数を数えた。

MCPツールごとのtools/list実測バイト数。sandbox_createが8,741バイトで最大
tools/list の実測バイト数(上位6本)。接続している間ずっとコンテキストを占有する

公開ツールは19本、tools/list のペイロード全体は 47,800バイトだった。内訳の上位は sandbox_create 8,741B、sandbox_list 6,502B、command_run 5,692B、sandbox_connect 5,554B、sandbox_get_info 4,444B。sandbox_create 1本だけで全体の18%を占める。

これは「重い」と断じる数字ではないが、MCPサーバーを繋いでいる間はツール定義が毎リクエストのコンテキストに載り続ける点は意識しておきたい。サンドボックス操作を常時使わないセッションでは、必要なときだけ有効化するほうが合理的だ。エージェントのコンテキストを何が食っているかという観点は、OpenViking徹底解説で扱ったコンテキスト管理の議論とそのまま地続きになる。


OpenSandboxと他のサンドボックスOSSの使い分け

サンドボックス系OSSは選択肢が増えたぶん、比較軸を「起動速度」だけに置くと選定を誤る。実際に触った範囲での整理は次の通りだ。

観点 OpenSandbox MicroVM系(CubeSandbox等) オールインワン系(AIO Sandbox等)
主眼 統一APIと運用機能(出口制御・鍵) 起動速度と実行密度 単一コンテナへの機能同梱
隔離 gVisor/Kata/Firecrackerを選択 独自microVM コンテナ
認証情報 Credential Vaultで外部注入 基本は環境変数 基本は環境変数
ネットワーク ingress+per-sandbox egress(dns+nft) eBPF等でフィルタ 通常のコンテナNW
スケール Kubernetesランタイムで分散 高密度単一ホスト〜クラスタ 単一ホスト前提が多い
SDK 5言語+CLI+MCP 言語限定のことが多い SDK同梱
flowchart TD A["サンドボックスを選ぶ"] --> B{"エージェントに
外部APIの鍵を
渡す必要があるか"} B -->|"ある"| C{"鍵をサンドボックス内に
置いてよいか"} B -->|"ない"| D["起動速度・密度で選ぶ
(microVM系が有利)"] C -->|"置きたくない"| E["OpenSandbox
Credential Vault"] C -->|"許容できる"| D E --> F{"egressモードを
dns+nftにできるか"} F -->|"できる"| G["Credential Vault 有効"] F -->|"できない"| H["起動を拒否される
→ 鍵の設計を見直す"]

判断の起点は「そのサンドボックスの中で動くエージェントに、外部APIの鍵を渡す必要があるか」だ。渡す必要がなく、ただ生成コードを実行するだけなら、起動速度と密度で選ぶほうが素直である。渡す必要があり、かつプロンプトインジェクションを現実的なリスクとして扱うなら、Credential Vaultのような外部注入の仕組みは選定理由になる。

ただし前節で見たとおり、その機能は dns+nft を要求する。nftによる強制ができない環境——たとえばそのcapabilityを与えられないマネージド環境——では、Credential Vaultは有効化できない。「使えるかどうか」は機能の有無ではなく、egressの強制を自分の基盤で満たせるかで決まる。導入検討では、ここを最初に確認したほうが手戻りが少ない。

コーディングエージェントを実際に載せる側の話としては、OpenHands 使い方のような自律型エージェントをサンドボックスに閉じ込める構成が典型的なユースケースになる。OpenSandboxは公式exampleにClaude Code・Gemini CLI・OpenAI Codex CLI・Qwen Code・Kimi CLIそれぞれをサンドボックス内で動かす手順を持っており、この用途を主要な想定に置いている。


まとめ——評価できる点と、導入前に確認すべき点

OpenSandboxで確認できたことを整理する。

評価できる点
・サンドボックスに本物の認証情報を置かないCredential Vaultという明確な設計判断がある
・その保証がfail-closedで実装されている。既定状態(環境変数未設定)はdns-onlyと解釈され、Credential Vaultは起動を拒否する
・拒否はサイドカー(vault.go)とサーバー(リクエスト検証)の2層が独立して行い、双方にテストがある
・Credential Vaultは公開済みのリリースで使える。ガイドが要求する opensandbox-server >= 0.2.0 に対しPyPIは0.2.2、Python SDK >= 0.1.11 に対し0.1.15が公開済みで、SDK 0.1.15に credential_proxy が実在することも確認した

導入前に確認すべき点
dns+nft を強制できる環境が必要。nftを使えない基盤ではCredential Vaultは選べない
・pause/resumeでvaultは空になる。再投入の経路をサンドボックスの外に用意しておく
・Istio/Envoy等の透過型サービスメッシュとは同居できない
・default-allowのegressポリシーは拒否ではなく警告止まり。defaultAction="deny" は自分で設定する
opensandbox-mcpmcp<2 の固定が要る(記事公開時点)

AIエージェントの実行環境をどう隔離するかという議論は、そろそろ「隔離できるか」から「隔離した上で必要な権限だけをどう渡すか」に移りつつある。OpenSandboxのCredential Vaultは、その移行を具体的なコードとして見せている実装のひとつだ。


参照ソース

opensandbox-group/OpenSandbox — GitHub(Apache-2.0。README・スター数・リリース構成)
Credential Vault ガイド — 公式ドキュメント(注入方式・要求バージョン・pause/resume制約・サービスメッシュ非対応)
Secure Container Runtime ガイド — 公式ドキュメント(gVisor/Kata/Firecrackerの起動時間・オーバーヘッド比較表)
components/egress — 公式リポジトリpkg/credentialvault/vault.goReady()pkg/constants/mode.goParseEgressModecredential_vault_handler_test.go
Kubernetesデプロイガイド — 公式ドキュメント(冒頭デモ動画の出典)