【Claude Code 連載 第17回】サンドボックス——確認を減らすのではなく、境界を引く

スポンサーリンク
【Claude Code 連載 第17回】サンドボックス——確認を減らすのではなく、境界を引く 用語解説
【Claude Code 連載 第17回】サンドボックス——確認を減らすのではなく、境界を引く
この記事は約9分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

スポンサーリンク

背景

この連載では、Claude Codeの公式ドキュメントを1ページずつ読み解いていきます。公式ドキュメントは情報が網羅されている分、「結局どの機能を、どんな場面で使えばいいのか」は自分で考える必要があり、読むのに意外と時間がかかります。そこで、私が実務で使うために読み込んだ内容を「使う場面→実例コード」の順に整理して残していくことにしました。専門家の解説というより、一次情報を読んだ記録です。推測や動作を確認していない部分には、その都度そう書きます。

1. これは一言でいうと何か

Bash サンドボックスは、Claude にほとんどのシェルコマンドを実行させつつ、コマンドがアクセスできるファイルとネットワークドメインを定義し、OS がその境界を実施する仕組みです。1コマンドごとに実行許可を求める代わりに、境界を1度決めます。

前回(第16回)の権限ルールとの違いは実施主体です。権限ルールはコマンド文字列を見て実行前に判断しますが、サンドボックスは実行中のプロセスに OS が境界を強制します。だからモデルが何を実行しようと、また許可されたコマンドが名前から想像される以上のことをしようとも、境界は保たれます。

2. どういう場面で役立つか

  • ビルドやテストを毎回承認するのが面倒:自動許可モードなら、サンドボックス内で完結するコマンドはプロンプトなしで走ります。境界の外に出ようとしたものだけが通常の許可フローに落ちます。
  • .ssh.aws を読ませたくない:デフォルトの読み取りポリシーはコンピュータ全体を読める設定なので、認証情報ファイルも読めてしまいます。sandbox.credentials で明示的に塞ぎます。
  • 組織全体に強制したい:管理設定で配れば、開発者が緩められないブール値キーとして効きます。
  • 無人実行の境界がほしい:権限モードの自動モードとは独立に動作し、組み合わせられます。

向かないケース・注意すべきケース。

  1. ネイティブ Windows では動きません。 macOS・Linux・WSL2 のみです。WSL1 も非対応で、Windows なら WSL2 内で実行します。
  2. 一部のツールは中で動きません。 docker は非互換で excludedCommands 行きです。watchman も非互換なので jest --no-watchman を使います。ghgcloudterraform などの Go 製 CLI は macOS の Seatbelt 下で TLS 検証に失敗することがあります。
  3. ハードなセキュリティ境界として頼るには制限があります。 ドキュメントは「完全な分離境界ではない」と明記しています。とくにネットワーク側は後述のとおり弱点があります。

3. コード・コマンドの実例と解説

前提条件

  • macOS は追加インストール不要(組み込みの Seatbelt を使用)。Linux と WSL2 では bubblewrapsocat が必要です。
  • 未導入でも /sandbox は起動でき、パネルの Dependencies タブに不足が表示されます。依存関係チェックは起動時に走るため、インストール後は Claude Code を再起動してください。
  • パネルでモードを選ぶと .claude/settings.local.json に書き込まれます(現プロジェクト限定、git 非対象)。全プロジェクトなら ~/.claude/settings.jsonsandbox.enabledtrue にします。

セットアップ

sudo apt-get install bubblewrap socat

Ubuntu / Debian の場合です(Fedora は sudo dnf install bubblewrap socat)。bubblewrap がファイルシステム分離を、socat がプロキシ経由のネットワーク中継を担います。

なお Ubuntu 24.04 以降では、既定の AppArmor ポリシーが bubblewrap に必要なユーザー名前空間の作成を妨げます。 sysctl kernel.apparmor_restrict_unprivileged_userns1 を返す場合は、bwrap 用の AppArmor プロファイル追加が必要です(手順は公式参照)。

書き込み先を広げる

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "allowWrite": ["~/.kube", "/tmp/build"]
    }
  }
}

既定では作業ディレクトリとセッション一時ディレクトリにしか書き込めません。kubectlterraform がその外へ書く必要がある場合は allowWrite で個別に開けます。OS レベルで実施されるため、子プロセスまで含めて全コマンドが従います。 ツールをまるごと excludedCommands でサンドボックス外に出すより、こちらが推奨アプローチです。

パスの書き方は権限ルールと異なります。 サンドボックスでは /tmp/build がそのまま絶対パス、~/ がホーム相対、./ またはプレフィックスなしが設定ソース相対です。第16回で見た //path が絶対、/path が設定ソース相対という Read / Edit ルールの規約とは別物なので、混ぜないよう注意してください。

ホームディレクトリの読み取りを塞ぐ

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyRead": ["~/"],
      "allowRead": ["."]
    }
  }
}

ホーム全体の読み取りをブロックしつつ、現在のプロジェクトだけを開ける設定です。読み取りルールが重なった場合はより具体的なパスが優先されます。

この設定はプロジェクトの .claude/settings.json に置く必要があります。 . がプロジェクトルートに解決されるのはプロジェクト設定にある場合だけで、同じものを ~/.claude/settings.json に置くと .~/.claude を指し、プロジェクトファイルは塞がれたままになります。

認証情報を保護する

{
  "sandbox": {
    "enabled": true,
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ],
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
}

AWS 認証情報と SSH ディレクトリの読み取りをブロックし、環境変数をサンドボックス化されたコマンドの環境から削除します(v2.1.187 以降)。組み込みの認証情報拒否リストは存在しません。 ここに書いたものだけが制限されるので、デフォルトのままだと ~/.ssh は読めます。

環境変数には "mode": "mask" もあります(v2.1.199 以降)。deny は変数を消すため ghnpm のようにそれを必要とするツールも壊しますが、mask ならコマンドはセンチネル値を見て、サンドボックスプロキシが送信時に実物へ差し替えます。ただしプロキシが中身を見る必要があるため network.tlsTerminate の設定が必須で、なければマスキングは安全側に失敗します(認証が通らない)。また mask はリポジトリ側の設定からは無視され、ユーザー設定・管理設定・--settings からのみ有効です。

組織に強制する

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false
  }
}

管理設定で配る構成です。failIfUnavailable は、依存関係不足時に警告してサンドボックスなしで実行するという既定の挙動をやめ、起動そのものをブロックします。allowUnsandboxedCommands: false は後述のエスケープハッチを無効化します。

ブール値キーは管理値が優先されますが、配列キー(excludedCommandsallowRead など)は全スコープでマージされるため、開発者が広げられます。読み取りを固定したいなら allowManagedReadPathsOnly、ドメインなら allowManagedDomainsOnly を使います。excludedCommands には同等のロックダウンがないので、管理リストは狭く保つことになります。

その他(/sandbox パネルの3タブ、WSL2 の Windows バイナリ制約、$TMPDIR の扱い、git worktree での .git 書き込み、カスタムプロキシの httpProxyPort / socksProxyPortallowAppleEventsenableWeakerNestedSandboxenableWeakerNetworkIsolation、トラブルシューティング各項)は公式ドキュメントを参照してください。

4. まとめと次回予告

  • サンドボックスは「確認を減らす」機能ではなく「境界を引く」機能です。実施するのは OS で、子プロセスまで効きます。
  • 既定は書き込みが作業ディレクトリのみ、読み取りはほぼ全体。認証情報は自分で塞ぐ必要があります。
  • 権限ルールとは補完関係です。パスとドメインの指定は最終的なサンドボックス設定にマージされます。

エスケープハッチの存在は知っておくべきです。 サンドボックス制限でコマンドが失敗すると、Claude は失敗を分析し、dangerouslyDisableSandbox パラメータでサンドボックス外で再試行することがあります。再試行には通常の許可フローが適用されるので、既定モードでは確認が出ます。自動モードでは分類器が評価するため確認は出ません。毎回確認したいなら Bash(dangerouslyDisableSandbox:true) の ask ルールを追加します。完全に塞ぐなら allowUnsandboxedCommands: false です。

ネットワーク側の限界も理解しておいてください。 組み込みプロキシはクライアントが申告したホスト名で許可判断をし、既定では TLS を終端も検査もしません。したがって github.com のような広いドメインを許可すると、ドメインフロンティング等で許可リスト外のホストに到達される可能性があります。より強い保証が要るなら、TLS を終端して検査するカスタムプロキシを構成します。

最後に、両輪であることの意味を。ドキュメントは「効果的なサンドボックス化にはファイルシステムとネットワーク両方の分離が必要」と述べています。ネットワーク分離がなければ SSH キーを流出させられ、ファイルシステム分離がなければバックドアを仕掛けてネットワークを得られるからです。既定を広げるときは、allowWrite・広い allowedDomainsexcludedCommands の例外が、反対側の制限を打ち消していないかを確認してください。

次回予告:連載の締めくくりとして「セキュリティ」または「ベストプラクティス」を取り上げる予定です。

関連ページ(本記事で触れた概念の詳細):allow / ask / deny ルール →「権限を設定する」、モードの選び方 →「権限モードを選択する」、dev コンテナや VM との比較 →「サンドボックス環境」、完全な設定リファレンス →「設定」、CLAUDE_CODE_SUBPROCESS_ENV_SCRUB →「環境変数」、包括的な保護 →「セキュリティ」。


本記事は執筆時点の公式ドキュメント(サンドボックス化された Bash ツールを設定する)に基づきます。最新は公式ドキュメントをご確認ください。

よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました