Claude Media
sandbox.excludedCommandsでdockerなど非対応コマンドを対象外にする

sandbox.excludedCommandsでdockerなど非対応コマンドを対象外にする

sandbox.excludedCommandsは、dockerやosascriptなどサンドボックス内で動かないコマンドだけを外で実行させる設定です。書き方と、書いても効かない呼び出しの形をまとめました。

Claude Codeのサンドボックスを有効にすると、dockerやgh、osascriptのような一部のコマンドが正常に動かなくなることがあります。sandbox.excludedCommandsは、こうした特定のコマンドだけをサンドボックスの外で実行させる設定です。ただし、書いたコマンドが必ず外に出るわけではありません。エントリに一致しないコマンドを&&でつないだ呼び出しや、cdを含む呼び出しは、内側に残ります。

excludedCommandsは何を除外する設定か

sandbox.excludedCommandsは、Claude Codeが実行するBashコマンドのうち、サンドボックスの外で動かすものを名前で指定する配列です。既定値は未設定で、何も対象外になりません。各エントリの書式は、許可ルールBash(...)の中身と同じです。dockerのような完全一致のコマンド名、docker *のようなプレフィックス、ワイルドカードパターンを書けます。

書いたのにサンドボックスの中で動く呼び出し

エントリは、Bashの呼び出しに含まれるすべてのコマンドを覆っているときだけ、その呼び出しをサンドボックスの外に出します。docker *だけを書いた状態でnpm ci && docker build .を実行しても、外には出ません。npm ciがどのエントリにも一致しないためです。一部分でも一致すれば全体が外へ出る、という動きではありません。

この挙動はv2.1.277からです。それ以前は、一部のコマンドに一致すれば呼び出し全体が外へ出ていました。

さらに、エントリが全コマンドを覆っていても、次の形の呼び出しはサンドボックスに残ります。

注意

エントリを書いても内側に残る呼び出しの形

  • 先頭が特定のコマンド

    sudo・eval・xargsで始まる呼び出し。

  • ディレクトリ移動を含む

    cd・pushd・popdが、呼び出しのどこにあっても該当します。

  • 入れ子の構文

    コマンド置換、サブシェル、ifやforのような制御構文。

  • リダイレクトと変数

    docker build . > build.logのようなリダイレクト(2>&1のように記述子を複製するだけのものは除く)と、コマンド名が変数から来る呼び出し。

公式の例では、docker *のエントリがあってもcd build && docker compose upは内側で走り、cdのエントリを足しても変わりません。上の一覧は「among others」(ほかにもある)と書かれた例示なので、完全な一覧ではありません。動かないコマンドをexcludedCommandsに入れたのに同じエラーが出るときは、まず呼び出しの形を疑います。呼び出しからこれらの形を除けるかどうかも、見直しの候補になります。

dockerを対象外にする書き方

dockerはサンドボックスと相性が悪く、公式のトラブルシューティングでは「dockerコマンドが失敗する場合はdocker *をexcludedCommandsに加える」と案内されています。

{
  "sandbox": {
    "enabled": true,
    "excludedCommands": ["docker *"]
  }
}

この書き方では、dockerで始まるコマンドが対象になります。docker buildとdocker psのように後ろの引数が変わっても、プレフィックスが一致していれば同じ扱いです。特定のサブコマンドだけに絞りたい場合は、docker ps *のように書きます。docker psと書くと、引数なしのdocker psだけに一致します。Claude CodeでDockerを実行するようなコンテナ中心の開発フローでは、dockerを含む呼び出しが上の形に当たっていないかが要点になります。

dockerを外へ出さず、ソケットだけを許可する道も考えられます。sandbox.network.allowUnixSocketsは、サンドボックス化されたコマンドが使えるUnixソケットのパスを列挙する設定です。ただしLinuxとWSL2ではこのリストが無視され、代わりにallowAllUnixSocketsを使う仕様です。これでdockerが動くとは公式に書かれておらず、確認できているのは、ソケットの許可がDockerデーモンの操作権限に及ぶという警告までです。

くらべる

dockerをどちらの形で通すか

コマンド単位

excludedCommandsで外へ出す

docker *のコマンドがサンドボックスの隔離を受けなくなります。前節のとおり、cdやリダイレクトを含む呼び出しは外へ出ません。

ソケット単位(macOS)

allowUnixSocketsで通す

dockerはサンドボックス内に残り、指定したソケットへの接続だけが許可されます。公式は、/var/run/docker.sockの許可がホストシステムへのアクセスと実質同じ意味を持つと警告しています。

どちらを選んでも、dockerデーモンの操作権限をコマンドに渡すことには変わりません。

複数のツールをまとめて対象外にする

docker以外にも動かないツールがある場合は、同じ配列に複数のエントリを並べます。

{
  "sandbox": {
    "enabled": true,
    "excludedCommands": ["docker *", "gh *", "osascript *"]
  }
}

配列は設定ファイルの階層(ユーザー・プロジェクト・ローカル)をまたいでマージされます。プロジェクトの.claude/settings.jsonにチームで共有するエントリを書き、個人の環境だけで必要なものはユーザー設定に足す、という分け方ができます。ただしmanaged設定でstrict sandbox modeにしている組織では、v2.1.282以降プロジェクト側のエントリは無視されます(後述)。

何を許可して何を許可しないかを1つの設定にまとめる例

excludedCommandsは単独ではなく、filesystemやnetworkの設定と一緒に書くことが多い項目です。次の例は、サンドボックスを有効にして確認プロンプトを省略し、dockerだけを外で動かす構成です。あわせてビルド用ディレクトリと~/.kubeへの書き込みを許可し、AWSの認証情報ファイルを読めなくし、GitHubとnpmへの通信を確認なしで通します。

{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "excludedCommands": ["docker *"],
    "filesystem": {
      "allowWrite": ["/tmp/build", "~/.kube"],
      "denyRead": ["~/.aws/credentials"]
    },
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    }
  }
}

excludedCommandsが担うのはdockerの部分だけです。残りの制限は、サンドボックス化されたコマンドにそのまま効き続けます。

excludedCommandsは安全境界ではない

excludedCommandsに書いたコマンドも、通常の許可フローは通ります。外れるのはサンドボックスの隔離だけです。権限ルールでaskやdenyにしているコマンドは、対象外にしても確認や拒否の対象のままです。

autoAllowBashIfSandboxed(既定はtrue)の恩恵も及びません。この設定が確認なしで走らせるのは、サンドボックス化されたBashコマンドです。サンドボックスで動かせないコマンドは通常の許可フローに戻るので、外に出したコマンドの確認プロンプトは減りません。

公式は、この設定を「利便性のための仕組みで、安全境界ではない」と位置づけています。ツールが特定のパスに書き込めればよいだけなら、sandbox.filesystem.disabledの記事で扱うfilesystem.allowWriteのように書き込み先を個別に追加するほうが、隔離の範囲を狭く保てます。

サンドボックスの他の一覧設定には、allowManagedReadPathsOnlyやallowManagedDomainsOnlyのように、managed設定のエントリだけに絞り込めるものがあります。excludedCommandsにはこの種の管理者専用ロックがありません。

ただしchangelogのv2.1.282には、この挙動を変える記述があります。managed設定か--settingsでallowUnsandboxedCommands: falseにしているとき、またはmanagedでallowManagedDomainsOnly: trueにしているときは、プロジェクト設定とローカル設定のエントリが無視されます。ユーザー設定のエントリは対象外なので、そのまま残ります。docsの該当ページにはまだこの変更が反映されておらず、出典はchangelogです。

症状から探す:excludedCommandsで直るもの・直らないもの

公式のトラブルシューティングには、excludedCommandsが候補になる症状と、別の手当てが案内されている症状が混在しています。

症状公式の案内
dockerコマンドが失敗する公式の案内docker *をexcludedCommandsに追加
gh・gcloud・terraformがmacOSでTLS検証に失敗する公式の案内excludedCommandsに列挙。MITMプロキシとカスタムCAを使う構成ならenableWeakerNetworkIsolation
open・osascriptがmacOSでエラー-600になる公式の案内allowAppleEvents、またはexcludedCommands
jestが固まる・失敗する公式の案内watchmanがサンドボックスと非互換。jest --no-watchmanで実行
git merge・git checkoutがunable to unlink oldで失敗する公式の案内承認して外で再試行。頻発するならexcludedCommands
pbcopy・xclip・wl-copyでクリップボードが更新されない公式の案内/copyを使う。excludedCommandsに入れても、パイプで渡す呼び出しは外に出ない

unable to unlink oldは、サンドボックスが書き込みを拒否しているファイルをgitが置き換えようとしたときに出ます。対象は、.claude/skillsのような保護パス、denyWriteに入れたパス、サンドボックスが書き込みを許す範囲の外です。LinuxとWSL2では、エラー文の末尾がRead-only file systemになります。allowUnsandboxedCommandsをfalseにしていると、Claudeは外での再試行を提案できません。その場合は、別のターミナルで自分で実行します。

ghなどGo製ツールは、macOSのSeatbelt下でTLS検証に失敗することがあります。失敗するツールをexcludedCommandsに書くのが基本の対処です。network.httpProxyPortでMITMプロキシとカスタムCAを使う構成では、enableWeakerNetworkIsolationが必要になります。これは、サンドボックスからmacOSのシステム信頼サービスcom.apple.trustd.agentに届かないためです。書いても効かない場合は、sandbox.excludedCommandsが効かない原因の記事にまとめた既知の挙動が当たっていないか確認します。

openやosascriptのエラー-600は、サンドボックスがApple Eventsの送信を既定で止めているために起きます。allowAppleEventsを有効にすると、サンドボックス化されたコマンドが他のアプリケーションを確認なしで起動でき、コード実行の隔離が崩れます。この設定はユーザー・managed・CLIの設定でのみ有効で、プロジェクト設定では無視されます。

他のサンドボックス緩和設定との使い分け

設定効果向く場面
sandbox.excludedCommands効果指定したコマンドだけをサンドボックス外で実行向く場面特定のツールだけが内側で動かない
sandbox.filesystem.allowWrite効果書き込み範囲を個別に追加(隔離は維持)向く場面ツールが特定パスへの書き込みだけを必要とする
sandbox.allowUnsandboxedCommands効果falseでClaudeによる外での再試行を禁止向く場面strict sandbox modeにしたい
sandbox.enableWeakerNetworkIsolation効果macOSのTLS信頼サービスへの到達を許可向く場面MITMプロキシとカスタムCAでGo製CLIの証明書検証が必要
sandbox.allowAppleEvents効果Apple Eventsの送信をサンドボックス全体で許可向く場面openやosascriptなどが恒常的に必要

enableWeakerNetworkIsolationとallowAppleEventsは、どちらもサンドボックス全体に効く緩和です。問題のツールが1つならexcludedCommandsのほうが範囲は狭く済みます。ドメイン単位でネットワークを絞る設定は、IPv6ドメイン許可リストの書き方の記事でも扱っています。

sandboxが起動できないときとの違い

excludedCommandsは、サンドボックスを有効にしたまま特定のコマンドだけを外に出す設定です。サンドボックスが起動できない状況とは別物です。LinuxやWSL2でbubblewrapやsocatが入っていない、あるいはプラットフォームが未対応だと、Claude Codeは警告を出して全コマンドをサンドボックスなしで実行します。これを起動時のエラーで止めたいなら、sandbox.failIfUnavailableをtrueにします(既定はfalse)。

strict sandbox modeでは唯一の出口になる

sandbox.allowUnsandboxedCommandsをfalseにすると、Claudeはサンドボックスに阻まれたコマンドをdangerouslyDisableSandboxパラメータで再試行できなくなります。/sandboxパネルのOverridesタブは、この状態をStrict sandbox modeと表示します。

/sandbox

この状態では、Claudeが実行するすべてのコマンドが、サンドボックス化されるかexcludedCommandsに載っているかのどちらかになります。通常モードの再試行は、Manualモードなら確認プロンプトを経て外で走ります。strict modeでは再試行の道が閉じるので、excludedCommandsがサンドボックスをすり抜ける唯一の入り口になります。managed設定でstrict modeにしている場合、v2.1.282以降はプロジェクト・ローカル設定のエントリが無視されます。

!から始まる、ユーザー自身が打つシェルモードのコマンドは別扱いです。strict sandbox modeを有効にしていても、既定ではサンドボックスの外で走ります。バックグラウンドセッション(strict modeが!コマンドにも及ぶ)と、Linuxで環境変数CLAUDE_CODE_SUBPROCESS_ENV_SCRUBを設定したセッション(すべてのコマンドがサンドボックス内で走る)は例外です。v2.1.260より前は、どのセッションでも!コマンドがstrict modeの対象でした。

手元のclaudeで確認できたこと

v2.1.285(ネイティブインストール)で、ログインせず空の設定ディレクトリを使って確認しました。claude --versionは2.1.285 (Claude Code)を返します。claude --helpの出力に、サンドボックス専用のオプションはありません。sandboxという語は--dangerously-skip-permissionsの説明に1回出るだけで、サンドボックス機能のオプションではありません。excludedCommandsのようなサンドボックス設定は、設定ファイルと/sandboxパネルで扱う設計です。claude doctorは設定ファイルを読んで導入状態を診断しますが、出力にsandboxの項目はありませんでした。サンドボックス設定の反映は、セッション内の/sandboxパネルで見ることになります。

まとめ

dockerが失敗したら、まずallowWriteで足りないかを確かめ、足りなければdocker *を足して呼び出しの形を見直します。strict sandbox modeの組織では、プロジェクト側に書いたエントリが効くかどうかを、managed設定の条件で確認します。

この記事を共有:XはてブLinkedIn