公開日: 2026-04-13
著者: Mike Nomitch, Gabi Villalonga, Simón
所要時間: 7 分
概要
AI 大規模言語モデルや OpenCode、Claude Code といったハーネスがますます高機能になるにつれ、チャットメッセージ、カンバンの更新、vibe コーディング UI、ターミナルセッション、GitHub コメントなどをきっかけにサンドボックス化されたエージェントを起動するユーザーが増えています。Sandbox は単なるコンテナを超える重要なステップです。なぜなら以下のような利点があるからです。
- セキュリティ: 信頼できないエンドユーザー(や悪意のある LLM)がサンドボックス内で実行されても、ホスト マシンや並行して動く他のサンドボックスを侵害しません。これは伝統的には(ただし常にではないにせよ)microVM で行われます。
- 速度: エンドユーザーは新しいサンドボックスを素早く立ち上げ、以前の状態を素早く復元できるべきです。
- 制御: 信頼されたプラットフォームは、サンドボックスという信頼されないドメイン内で操作を行える必要があります。ファイルのマウント、どのリクエストがアクセスできるかの制御、特定コマンドの実行などが該当します。
本日は、Sandboxes とすべての Containers に新たな重要な制御機能を追加したことをお知らせします: outbound Workers。これらはプログラム可能な egress プロキシであり、サンドボックスで実行されるユーザーがさまざまなサービスに簡単に接続し、可観測性を追加し、エージェントにとって重要な柔軟かつ安全な認証を提供できるようにするものです。
仕組み
以下は outbound Worker を使ってヘッダーにシークレットキーを追加する簡単な例です。
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"github.com": (request, env, ctx) => {
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
}
}
サンドボックス内で動くコードが "github.com" にリクエストを行うたびに、そのリクエストはハンドラを通してプロキシされます。これにより、各リクエストでログ、修正、キャンセルなど任意の処理を行えます。この例ではシークレットを安全に注入しています(後述)。プロキシはサンドボックスと同じマシン上で動作し、分散状態にアクセスでき、単純な JavaScript で簡単に変更できます。サンドボックスに対する認証まわりの可能性が大きく広がる点に私たちは興奮しています。
エージェント型ワークロードにおける一般的な認証手法
エージェント型ワークロードの核心的な問題は、ワークロードを完全には信頼できないことです。現時点で LLM が意図的に悪意を持つとは限りませんが、不適切なデータ利用や不正な操作を防ぐための保護を適用する必要があります。よく使われる方法はいくつかありますが、それぞれ欠点があります。
- 標準的な API トークン: 環境変数やマウントされたシークレットファイルを通してアプリケーションに注入される最も基本的な方法です。最も単純ですが最も安全性が低い方法とも言えます。サンドボックスが侵害されたり誤ってトークンを外部に送信したりしないと信頼する必要があります。エージェントを完全には信頼できないため、トークンの有効期限やローテーションを設定する必要があり、管理が手間になります。
- Workload identity tokens(OIDC トークン等): この方法は一部の問題を解決します。エージェントに一般的な権限を持つトークンを直接与える代わりに、エージェントの ID を証明するトークンを与え、これを短命なアクセス トークンに交換させます。OIDC トークンは特定のワークフロー終了後に無効化でき、有効期限管理も容易です。ただし、多くのサービスが OIDC に対してファーストクラスのサポートを持たないため、プラットフォーム側で独自のトークン交換サービスを用意する必要があり、導入が難しくなります。
- カスタムプロキシ: Workload identity tokens と組み合わせることで最大の柔軟性を提供します。サンドボックスの egress を信頼できるコードに通せれば、必要なルールを自由に挿入できます。上流サービスの RBAC が不十分で細かい権限制御ができない場合、自分でコントロールと権限を実装すればよい、という選択肢です。ただし、サンドボックスのすべてのトラフィックをどうやって横取りするのか、動的かつプログラム可能なプロキシをどう構築するのか、効率的にプロキシするにはどうするのか—これらは簡単ではありません。
これらの不完全さを踏まえ、理想的な認証機構は以下を満たすべきだと考えます:
- ゼロトラスト: 信頼されないユーザーにトークンを与えない。
- シンプル: 複雑なトークンの発行・回転・復号を伴わない、作成が容易な仕組み。
- 柔軟: 上流システムに細かいアクセス制御を期待しない。任意のルールを適用可能。
- ID 対応: 呼び出しを行っているサンドボックスを識別し、個別のルールを適用できること。
- 可観測性: どのような呼び出しが行われているか簡単に収集できること。
- 高性能: 中央集権的で遅い真実源への往復が発生しないこと。
- 透過性: サンドボックス内のワークロードが直接意識しなくても動作すること。
- 動的: ルールをその場で変更できること。
私たちは Sandboxes 向けの outbound Workers がこれらを満たすと考えています。以下で具体例を見ていきます。
Outbound Workers の実践
基本: 制限と可観測性
まずは非常に基本的な例を見ます: リクエストをログに記録し、特定の操作を拒否する例です。この場合、sandbox からのすべての発信 HTTP リクエストを横取りする outbound 関数を使います。少ない JavaScript で GET のみを許可し、それ以外のメソッドはログに残して拒否できます。
class MySandboxedApp extends Sandbox {
static outbound = (req, env, ctx) => {
if (req.method !== 'GET') {
console.log(`Container making ${req.method} request to: ${req.url}`);
return new Response('Not Allowed', { status: 405, statusText: 'Method Not Allowed'});
}
return fetch(req);
};
}
このプロキシは Workers 上で動作し、サンドボックス化された VM と同じマシン上で実行されます。Workers は高速レスポンスのために設計されており、追加のレイテンシは極めて小さいです。また Workers 上で動くため、可観測性が標準で得られます。Workers ダッシュボードでログや発信リクエストを確認したり、APM ツールにエクスポートしたりできます。
ゼロトラストな資格情報注入
エージェント向けにゼロトラスト環境を強制するにはどうするか考えてみましょう。たとえば、プライベートな GitHub インスタンスにリクエストしたいが、LLM にプライベート トークンを絶対に渡したくない場合です。outboundByHost を使って特定のドメインや IP に対する関数を定義できます。この例ではドメインが "my-internal-vcs.dev" の場合に保護された資格情報を注入します。サンドボックス化されたエージェントはこれらの資格情報にアクセスしません。
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
}
}
各サンドボックス・インスタンスごとに同じトークンを注入する必要はありません。コンテナの ID に基づいて応答を条件分岐するのも簡単です。
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
const authKey = await env.KEYS.get(ctx.containerId);
const requestWithAuth = new Request(request);
requestWithAuth.headers.set("x-auth-token", authKey);
return fetch(requestWithAuth);
}
}
Cloudflare Developer Platform の利用
前の例に気づいたかもしれませんが、outbound Workers を使うもう一つの大きな利点は Workers エコシステムへの統合が容易になることです。従来、ユーザーが R2 にアクセスしたい場合、R2 の資格情報を注入してからコンテナ内から公開 R2 API に呼び出しを行う必要がありました。KV、Agents、他の Containers、他の Worker サービスなども同様です。今では outbound Workers から任意の binding を直接呼び出せます。
class MySandboxedApp extends Sandbox {
static outboundByHost = {
"my.kv": async (req, env, ctx) => {
const key = keyFromReq(req);
const myResult = await env.KV.get(key);
return new Response(myResult);
},
"objects.cf": async (req, env, ctx) => {
const prefix = ctx.containerId
; const path = pathFromRequest(req);
const object = await env.R2.get(${prefix}/${path});
const myResult = await env.KV.get(key);
return new Response(myResult);
},
};
}
トークンを解析してポリシーを設定する代わりに、コードと任意のロジックでアクセスを条件付けできます。R2 の例ではサンドボックスの ID を使ってアクセス範囲を簡単に絞ることもできました。
制御を動的にする
ネットワーク制御は動的であるべきです。多くのプラットフォームでは Container や VM のネットワーク設定は静的で、次のような形になります:
{
defaultEgress: "block",
allowedDomains: ["github.com", "npmjs.org"]
}
これは全くないよりは良いですが、さらに改善できます。多くのサンドボックスでは起動時にポリシーを適用し、特定の操作を終えたら別のポリシーで上書きしたいことがあります。たとえばサンドボックスを起動して依存関係を NPM や GitHub から取得し、その後 egress をロックダウンする、といった運用です。これによりネットワークを開く時間を最小にできます。
これを実現するために outboundHandlers を使用できます。outboundHandlers により任意の outbound ハンドラを定義し、setOutboundHandler メソッドでプログラム的に適用できます。各ハンドラは params を受け取り、コードから振る舞いをカスタマイズできます。以下はカスタムの "allowHosts" ポリシーでいくつかのホスト名を許可し、その後 HTTP を無効化する例です。
class MySandboxedApp extends Sandbox {
static outboundHandlers = {
async allowHosts(req, env, { params }) {
const url = new URL(request.url);
const allowedHostname = params.allowedHostnames.includes(url.hostname);
if (allowedHostname) {
return await fetch(newRequest);
} else {
return new Response(null, { status: 403, statusText: "Forbidden" });
}
}
async noHttp(req) {
return new Response(null, { status: 403, statusText: "Forbidden" });
}
}
}
async setUpSandboxes(req, env) {
const sandbox = await env.SANDBOX.getByName(userId);
await sandbox.setOutboundHandler("allowHosts", { allowedHostnames: ["github.com", "npmjs.org"] });
await sandbox.gitClone(userRepoURL)
await sandbox.exec("npm install")
await sandbox.setOutboundHandler("noHttp");
}
この仕組みはさらに拡張できます。エージェントがエンドユーザーに "cloudflare.com への POST を許可しますか?" のように尋ね、その時点で必要なツールに応じてルールを変更する、という対話型ワークフローも可能です。動的な outbound Workers によりルールをその場で変更してこのレベルの制御を実現できます。
TLS サポートと MITM プロキシング
リクエストを許可・拒否するだけでなく内容を扱いたい場合、HTTPS リクエストを Workers プロキシで復号する必要があります。このため、各 Sandbox インスタンスごとに一時的なエフェメラルな認証局 (CA) と秘密鍵が生成され、その CA がサンドボックス内に配置されます。デフォルトではサンドボックス インスタンスはこの CA を信頼します。標準のコンテナインスタンスは必要に応じて信頼するように選択できます(たとえば sudo update-ca-certificates を呼ぶなど)。
export class MyContainer extends Container {
interceptHttps = true;
}
MyContainer.outbound = (req, env, ctx) => {
}
(本文はここで途切れていますが、概念としては Workers プロキシが HTTPS を終端して内容にアクセスできるよう、サンドボックス側で一時 CA を信頼させる仕組みを提供する、という点が重要です。)
まとめ
Outbound Workers を Sandbox と組み合わせることで、ゼロトラストな資格情報注入、動的なネットワーク制御、可観測性の向上、そして既存の Workers エコシステムとの緊密な統合が可能になります。これにより、エージェント化されたワークロードに対して安全かつ柔軟に認証と制御が行えるようになります。