Claude Media
enableWeakerNestedSandboxとbwrapPathでDocker内のsandboxを動かす

enableWeakerNestedSandboxとbwrapPathでDocker内のsandboxを動かす

非特権Dockerでsandboxが動かない原因は/procのマウントです。enableWeakerNestedSandboxで何が弱まるか、PATH外のbwrapを指すbwrapPathとの使い分けを比べます。

2つの設定は何を解くのか

sandbox.enableWeakerNestedSandboxは、非特権のDockerコンテナの中でClaude CodeのLinux sandboxを動かすための設定です。sandbox.bwrapPathは、PATHの外に置いたbubblewrap(bwrap)をsandboxに使わせる設定です。名前は似ていませんが、どちらもLinuxとWSL2のbubblewrapまわりを調整するキーなので、閉じたネットワークやコンテナで運用するときに一緒に出てきます。

解く問題は別物です。

設定解く問題設定できる場所既定値
enableWeakerNestedSandbox解く問題コンテナ内でbwrapが新しい/procをマウントできない設定できる場所どの設定ファイルでも可既定値false
bwrapPath解く問題bwrapがPATH上に無い設定できる場所管理設定(managed)のみ既定値未設定(PATH検索)

どちらもsandboxを有効にするsandbox.enabledの上に乗るキーです。有効化そのものはsandbox.enabledの記事が扱っています。

非特権Dockerでsandboxが失敗する理由

非特権コンテナでは、bubblewrapが新しい/procファイルシステムをマウントできません。sandbox内のコマンドは次のようなbwrapエラーで失敗します。

bwrap: Can't mount proc on /newroot/proc: Operation not permitted

メッセージが「Operation not permitted」で、しかもコンテナの中で出ているなら、enableWeakerNestedSandboxで解消する症状です。NixOSの権限エラーやunshareの失敗は、原因が別なので対処も違います。症状別の切り分けはNixOSのbwrapエラーとapply-seccompのsetgroups失敗にあります。

enableWeakerNestedSandboxで何が弱まるか

trueにすると、内側のsandboxは新しい/procを作らず、コンテナが持っている既存の/procをbind-mountします。これでbwrapは起動できますが、新しいマウントなら隠れるはずのプロセス情報が、sandbox内のコマンドから見えるようになります。

{
  "sandbox": {
    "enabled": true,
    "enableWeakerNestedSandbox": true
  }
}

値の意味は2つだけです。

  • true: 既存の/procをbind-mountする
  • false(既定): 新しい/procをマウントする。非特権Dockerでは動かない

弱まるのは/procの見え方です。ファイルシステムやネットワークの許可設定がそのまま消えるわけではありません。ただ、外側のコンテナがすでに隔離を担っている前提で使う設定です。コンテナ自体が特権付きだったり、ホストの/procやソケットを大きく共有していたりすると、sandboxの内側が弱くなった分を外側で補えません。

名前が似たenableWeakerNetworkIsolationはmacOS用で、効く場所がまったく違います。違いはenableWeakerNetworkIsolationの記事にまとめてあります。

設定する場所と、組織が止める方法

enableWeakerNestedSandboxはどの設定ファイルにも書けます。つまり開発者がユーザー設定から、リポジトリが.claude/settings.jsonからtrueにできます。管理側で許したくないなら、管理設定でfalseを固定します。管理設定が値を持つBoolean系のキーは、ローカルの値が無視されるためです。

sandboxを管理者が必須にしている構成(allowUnsandboxedCommands: falseかallowManagedDomainsOnly: trueが管理設定にある状態)では、扱いがもう一段厳しくなります。

設定を書いた場所true の扱いfalse の扱い
リポジトリの.claude/settings.jsonと.claude/settings.local.jsontrue の扱い無視されるfalse の扱い有効のまま
管理設定、--settings、ユーザー設定true の扱い有効false の扱い有効

リポジトリ側から勝手に隔離を弱められず、falseだけは通るという非対称な作りです。この挙動はClaude Code v2.1.285以降が前提です。管理設定の全体像はClaude Code組織管理ガイドにあります。

bwrapPathでPATH外のbwrapを使う

エアギャップ環境では、パッケージマネージャーからbubblewrapを入れられないことがあります。社内で用意したコピーを/opt/admin/bwrapのような場所に置くなら、bwrapPathでその絶対パスを指します。

{
  "sandbox": {
    "enabled": true,
    "bwrapPath": "/opt/admin/bwrap"
  }
}

押さえる点は4つです。

  • 管理設定からだけ読まれます。ユーザー・プロジェクト・ローカルのファイルに書いても、別のバイナリを指させる経路にならないよう無視されます
  • 値は絶対パスの文字列です。相対パスは捨てられ、PATH検索に戻ります
  • 起動時の依存確認と、sandbox内の各コマンドを包むときの両方で、そのパスが使われます
  • Linux、WSL2のみが対象です

socatを同じ要領で指すsandbox.socatPathもあり、違いはsocatPathの記事で比べています。

組み合わせるときの判断

Docker内かつエアギャップ、という環境では2つが同時に要ることがあります。ただし役割が独立しているので、症状から順に外していくと迷いません。

手順

Linuxのsandboxが動かないときの確認順

  1. 1

    bwrapが見つかるか

    /sandboxパネルがbubblewrapの不足を示すなど、バイナリ自体が見つからないならPATHの問題です。入れ直すか、管理設定のbwrapPathで場所を教えます。

  2. 2

    起動後に/procのエラーが出るか

    Can't mount proc on /newroot/procなら、コンテナが非特権です。外側が隔離を担うと判断できる場合に限り、enableWeakerNestedSandboxをtrueにします。

  3. 3

    それでも出るなら別の原因

    Ubuntu 24.04以降の既定のAppArmorポリシーは、bubblewrapが隔離に使うユーザー名前空間の作成を止めます。/procとは無関係な原因なので、enableWeakerNestedSandboxでは直りません。下の節の手順でbwrap用のプロファイルを足します。

Ubuntu 24.04以降でユーザー名前空間を許可する

まず制限が効いているかを確かめます。WSL2の中でも同じ確認ができます。

sysctl kernel.apparmor_restrict_unprivileged_userns

0が返れば制限はなく、この手順は不要です。No such file or directoryと出る場合もキー自体が無いので不要です。1が返ったときだけ、bwrapにユーザー名前空間を許すプロファイルを置きます。

sudo tee /etc/apparmor.d/bwrap > /dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>
 
profile bwrap /usr/bin/bwrap flags=(unconfined) {
  userns,
  include if exists <local/bwrap>
}
EOF
sudo systemctl reload apparmor

このプロファイルが対象にするのはbwrap自身だけで、bwrapがsandbox内で動かすコマンドには及びません。

/sandboxパネルのDependenciesタブは、ripgrep・bubblewrap・socat・seccompフィルターのうち、足りないものを一覧します。タブが出ないなら依存は揃っています。まずここで1の段階を確かめられます。

WSL2で試している場合は、先にWSLのバージョンも確かめます。PowerShellでwsl -l -vを実行し、Sandboxing requires WSL2と出たなら、そのディストリビューションはWSL1で動いています。WSL2へ上げるか、sandboxなしで使うかのどちらかになり、この記事の2つの設定では解決しません。ネイティブのWindowsでもsandboxは動かないため、bwrapPathを含めて対象外です。

コンテナイメージに依存と設定を焼き込む

Linux側のsandboxはbubblewrapとsocatに頼ります。Dockerfileで先に入れておけば、起動してから/sandboxで足りない依存に気づく手間が減ります。Ubuntu系のイメージなら、パッケージは次の1行です。

RUN apt-get update && apt-get install -y bubblewrap socat

Fedora系ならdnf install bubblewrap socatに置き換えます。ripgrepはネイティブ版のClaude Code本体に同梱されているので、別に入れる必要はありません。Unixドメインソケットの遮断を足すseccompフィルターは任意の依存で、足りなければnpm install -g @anthropic-ai/sandbox-runtimeで入れます。

設定はイメージの中に置くと、起動のたびに配る手間が消えます。管理設定のファイルはLinuxなら/etc/claude-code/managed-settings.jsonです。ここに次を書くと、このコンテナの中ではsandboxが必須になります。

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

各キーの役割は次のとおりです。

  • failIfUnavailable: 依存が欠けているなどでsandboxを起動できないとき、隔離なしで動き続けず、起動時にエラーで終了する。既定はfalseで、そのままだと隔離なしで動いてしまう
  • allowUnsandboxedCommands: false: sandboxで失敗したコマンドを、Claudeがsandbox外で再試行する経路を閉じる
  • enableWeakerNestedSandbox: 前の節の/procの問題を避ける

管理設定にallowUnsandboxedCommands: falseを置くと、リポジトリ側の.claude/settings.jsonがenableWeakerNestedSandboxをtrueにしても無視されます。上の例のようにイメージ側の管理設定で自分でtrueを書いておけば、この制約の影響を受けません。依存を入れ直したあとは、依存確認が起動時に走るため、Claude Codeを再起動してから/sandboxで確かめます。

/procが見えると何が変わるか

enableWeakerNestedSandboxで変わるのは、sandbox内のコマンドから見える/procだけです。設定リファレンスの記述は「プロセス情報が露出する」までで、どの項目がどこまで見えるかの一覧は載っていません。具体的な見え方はコンテナのPID名前空間の設定に左右されるため、機密に近い環境では、実際にpsなどを試して確かめてから使う選択肢があります。

確かめた結果、コンテナ内のプロセスしか見えないなら、露出の範囲はコンテナの内側に収まります。ホストのプロセスまで見えるなら、外側のコンテナ設定の見直しが先です。

外側のコンテナに隔離を任せる設計

この設定を使うのは、sandboxを「二重化」したいのではなく、外側のコンテナが本来の境界である構成です。たとえばCI用のコンテナ、使い捨てのDevContainerがそうです。DevContainerで認証情報を遮断する設計はDevContainerの完全実装が扱っています。

excludedCommandsでdockerを対象外にする使い方とは、向きが逆です。そちらはsandboxの内側からDockerを使う話で、本記事の設定はDockerの内側でsandboxを動かす話です。

まとめ

enableWeakerNestedSandboxは、非特権Dockerで/procのマウントが通らないときにだけtrueにする、隔離を一段弱める設定です。bwrapPathは管理設定専用で、PATH外のbwrapを使わせます。エラーが「bwrapが無い」のか「/procを作れない」のかを先に見分ければ、どちらを触るかは決まります。組織で運用するなら、前者は管理設定で配り、後者は管理設定でfalseに固定しておく選択肢があります。

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