Claude Codeをsandbox-runtime(srt)で起動する設定と既定の遮断範囲
sandbox-runtime(srt)でClaude Code全体を包む手順。~/.srt-settings.jsonの書き方、既定で遮断される範囲、Linuxで守れない範囲を解説します。
Claude Codeをsandbox-runtime(srt)で起動する設定と既定の遮断範囲
sandbox-runtime(コマンド名はsrt)は、Claude Codeのプロセス全体をSeatbeltまたはbubblewrapの隔離で包むnpmパッケージです。~/.srt-settings.jsonを先に用意し、npx @anthropic-ai/sandbox-runtime claudeで起動します。設定が無いままでも起動はできますが、ネットワークは遮断され、書き込みも限られた場所にしか通りません。この記事では、必要な設定、起動手順、既定で遮断される範囲、Linuxで守れない範囲を順に説明します。
sandbox-runtimeは組み込みsandboxと何が違うのか
Claude Codeには/sandboxで有効にする組み込みのBashサンドボックスがあります。こちらが制限するのはBash、PowerShell、Monitorのコマンドとその子プロセスです。Read・Edit・WebFetchなどの組み込みツールは、Claude Codeのプロセス内で動くため対象外です。MCPサーバーとコマンドフックも、ホスト上で制約なしに動きます。
sandbox-runtimeは、同じ隔離の仕組みでClaude Code本体を丸ごと包みます。ファイルツール、MCPサーバー、フックまで同じ境界の内側に入る点が違いです。
| 方式 | 隔離される範囲 | Dockerの要否 |
|---|---|---|
| 組み込みのBashサンドボックス | 隔離される範囲Bash・PowerShell・Monitorのコマンドと子プロセス | Dockerの要否不要 |
| sandbox-runtime | 隔離される範囲ファイルツール・MCPサーバー・フックを含むプロセス全体 | Dockerの要否不要 |
| Dev container | 隔離される範囲開発環境全体 | Dockerの要否必要 |
組み込み側の設定はsandbox.enabledとサンドボックス設計の解説にまとめてあります。Dockerで囲う方法はDevContainerの実装記事が扱っています。各方式の比較はAIコーディングエージェントのサンドボックス実装比較にあります。
公式は、--dangerously-skip-permissionsで無人運転するときにこの方式を選択肢の一つに挙げています。権限プロンプトが無い状態では、選んだ隔離境界が唯一の防御になるためです。この場合はrootでなく非rootユーザーで動かします。LinuxとmacOSでは、rootのまま同フラグを付けるとClaude Codeが起動を拒否します。srt自体をroot(euid 0)で動かす場合は、capabilityのバウンディングセットにCAP_SETFCAPが必要です。無いと、サンドボックス内のコマンドはすべてuidマップの書き込みでOperation not permittedになります。非rootユーザーなら、この条件は関係しません。
v2.1.289のclaude --helpでは、このフラグの説明は「Bypass all permission checks.」で始まります。続きは「Recommended only for sandboxes with no internet access.」です。同じ--helpには--settings <file-or-json>もありますが、これはClaude Code自身の設定です。srtの設定ファイルとは別物なので、混同しないでください。
sandbox-runtimeはベータのリサーチプレビューです。公式は設定形式が今後変わりうると明記しています。
起動前に入れておく依存パッケージ
LinuxとWSL2では、組み込みsandboxと同じbubblewrapとsocatに加え、ripgrepが要ります。Claude Codeはripgrepを同梱していますが、単体のruntimeはPATHから探します。ディストリビューションのパッケージマネージャーで入れてください。
# Ubuntu / Debian の例
sudo apt-get install bubblewrap socat ripgrepmacOSでは、Claude Codeの公式ドキュメントは追加パッケージ不要としています。一方、sandbox-runtimeのREADMEはmacOSの依存にripgrep(brew install ripgrep)を挙げています。禁止パスの検出に使うためです。入れ忘れないよう、macOSでもripgrepを用意しておくと確実です。
Ubuntu 24.04以降では、kernel.apparmor_restrict_unprivileged_usernsが既定で有効です。この設定はネームスペースから権限を剥がすため、READMEは無効化するか、AppArmorのプロファイルで許可するよう案内しています。同じ症状の原因はunshare(CLONE_NEWUSER)の失敗記事でも扱っています。
~/.srt-settings.jsonに何を書くか
設定ファイルの置き場所は~/.srt-settings.jsonです。別の場所に置くなら--settingsで渡します。既定では、ネットワークは全拒否で、書き込みは組み込みのわずかなパスに限られます。Claude Codeを動かすなら、次の許可が最低限要ります。
書き込みを許可する場所:
- プロジェクトのディレクトリ
- Claude Codeの設定パスである
~/.claudeと~/.claude.json - Claude Codeが実行時ファイルを書くディレクトリ(Linux・WSL2は
/tmp、macOSは/private/tmp)
通信を許可するドメイン:
api.anthropic.com(サードパーティのプロバイダーを使うなら、そのエンドポイント)claude.aiとplatform.claude.com(OAuthのサインインとトークン更新に使う)
サードパーティのプロバイダーでも、api.anthropic.comは残します。WebFetchのドメイン安全確認が、既定ではこのドメインを呼ぶためです。skipWebFetchPreflight: trueを設定すれば省けます。APIキー認証で動かすなら、claude.aiとplatform.claude.comは外せます。
実行時ファイルの置き場はCLAUDE_CODE_TMPDIRを設定していれば、そのディレクトリになります。macOSでは/tmpが/private/tmpへのシンボリックリンクで、Seatbeltは解決後のパスで判定します。次の例の/tmpは、macOSなら/private/tmpに読み替えてください。
最小構成の例を示します(設定形式はREADMEの例に準じた形で、プロジェクトの場所は.としています)。
{
"network": {
"allowedDomains": [
"api.anthropic.com",
"claude.ai",
"platform.claude.com"
]
},
"filesystem": {
"allowWrite": [".", "~/.claude", "~/.claude.json", "/tmp"]
}
}社内のパッケージレジストリやGitHubを使う作業では、allowedDomainsにそれらのドメインを足します。*.github.comのようなワイルドカードと、api.example.com:443のようなポート指定が使えます。deniedDomainsは許可リストより先に評価され、優先されます。
allowWriteとdenyWriteの関係は、通常の直感と逆になる場面があります。書き込みはdenyWriteがallowWriteに勝ちます。読み取りは逆で、allowReadがdenyReadに勝ちます。書き込みの調整方法はallowWrite/denyWriteの記事、読ませたくない認証情報の隠し方は認証情報ファイルのmask設定が参考になります。
初回の起動手順とLinuxで先に作っておくもの
LinuxとWSL2のruntimeは、書き込み許可を「すでに存在するパス」にだけ適用します。まっさらな環境では、初回起動の前に設定パスを作っておく必要があります。
mkdir -p ~/.claude && \
{ [ -f ~/.claude.json ] || echo '{}' > ~/.claude.json; }設定ファイルとパスが揃ったら、npxでClaude Codeを包んで起動します。
npx @anthropic-ai/sandbox-runtime claudeClaude Codeは、設定した境界の内側で立ち上がります。同じ形式のコマンドで、単体のMCPサーバーやほかの補助プロセスも包めます。READMEにはnpm install -g @anthropic-ai/sandbox-runtimeで入れるsrtコマンドの例もあり、srt --settings /path/to/srt-settings.json <コマンド>の形でも使えます。
設定が読めなかったときの挙動
挙動は、設定ファイルの渡し方で分かれます。
設定ファイルの状態と起動の可否
ファイルが無い
~/.srt-settings.jsonが無く--settingsも渡さないと、そのまま起動します。ネットワークは遮断され、書き込めるのは/tmp/claude・~/.npm/_logs・~/.claude/debugなどの組み込みパスだけです。
空・読めない・不正
~/.srt-settings.jsonでも--settingsで渡したファイルでも、空・読み取り不可・検証失敗なら起動しません。--settingsのファイルが存在しない場合も同じです。
公式は「クリーンに起動したことを、設定が読み込まれた証拠と考えない」と注意しています。~/.srt-settings.jsonが無いときは、エラーが出ないまま既定に落ちるからです。この場合Claude Codeは立ち上がっても、API通信が遮断されて何もできない状態になります。
READMEは、存在するのに読めない設定を既定値で補わない理由を述べています。既定値は「弱い設定」ではなく「別の設定」で、戻すとファイルに書いたdenyReadや認証情報のルールまで落ちてしまうためです。
組み込みの書き込み先のうち~/.npm/_logsと~/.claude/debugは、denyReadがそのパスを覆うと対象から外れます。home配下を読み取り禁止にするなら、この2つはallowWriteに自分で書きます。
設定の適用を確実にしたいなら、--settingsで明示するのが安全です。読み込みに失敗すれば起動しないので、静かに既定へ落ちる事故を避けられます。srt側の--settingsは、包むコマンドの前に置きます。
設定が効いているかを起動後に確かめる
- 1
srtに設定ファイルを渡して起動する
srt --settings ~/srt-settings.json claudeの形で起動します。 - 2
許可していないドメインへ通信させる
Claude Codeに
curlを実行させます。READMEには、ポリシーによる拒否はHTTPとCONNECTで403になり、X-Proxy-Error: blocked-by-sandbox-runtimeヘッダーが付くと書かれています。srt --debug curl https://example.comのように--debugを付けると、拒否の様子を詳しく追えます。 - 3
禁止パスへ書き込ませる
allowWrite: ["."]でも、.bashrcへの書き込みはREADMEの例どおりOperation not permittedで失敗します。.git/hooks/pre-commitも同様に書けません。 - 4
拒否の記録を見る
macOSは
log stream --predicate 'process == "sandbox-exec"' --style syslogで違反をリアルタイムに追えます。Linuxのbubblewrapには違反の報告機能がないため、READMEはstrace -f srt <コマンド> 2>&1 | grep EPERMを案内しています。
何も設定しなくても遮断される書き込み先
runtimeは、リスクの高い書き込みを設定なしでも止めます。プロジェクトのルートでは、次の場所が対象です。
.git/hooks.git/config(filesystem.allowGitConfig: trueを設定すると許可).mcp.json.claude/commandsと.claude/agents- シェルの起動ファイル
READMEの一覧には、.bashrc、.zshrc、.gitconfig、.vscode/、.idea/なども入っています。allowWrite: ["."]と書いていても、これらへの書き込みは失敗します。denyWriteがallowWriteに優先するためです。
ただし、公式は「許可した書き込みパスには、Claude Codeが設定を読み込むほかの場所も含まれる」と警告しています。セッションがそこへ書けると、次にClaude Codeを起動したとき、サンドボックス外で動くフック、権限ルール、MCPサーバーを仕込めます。こうしたパスは、denyWriteで自分で塞ぐ必要があります。Claude Codeのドキュメントのsandbox runtimeの説明には、塞ぐべき具体的なパスは挙がっていません。~/.claudeを許可している場合は、その配下の設定ファイルをdenyWriteへ入れるかを検討します。例は~/.claude/settings.jsonです。Claude Code側の書き込みが必要な機能と衝突しないか、実際に確かめてください。
Linuxではあとからできるネストしたリポジトリをruntimeは守れない
macOSとLinuxで、遮断の効き方が違います。
禁止パスの判定タイミング
macOS
起動後に作られたネストしたファイルやリポジトリも対象になります。
Linux・WSL2
起動後に作られたネストしたファイルやリポジトリは対象になりません。
Linux・WSL2では、起動時に禁止リストを作ります。プロジェクトのルートは確実に守り、起動時点で存在するネストしたコピーは浅く探して、ベストエフォートで守ります。git init、git clone、雛形生成が典型で、これらで作った.git/hooksは保護されません。
「浅く探す」深さはmandatoryDenySearchDepthで調整できます。READMEによる仕様は次のとおりです。
- 既定値は3で、指定できる範囲は1から10
- 大きくすると保護は強まるが、起動が遅くなる
- プロジェクトのルート直下(深さ0)は、この値によらず常に保護される
{
"mandatoryDenySearchDepth": 5,
"filesystem": {
"allowWrite": [".", "~/.claude", "~/.claude.json", "/tmp"]
}
}この値を上げても、起動後に作られたリポジトリは守れません。上げて効くのは、起動時にすでにある深めのネストだけです。なお、READMEには「存在しない禁止パスもLinuxでは/dev/nullのマウントなどで塞ぐ」という記述もあります。それでも公式のClaude Code側の説明は、セッション中に作られるものを対象外としています。
LinuxとmacOSで、パス指定の効き方が違う箇所
同じ設定ファイルでも、書き方によってLinuxでは黙って効かない項目があります。READMEが挙げる違いは次のとおりです。
allowWriteとdenyWriteは、Linuxでは文字どおりのパスだけを受け付けます。末尾の/**は外されて(src/**はsrcの意味)、それ以外のglobは読み飛ばされます。macOSではsrc/**/*.tsのようなgitスタイルのglobが使えますdenyReadとallowReadのglobは、Linuxではコマンドを包んだ時点で存在するパスに展開されます。あとから作られたファイルは対象外です。先頭のディレクトリ部分にワイルドカードがある/**/*.pemのような指定は、Linuxでは読み飛ばされます- Linuxで
denyReadが覆ったディレクトリは、中身が空で書き込み可能なtmpfsとして見えます。そこへの書き込みは通りますが、コマンドが終わると消えます - 保護対象パスの親ディレクトリは、Linuxでは
mvやrmdirで動かせません。EBUSY(Device or resource busy)で失敗します allowUnixSocketsはLinuxでは無視されます。seccompがパスで絞れないためです。ソケットをまとめて許可するならallowAllUnixSockets: trueを使います。Unixソケットを扱うseccompのビルド済みバイナリはx64とarm64だけが対応で、ほかのアーキテクチャーではallowAllUnixSockets: trueを設定します
~/.claude配下の設定ファイルをdenyWriteで塞ぐときは、~/.claude/*.jsonのようなglobでなく、ファイルを1つずつ書きます。Linuxでglobを書くと、その指定は読み飛ばされます。
読み取りを絞るレシピとして、READMEには"denyRead": ["/Users"], "allowRead": ["."]の形(macOS)があります。Linuxでは/homeを禁止して、現在のディレクトリを許可し直します。allowReadがdenyReadに勝つ順序を利用しています。
Windowsのalpha対応
READMEには、Windows向けのalpha版の節があります。Claude Codeの公式ドキュメントのruntime手順は、依存パッケージをLinux・WSL2・macOSについてだけ挙げています。
Windows版は、マシンごとに一度npx @anthropic-ai/sandbox-runtime windows-installを実行します。UACの確認が1回出ます。包まれたコマンドは、呼び出したユーザーではなく専用のローカルユーザーsrt-sandboxとして動きます。そのため、nvmやScoopでユーザー単位に入れたツールは開けません。マシン全体への導入に切り替えるか、プロファイルのパスをfilesystem.allowReadへ足します。
無人運転のあとに見直すもの
公式は、無人運転のあとに「書き込み可能にしたパス」を見直すよう求めています。Linux・WSL2ではさらに、セッションが作ったものも見直しの対象です。ネストしたリポジトリの.git/hooksや.git/configが、その典型です。
次の点は、隔離の限界として押さえておきます。
- プロセスはホストのカーネルを共有するため、カーネルの脆弱性による脱出は理論上ありえる。カーネル水準の分離が要るなら、gVisorや別のVMを選ぶ
- プロキシはクライアントが名乗るホスト名でドメインを判定し、暗号化された通信の中身は検査しない。ドメインフロンティングのような手法で、許可リスト外へ出られる可能性がある
- Dockerの中ではbubblewrapが動かないことがあり、
enableWeakerNestedSandboxで動かせる。READMEはこの設定が隔離を大きく弱めると警告している - ネットワークを許可したままでは、エージェントが読めるデータは外へ出せる。プロジェクトを書き込み可能にしていれば、そのコードも書き換えられる
境界を強くしたいなら、Dev containerや専用のVMのほうが向きます。信頼できないリポジトリを扱うなら、公式は専用のVMかクラウドセッションを勧めています。sandbox-runtimeは「Dockerなしで、MCPサーバーとフックまで含めて包みたい」ときの選択肢です。