Linux での Claude Code のサンドボックス: srt が遮断するもの、bypassPermissions が開けるもの
組み込みのサンドボックスはシェルコマンドを囲む本物の OS 境界ですが、囲むのはシェルコマンドだけです。その強さは、セットアップ画面に表示されない 2 つの要素で決まります。権限モードと、コマンドの開始時点で何が存在していたかです。srt が何をブロックし何を通すかを Ubuntu で試した結果、いまも Linux でサンドボックスを壊すバグ、そしてコンテナや VM のほうが合う場面をまとめます。
目次
Anthropic の sandbox-runtime は新しいものではありません。リポジトリは 2025年10月20日に作成され、バージョン 0.0.81 は 2026年10月20日にリリースされました。Claude Code の組み込みサンドボックスの下で動いているエンジンで、両者はひとつの境界を持つひとつのものとして語られがちです。実際はそうではなく、その境界も「サンドボックス」という言葉から想像するほど固定されたものではありません。
Ubuntu 24.04 のマシンで両方を動かしました。srt 0.0.81 は手動で、Claude Code 2.1.310 はサンドボックスを有効にしてヘッドレスで実行しています。この記事の軸になる結果は 2 つです。bypassPermissions モードでは、サンドボックスのネットワーク側が、キーをもうひとつ設定するまで許可リストにないホストをそのまま通しました。そして Linux では、保護対象パスのリストがコマンドの開始時に作られるので、そのコマンドの中で作られたリポジトリはコマンドが終わるまで保護されません。どちらも同じことを示しています。手に入るサンドボックスは /sandbox パネルに表示されない条件で決まるので、信頼できる確認方法は、実際に使うモードでプローブを走らせることだけだと考えています。
Claude Code のサンドボックスは実際に何を遮断するのか
シェルコマンドと、そこから起動されるプロセスです。遮断する側面はファイルとネットワークの 2 つです。サンドボックスのドキュメントによると、サンドボックス内のコマンドが書き込めるのは作業ディレクトリ、ユーザーごとの一時ディレクトリ、追加したディレクトリだけで、それ以外の場所には書き込めません。ネットワークへの直接の経路はなく、接続はマシン上のプロキシを経由し、プロキシが各ホスト名を許可リストと照合します。許可リストは最初は空です。サンドボックスはデフォルトでオフです。プロジェクト単位なら /sandbox で、すべてのプロジェクトなら ~/.claude/settings.json の sandbox.enabled で有効にします。
デフォルトで開いたままのものも同じくらい重要です。読み取りはマシンのほぼ全体で許可されていて、~/.ssh や ~/.aws/credentials も含まれます。環境変数は Claude Code から継承され、シークレットもそのまま引き継がれます。どちらも filesystem.denyRead と credentials ブロックで絞り込めますが、対象になるのはリストに書いたものだけです。ドキュメントには、組み込みの認証情報拒否リストはないと書かれています。
そして、サンドボックスがまったく関知しないものがあります。Read、Edit、Write、WebFetch、WebSearch はサンドボックスではなく権限ルールに従うので、denyRead のエントリは Read ツールを止めず、allowedDomains は WebFetch を制限しません。フック、ローカルの MCP サーバー、LSP サーバー、プラグインのモニター、ステータスラインのコマンド、apiKeyHelper は、ユーザーのアクセス権をすべて持ったまま動きます。mod が起動するものも同じです。
Claude Code のサンドボックスと sandbox-runtime (srt) はどう違うのか
エンジンは同じで、範囲が違います。Claude Code は @anthropic-ai/ パッケージの上にサンドボックスを構築し、Bash、PowerShell、Monitor のコマンドをひとつずつ新しいサンドボックスで包みます。このパッケージには任意のプロセスを包む CLI、srt も付いています。包む対象は 1 つのコマンドでも、ローカルの MCP サーバーでも、Claude Code そのものでもよく、最後の場合はファイルツール、フック、MCP サーバーも境界の内側に入ります。README はいまも、設定フォーマットが変わる可能性のあるベータのリサーチプレビューと位置づけています。
Linux での境界は bubblewrap です。サンドボックス内のプロセスには、書き込んではいけないすべての場所に読み取り専用のバインドがかかり、外への経路がないネットワーク名前空間が与えられます。ホスト側では HTTP 用と SOCKS5 用の 2 つのプロキシが動き、socat が中継する Unix ソケット経由でサンドボックスとつながります。同梱の seccomp ヘルパーは新しい Unix ソケットの作成をブロックし、コマンドが独自のユーザー名前空間を作るためのシステムコールもブロックします。ユーザー名前空間の中では、読み取り専用のバインドをアンマウントできてしまうからです。macOS では、同じポリシーが生成された Seatbelt プロファイルになります。ネイティブの Windows では、srt に専用のローカルアカウントでコマンドを実行するアルファ版があります。Claude Code 自体は、WSL2 に移さない限り、Windows ではコマンドをサンドボックスなしで実行します。
bypassPermissions は Claude Code のサンドボックスのネットワークの壁に何をするのか
ネットワークについての判断を代わりに下してしまい、その答えは「許可」です。サンドボックス内のコマンドが許可リスト外のホストに接続しようとすると、Claude Code は判断を権限モードに委ねます。手動モードと acceptEdits は確認を求め、dontAsk は拒否します。auto モードは、Claude が事前にそのコマンドに対してホストを列挙し、分類器がそのリストを承認した場合を除いて拒否します。bypassPermissions では、ドキュメントによると、接続は確認なしで許可されます。サンドボックスを有効にし、サンドボックスなしの再試行をオフにし、許可ドメインを一切設定しない状態で、同じ example.com への curl を 4 つのヘッドレスセッションで実行しました。
| 権限モード | strictAllowlist | 結果 |
|---|---|---|
| default | オフ | ブロック: deny network-outbound example.com:443 (user denied) |
| default | オン | ブロック: host is not on the allow list |
bypassPermissions | オフ | 200 |
bypassPermissions | オン | ブロック: host is not on the allow list |
1 行目には注意点があります。ヘッドレスの claude -p セッションには確認に答える人がいないので、「user denied」は確認がフェイルクローズした結果です。対話セッションなら確認を求められていたはずです。auto モードは試していません。
確かめたかったのは 3 行目です。同じモードで touch ~/sandbox-probe は相変わらず Read-only file system で失敗したので、再試行をオフにした状態では、ファイルシステム側は持ちこたえ、ネットワーク側は許可リストにないホストを通したことになります。再試行をデフォルトのままにした場合について、ドキュメントは、失敗したコマンドのサンドボックスなしの再試行も bypassPermissions では確認なしで実行されると補足しています。そうなると、そのコマンドについてはどちらの側も守られません。ネットワーク側を閉じるキーが strictAllowlist (v2.1.219 以降) です。どのモードでも許可リスト外のホストを拒否し、リポジトリの .claude/settings.json に設定した場合は効果がありません。残りを固めるキーがあと 2 つあります。
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"network": { "strictAllowlist": true, "allowedDomains": ["github.com", "registry.npmjs.org"] }
}
}allowUnsandboxedCommands: false にすると、Claude Code は再試行を無視します。failIfUnavailable は、サンドボックスを実行できないときに起動の時点で止めます。デフォルトでは、サンドボックスなしでコマンドを実行してしまいます。この設定をどこに置くかが重要です。--settings で渡すか管理設定として配布した場合、再試行をオフにするとサンドボックスは管理者必須になり、Claude Code はリポジトリの excludedCommands やその他の制限を緩めるエントリを無視します。通常のユーザー設定ではそうならず、リポジトリの excludedCommands は該当するコマンドを引き続きサンドボックスの外に出せます。どちらの場合も、何が除外されているかを確認してください。
固めたあとでも、許可リストはホスト名で判定し、TLS の中身は検査しません。ドキュメントは、github.com のような広いエントリが情報持ち出しの経路になりうること、ドメインフロンティングでリスト外のホストに到達できる可能性があることを警告しています。追加するドメインはどれもひとつの通信路です。これは、サンドボックスから抜け出すあらゆる経路で扱った棚卸しの問題を、1 台のマシンに縮めたものです。
Linux で sandbox-runtime (srt) をどう動かすのか
bubblewrap、socat、ripgrep、パッケージ用の Node、そして設定ファイルが必要です。まずカーネル側を確認します。
bwrap --version
sysctl kernel.apparmor_restrict_unprivileged_usernssysctl が 1 を表示する場合 (Ubuntu 24.04 以降のデフォルトの AppArmor ポリシーです)、bubblewrap はユーザー名前空間を得られても、必要なケーパビリティは得られません。サンドボックスのドキュメントは /usr/bin/bwrap 用の AppArmor プロファイルを示し、README はもっと大雑把な対処として sysctl を 0 にする方法を示しています。テストマシンでは最初から 0 でした。
root 権限があれば、依存関係はそれぞれ 1 行で入ります。
sudo apt-get install bubblewrap socat ripgrep
npm install -g @anthropic-ai/sandbox-runtimeテストマシンには bubblewrap はありましたが、socat と ripgrep はありませんでした。スタンドアロンのランタイムはこの 2 つを PATH から探します。root がなければ、Debian パッケージを自分が所有するディレクトリに展開できます。そのディレクトリとパッケージは、書き込みを許可するどのパスの外にも置いてください。README は、書き込み可能な node_modules の中のコピーはサンドボックスの内側から書き換えられ、次回はサンドボックスなしで実行されうると警告しています。パス上にある書き込み可能な rg や socat も同じです。
mkdir -p ~/opt/srt-deps && cd ~/opt/srt-deps
apt-get download socat ripgrep
for f in *.deb; do dpkg-deb -x "$f" root; done
npm install --prefix ~/opt/srt @anthropic-ai/sandbox-runtime@0.0.81
export PATH="$HOME/opt/srt-deps/root/usr/bin:$HOME/opt/srt/node_modules/.bin:$PATH"そのうえで、別のスクラッチディレクトリで作業します。"allowWrite": ["."] が開放するのはこのディレクトリです。設定ファイルはここか ~/.srt-settings.json に置きます。ネットワークと認証情報のテストに使ったファイルは次のとおりです。
mkdir -p ~/srt-lab && cd ~/srt-lab && git init -q
cat > srt-test.json <<'EOF'
{
"network": { "allowedDomains": ["example.com"], "deniedDomains": [] },
"filesystem": { "denyRead": ["~/.ssh"], "allowWrite": ["."], "denyWrite": [] },
"credentials": { "envVars": [{ "name": "DEMO_TOKEN", "mode": "deny" }] }
}
EOFREADME 自身の例にある落とし穴をひとつ。コマンドを引用符で囲んだ srt "curl example.com" は、試したところ command not found で失敗しました。0.0.81 が文字列全体をプログラム名として扱ったためです。コマンド文字列は -c のあとに渡します。
srt --settings ./srt-test.json -c 'curl -sS -o /dev/null -w "%{http_code}\n" https://example.com'Ubuntu で sandbox-runtime は何をブロックし、何を通したのか
ポリシーの外を狙った書き込みと接続はすべて失敗し、デフォルト設定は、ドキュメントが通すと書いているものをそのとおりに通しました。各行は、スクラッチディレクトリからの srt -c 1 回分の実行です。
| プローブ | srt 0.0.81 での結果 |
|---|---|
touch ~/sandbox-probe | Read-only file system |
| 作業ディレクトリにファイルを書き込む | 許可 |
作業ディレクトリの .bashrc に追記する | Permission denied |
.git/hooks/pre-commit に書き込む | Read-only file system |
/tmp に書き込む | Read-only file system |
$TMPDIR (/tmp/claude) に書き込む | ホスト上にディレクトリが存在するまでは No such file or directory、存在したあとは許可 |
denyRead なしで ~/.ssh を一覧表示する | 許可、15 件 |
denyRead ありで ~/.ssh を一覧表示する | 許可、0 件 |
許可リストが空の状態でホストに curl する | CONNECT tunnel failed, response 403 |
許可したホストに curl する | 200 |
許可したホストに curl --noproxy '*' する | Could not resolve host |
1.1.1.1:53 に UDP パケットを送る | Network is unreachable |
ホストの 127.0.0.1 で動くサーバーに curl する | Couldn't connect to server |
サンドボックス内でサーバーを起動して curl する | 200 |
| D-Bus のシステムソケットに接続する | Operation not permitted |
unshare -U true | Operation not permitted |
DEMO_TOKEN (リストに記載) と OTHER_TOKEN (記載なし) | DEMO_TOKEN は未設定、OTHER_TOKEN は見える |
読み取りを拒否したディレクトリは、開けずにエラーになるのではなく、空に見えます。bubblewrap がその上に空の tmpfs をマウントするので、鍵ファイルを探すツールはエラーではなく「何もない」という結果を受け取ります。トークンの行は、認証情報ルールの縮図です。リストに書き忘れたものは隠されません。一時ディレクトリの行は、環境のページにある記述と一致します。Linux では、書き込みの許可はすでに存在するパスにしか適用されません。
localhost の 2 行は、ドキュメントが説明している Linux での挙動です。サンドボックスは独自のループバックを持つので、ホスト上の開発サーバーやデータベースへの直接接続は失敗し、Linux では allowLocalBinding は何の効果もありません。プロキシ経由なら、ホストのサービスには許可リストに載せた場合にだけ到達できます。載せ方は localhost か、127.0.0.1:3000 のようなアドレスとポートです。ループバックアドレスに解決されるそれ以外のホスト名は拒否されます。サンドボックス内では、env で配管も見えました。HTTP_PROXY、ALL_PROXY とその仲間、git の SSH をプロキシ経由でトンネルする GIT_SSH_COMMAND、そしてプロキシ変数を無視する JVM ツール向けの JAVA_TOOL_OPTIONS エージェントです。
Linux で sandbox-runtime の拒否リストはどこで漏れるのか
実行中のサンドボックスの中で現れる保護対象パスです。環境のページははっきり書いています。Linux ではランタイムが起動時に一度だけ拒否リストを作り、プロジェクトルートをカバーし、すでに存在する入れ子のコピーを浅くスキャンし、セッションがあとから作るものはカバーしません。1 回の実行で確認できます。
srt --settings ./srt-test.json -c 'git init -q sub && echo "echo hi" > sub/.git/hooks/pre-commit && echo wrote-hook'
# wrote-hook
srt --settings ./srt-test.json -c 'echo "echo hi" > sub/.git/hooks/post-commit'
# /usr/bin/bash: line 1: sub/.git/hooks/post-commit: Read-only file system最初のコマンドの中で作られたリポジトリには、フックが書き込めました。次の srt 実行は新しい拒否リストを作り、そのリポジトリを見つけて保護しました。1 つのコマンドの中で作ったサブディレクトリの .bashrc も、同じように書き込めました。macOS では拒否が書き込みのたびにチェックされるので、この穴は Linux だけのものです。穴の大きさは、サンドボックスが何を包んでいるかで決まります。Claude Code の組み込みサンドボックスはコマンドごとに新しいサンドボックスを起動するので、穴が開いているのは 1 コマンドの間だけです。srt claude はセッション全体を 1 つのサンドボックスで包むので、セッションが clone したりひな形から生成したりしたリポジトリは、再起動するまでリストの外に置かれたままです。
同じ仕組みは、あとに何かを残します。まだ存在しないファイルを保護するために、Linux のサンドボックスはコマンドの実行中、そのパスにプレースホルダーを置きます。空のディレクトリで srt の下で 4 秒間 sleep したところ、.bash_profile や .bashrc から .mcp.json、.vscode、.idea まで 12 個が作られ、その間ずっとホスト側から見えていました。正常に終了すると消えました。kill -9 のあとは残りました。中身は空で、読み取り専用、所有者は実行した自分のユーザーです。サンドボックスのドキュメントは .claude の設定パスに残る同じ残骸について書いていて、見つけるには claude doctor を使うよう案内しています。プロジェクトディレクトリなら単なるゴミです。ホームディレクトリを作業ディレクトリにしている場合、空の ~/.bash_profile は実害のある変更です。ログイン bash はそれを読み、~/.profile を読まなくなるからです。
Claude Code のサンドボックスが一部の Linux マシンで失敗するのはなぜか
未解決のリグレッションが 1 つあります。Issue 43454 です。Claude Code 2.1.92 が apply-seccomp ヘルパーを同梱して以来、ユーザー名前空間がケーパビリティなしで与えられるシステムでは、サンドボックス化されたすべての Bash コマンドがセットアップ中に失敗することがあります。新しい Ubuntu のデフォルトの AppArmor ポリシーもこれに該当します。最初の報告は次のとおりです。
apply-seccomp: write /proc/self/setgroups (nested userns is capability-restricted; caller must provide CAP_SYS_ADMIN): Permission denied
regression と reproduced のラベルが付いています。8 月のメンテナーのコメントは 2.1.233 で問題を確認し、回避策と注意点を挙げています。回避策は kernel. の設定 (システムのハードニングは弱まります)、注意点は、ドキュメントの AppArmor プロファイルは bubblewrap しかカバーしないので、それだけでは足りない可能性があることです。セットアップの節と合わせると、ここが罠です。標準の Ubuntu 24.04 以降では、ドキュメントの対処で bubblewrap は動くようになっても、ヘルパーは失敗したままになりえます。あるコメント投稿者の原因分析は、bubblewrap がすでに書き込みを不可能にした名前空間の中でヘルパーが setgroups に書き込もうとすることが失敗の原因だとし、sysctl が 0 でも失敗すると報告しています。この issue は 2026年10月24日の時点でまだオープンでした。テストマシンでは再現せず、1 台だけでは理由はわからないので、ここで問題なく動いたことは、あなたのマシンについてはほとんど何も教えてくれません。
その周辺のほかの不満は、ほとんどがドキュメントどおりの挙動です。FOO=bar npm test のように先頭に変数を付けたコマンドは、auto-allow モードでは自動承認されません。issue 50509 はこれをバグとして報告しましたが、いまのドキュメントは意図した挙動だと説明し、代入を含めた allow ルールという対処を示しています。docker はサンドボックス内では動きません。素の ssh とほとんどのデータベースドライバはプロキシ変数を無視するので、許可したホストにも接続できません。一方、SSH 経由の git は、ホストを許可すれば Linux で動きます。Chromium 自身のサンドボックスと rootless の Podman は、ユーザー名前空間を必要とするので失敗します。トラブルシューティングの節にある対処はどれも、コマンドをサンドボックスの外に出すか制限を外すもので、どちらなのかが明記されています。
Claude Code のサンドボックスより VM やコンテナのほうがよいのはどんなときか
閉じ込める対象がシェルコマンドだけではないときで、無人実行はすべてこれに当たります。環境のページは、完全な無人作業には Bash サンドボックスだけでは不十分で、--dangerously-skip-permissions のセッションは必ずコンテナ、VM、または sandbox runtime の中で実行すべきだと書いています。auto モードの分類器についても、隔離境界ではなくアクション単位の制御だとしています。これは記事「あなたの承認ゲートは、いまや推測です」の主張そのものです。無人実行の限界は、ハーネスが与える到達範囲のほうに設けるべきです。
選択肢は、範囲とセットアップの手間のトレードオフです。組み込みサンドボックスは、人が見ている作業で確認の回数を減らすためのものです。srt claude は Docker なしでプロセス全体を 1 つの bubblewrap 境界に入れます。ドキュメントによると、プロジェクト、~/.claude、~/.claude.json、/tmp への書き込み権限、api.anthropic.com へのネットワークアクセス、OAuth を使うなら claude.ai と platform.claude.com へのアクセスが必要で、さらにそれらの許可で露出する設定パスに denyWrite をかける必要があります。かけないと、次回サンドボックスなしで動くフックや MCP サーバーをセッションが残せてしまいます。デフォルト拒否のファイアウォールを備えたリファレンス実装のような dev container は、同じことを Docker で行います。その強さは、何をマウントするか、どの認証情報を中に置くか、ファイアウォールが何を許可するかで決まります。VM は独自のカーネルを加えます。ドキュメントは、信頼できないリポジトリには VM かクラウドセッションを推奨しています。
線をどこで引くかについての見方はこうです。組み込みサンドボックスは、誰かが見ているコマンドを囲む壁としてはよいものです。誰も見ていなくなったら、境界はプロセス全体とそのネットワークを囲むべきです。そしていちばん重要な確認は、セットアップ画面がけっして表示しないもの、つまりリストにないホストを権限モードがどう扱うかです。普段使っているモードで、再試行をオフにした状態で Claude にプローブを実行させ、サンドボックスなしで再実行しようという提案はすべて断ってください。書き込みの壁には touch ~/sandbox-probe、直接の経路には任意のサイトへの curl --noproxy '*'、許可リストには一度も許可していないホストへの素の curl を使います。パネルの表示より、その出力を信じてください。
01 / ディスカッション
コメント欄はありません。議論は X で行っています。
02 / ほかの記事
Claude アプリで auto mode が標準に: チェックが止めるもの、89% の調査が測ったもの
Anthropic の調査は、その設計については正直です。疲れたテスターに、仕込んだ危険なコマンドを 1 つ見せただけです。誤検知率も実運用での見逃し率も示しておらず、クラウドセッションには戻る先の Manual モードがありません。
Claude Code の料金 2026:同じ作業に Pro、Max、API はいくら請求するか
プランの料金は定額で、トークン単価も公開されています。そのため比較はただの計算に見えますが、そう単純ではありません。Claude Code は支払い方によって会話のキャッシュの仕方を変えます。そしてサブスクリプションの利用量をドル換算するのに多くの人が使うツールは、実際に使っていたキャッシュで計算するため、乗り換えた後に使うことになるキャッシュの料金は出てきません。
Claude Code のフック:誘導には強く、関門としては弱い
フックはモデルが同意するかどうかに関係なく実行されます。そのためフォーマットのような雑用や、エージェントを望むルートへ誘導する用途には向いています。関門としては見た目より弱く、指定したツールしか検査せず、チェック自体が壊れるとブロックを明示的に有効にしない限り呼び出しは通り、構成によっては、フックがブロックした呼び出しを mod が承認できてしまいます。