Agentにコードを実行させる——マシンを壊さずに
数ヶ月前、あるAgentが「テストデータベースのリセット」のもっとも綺麗な方法として、空の変数から構築したパスに rm -rf を実行することを選択するのを目の当たりにした。実際に提案されたコマンドは rm -rf /$DIR であり、$DIR は空だった。デフォルトモードで動かしていたため最初に確認を求めてきたので気づいた。「いいえ」をクリックし、スクリプトを修正し、手を少し震わせながらコーヒーを淹れに行った。
それは本物のラップトップ上の出来事だった。SSHキー、~/.aws/credentials、2ディレクトリ上の .env に入ったStripeの制限付きキー、まだpushしていないサイドプロジェクトの唯一のコピー——すべてが同じマシンに存在していた。Agentは悪意を持っていたわけではない。ただ間違っていたのだ。自信満々の若手が間違えるように。ただし、彼は1分間に200コマンドを入力し、疲れてスローダウンすることは決してない。
時間をかけて内面化すべきだったこと:危険はAgentが暴走することではない。自信ある間違ったコマンドと、環境変数のcredentialと、インターネットに到達できるネットワーク接続——この3つが退屈なかたちで交差することが危険だ。悪役は必要ない。事故と漏洩経路があれば十分で、ほとんどのセットアップはデフォルトでその両方を備えている。
要約: 分離はスペクトラムであり、スイッチではない。最も安価なレイヤーはAgentのpermissionプロンプトだ。最も強固なのは独自カーネルを持つmicroVMだ。自分で書いたコードの日常業務には、OSレベルのガードとpermissionゲートで十分だ。書いていないもの——ランダムなリポジトリ、スクレイピングしたスクリプト、別のAgentが生成したコード——については、OSが言いくるめられない本物の境界が必要だ。以下では梯子全体と、各段に実際に立っている場所を示す。
悪役モデルではなく、事故モデル
これを正直にthreat-modelすると、図はシンプルになる。Agentがコードを実行するとき、問題が起きうることは3つある:
- ローカルの状態を破壊する。 ファイルを削除する、間違ったものを上書きする、自分のブランチをforce-pushで上書きする。
- シークレットを読み取り、どこかに送信する。
~/.sshをgrepし、env変数のトークンを見つけて、あるドメインにcurlする。これが人々が過小評価する種類だ。 - 信頼したことのないコードを実行する。 依存関係のpost-installスクリプト、生成されたpayload、「ちょっと見るだけ」とcloneしたリポジトリ。
Permissionプロンプトは(1)と部分的に(3)に対応する。(2)に対してはほぼ無力だ。コマンドが実行される時点で、シークレットはすでにプロセスの環境に存在しているからだ。だから答えは「すべてを解決するツールを見つけること」ではない。安価なレイヤーを積み上げて愚かな破壊を止め、プロンプトへの説得だけでは不十分なケースのために下に本物の境界を追加することだ。
第1段:Agentのpermissionゲート
コーディングAgentはすでにpermissionモデルを持って出荷されており、それは持っている中で最も安価なコンテインメントだ。Claude Codeは、例えば異なるpermissionモードを持つ:default(読み取りは自由に実行され、それ以外はすべてプロンプト)、acceptEdits(ファイル編集と mkdir、mv、cp などの少数のファイルシステムコマンドが作業ディレクトリ内で自動承認)、plan(調査のみ、編集なし)、auto(別のclassifierモデルが各アクションをレビューしてエスカレーションをブロック)、dontAsk(事前承認済みツールのみ実行、ロックされたCIのため)、そして bypassPermissions——--dangerously-skip-permissions フラグ。ドキュメントはこれについて明確だ:「このモードはコンテナ、VM、インターネットアクセスのないdevコンテナなどの分離された環境でのみ使用してください。」
これらのモードは本物で有用だが、何であるかを理解しておく必要がある:コマンドを実行する前に、コマンド文字列に基づいて行われる決定であり、autoモードではモデルの判断も加わる。これはポリシーレイヤーであり、壁ではない。間違える可能性があり、創造的なコマンドは名前が示す以上のことをする可能性がある。私は探索には plan モードを使い、インフラに触れるものには default を使うが、信頼していないコードのセキュリティ境界としてはどちらも扱っていない。(Claude Codeのhookをガードレールとして別のところで書いた——同じ注意書き:hookはプロセス内で強制されるポリシーであり、分離ではない。)
第2段:モデルが言い返せないOSレベルのガード
これがほとんどのソロビルダーが飛ばす段であり、努力対安全の比率が最もよいものだ。考え方:モデルが実行すると決定したものに関係なく、すべてのシェルコマンドとその子プロセスに対してOSにファイルシステムとネットワークの境界を強制させる。
Claude CodeはこれをsandboxedなBashツールとして提供する。macOSでは組み込みの Seatbelt フレームワークを使う——インストール不要だ。LinuxとWSL2ではbubblewrapでファイルシステムを分離し、socat でトラフィックをプロキシ経由でルーティングする。デフォルトでは、sandboxedコマンドは作業ディレクトリとセッションの一時ディレクトリにしか書き込めず、ネットワークアクセスはデフォルトで拒否される:「ドメインは事前に許可されていない。コマンドが初めて新しいドメインを必要とすると、Claude Codeは承認を求める。」
permissionプロンプトよりも優れている理由:ドキュメントははっきり述べている。「オペレーティングシステムは実行中のプロセスにsandbox境界を強制するため、モデルが実行を選んだものに関わらず、また許可されたコマンドが名前の示す以上のことをしても、それは保たれる。」それがポリシーと強制の違いだ。
ドキュメントから直接引用した、2回読む価値のある注意点が2つある。1つ目、デフォルトの読み取りポリシーはまだcredentialファイルの読み取りを許可している——「このデフォルトはまだ ~/.aws/credentials や ~/.ssh/ などのcredentialファイルの読み取りを許可する。」それらを自分で denyRead に追加しなければならない。2つ目、ネットワークプロキシはホスト名でallowlistを強制し、「アウトバウンドトラフィックをターミネートしたりTLSインスペクションを行ったりしない」ため、github.com のような広い許可がドメインフロンティングによる漏洩経路になりうる。効果的なsandboxingは、彼らの言葉では「ファイルシステムとネットワークの分離の両方が必要」だ——片側を広げると静かにもう片側を台無しにできる。
私の方針:これを有効にし、credentialディレクトリの読み取りを拒否し、ネットワークallowlistを締めておく。無料で最も一般的な事故を防ぐ。ただし、これは本物のマシンへのハードニングレイヤーであり、クリーンルームではない。
第3段:コンテナ
コンテナは明らかな次のステップであり、信頼できるが散らかった仕事には十分だ。Docker内でAgentを動かし、プロジェクトディレクトリだけをマウントし、そのプロジェクトに必要なenv変数だけを渡す。被害範囲はコンテナだ。コンテナを削除すれば被害は消える。
正直な制限はコンテナが常に持っていたもの:ホストカーネルを共有する。コンテナはnamespaceとcgroupであり、別のマシンではない。自分で書いたコードには十分だ——現実的なリスクは事故であり、namespace境界は事故をよく封じ込める。本当に信頼できないコードに対しては、コンテナ内のカーネルエクスプロイトがホストに到達する:まれだが現実の攻撃クラスだ。中間の選択肢もある——GoogleのgVisorはコンテナのsyscallがホストに到達する前にユーザー空間カーネルでインターセプトし、完全なVMなしで攻撃面を縮小する。通常のコンテナより強く、ハードウェア仮想化より弱く、測定可能なI/Oコストがある。
Agentのための実用的なコンテナ設定:非rootユーザーとしてAgentを実行するdevコンテナで、プロジェクトだけをマウントし他は何もない。その非rootの詳細は重要だ——Claude Codeはrootとしてのbypass-permissionsモードでの起動を拒否し、devコンテナ設定は部分的に、自律的な実行が安全に放置できるよう非rootユーザーを提供するために存在する。
第4段:microVM、本物の壁
信頼していないコードを実行する必要があるとき、2026年のゴールドスタンダードは microVM だ:独自のカーネルを持つ本物の仮想マシン、ミリ秒単位でブートし、デバイスモデルはほぼゼロにまで削ぎ落とされている。Firecracker——AWSがLambdaとFargateのために構築したオープンソースVMM——はすべての人が使うものだ。その設計哲学全体は小さな攻撃面にある——「各microVMのメモリフットプリントと攻撃面積を減らすために不要なデバイスとゲスト向け機能を除外し」、BIOSなし・完全なデバイスモデルなしの最小カーネルをブートし、各ゲストをKVMハードウェア仮想化上で実行する。
セキュリティの主張は構造的だ:各ワークロードはハードウェア仮想化境界上に独自のカーネルを持つため、1つのmicroVM内のカーネルエクスプロイトはホストや隣接するVMに到達できない。コンテナが提供できない境界だ。コンテナには独自のカーネルがないのだから。コストはかつて起動時間だった;Firecrackerの約125msブートはその大部分を解消した。
Firecrackerを自分で運用したいと思うことはほとんどないはずだ。ホステッドサービスの存在意義はそこにある。
第5段:ホステッドsandboxサービス
「自分のものではないコンピューターをAgentに与える」という発想が正しく聞こえるなら、それはホステッドsandboxであり、このスペースは昨年で大きく成熟した。
Vercel Sandbox は2026年1月30日に一般公開された(アナウンス)。各sandboxをFirecracker microVM内で独自のファイルシステムとネットワークを持つAmazon Linux 2023上で、node26/node24/node22/python3.13 runtimeと sudo アクセス付きで実行する。シークレット漏洩問題へのセールスポイント:sandbox内のコードはインフラから分離されているため、プロジェクトの環境変数、データベース接続、またはクラウドリソースにアクセスできない——その分離が製品であり、有効にすることを覚えておく必要のある設定ではない。JSまたはPython SDK、またはCLIで操作する。永続性(自動保存と再開)はGAリリース以降デフォルトで有効だ。
E2B はもう1つの明らかな選択肢で、AIが生成したコードの実行に特化して狙っている。Apache-2.0のオープンソースで、PythonとJavaScriptのSDKがあり、DockerfileでmicroコンテナではなくmicroVMテンプレートをビルドし——気に入っている点——AWS、GCP、Azure、または自分のLinuxマシンでTerraform経由でセルフホスト可能だ。彼らのクラウドで始めて、後で書き直しなしにオンプレミスに移行できる。
トレードオフは通常のものだ:レイテンシ、実行あたりのコスト、AgentとコードのネットワークラウンドトリップO。ユーザー提出やAgent生成のスニペットを実行するツールに対しては、クリーンなカーネル境界と自動シークレット分離のために払う合理的な価格だ。自分のリポジトリを一日中編集するには過剰であり、摩擦が鬱陶しくなる。
実際に使っている構成
重要な2つのケースにマッピングした具体的な設定を以下に示す。
自分のコード、日常業務向け: 本物のマシンでClaude Codeを default または plan モードで、OSのsandboxを有効にして使う。絶対に外せない設定は、credentialディレクトリの読み取りを拒否してネットワークallowlistを短く保つことだ:
{
"sandbox": {
"enabled": true,
"filesystem": { "denyRead": ["~/.aws", "~/.ssh", "~/.config/gh"] },
"allowedDomains": ["registry.npmjs.org", "github.com"]
}
}
シークレットはAgentの作業ディレクトリが読み取れない .env に置き、サブプロセスからプロバイダーのcredentialを除去する(CLAUDE_CODE_SUBPROCESS_ENV_SCRUB)ことで、ビルドスクリプトの迷い込んだ curl が環境からAPIキーを取得できないようにしている。これはハードニングであり、壁ではない——そう扱っている。
自分で書いていないもの全て——監査中のリポジトリ、生成されたコード、信頼していない依存関係、夜間に無人で動くAgent——はmicroVMに入れる。実際にはホステッドsandbox(Vercel SandboxまたはE2B)を使い、カーネル境界とシークレット分離が無料でついてくるようにするか、非rootとしてAgentを動かすdevコンテナ内で、価値あるものが何もない箱の中で bypassPermissions を使う。私が従うルール:境界の強度はコードに対する信頼の程度に合わせるべきであり、境界の便利さに合わせるべきではない。
最初に犯したミスは、これを一つの決断として扱うこと——「sandboxingはオンかオフか」——だった。実は2つの決断だ。マシン上の信頼できるコードは事故を止める安価なガードを望む。信頼できないコードはモデルが言いくるめて通過できない壁を望む。信頼に合った段を選び、どちらにせよシークレットをdeny-readし、すべてのコマンドを見守ることなく動かし続けることができる——それがAgentを雇う意味の全てだった。
