Claude Media
allowWrite/denyWriteでkubectl・terraformの書き込み範囲を調整する

allowWrite/denyWriteでkubectl・terraformの書き込み範囲を調整する

sandbox.filesystem.allowWrite/denyWrite/allowReadで書き込み・読み取り範囲をパス単位で調整する方法と、denyWriteが許可領域の内側にも効く仕組みを扱います。

Claude Codeのサンドボックス化されたBashツールは、既定だと作業ディレクトリと一時ディレクトリの外への書き込みを止めます。kubectlやterraformのようなサブプロセスがkubeconfigやstateファイルへ書き込もうとすると、この既定のままでは失敗します。sandbox.filesystem.allowWrite・denyWrite・allowReadは、書き込みと読み取りの範囲をパス単位で調整する設定です。読み取りのルールが重なったときはより狭いパスを指すルールが勝ち、書き込み側ではdenyWriteが他の設定で書き込み可能になっている領域の内側にも効きます。

allowWriteでkubectl・terraformの書き込み範囲を広げる

既定の書き込み範囲は、現在の作業ディレクトリとそのサブディレクトリ、--add-dir・/add-dir・permissions.additionalDirectoriesで追加したディレクトリ、そして$TMPDIRが指すユーザーごとの一時ディレクトリに限られます。kubectl・terraform・npmのようなサブプロセスがこの範囲の外へ書き込む必要があるときは、sandbox.filesystem.allowWriteで特定パスへのアクセスを許可します。

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

この設定で、kubectlはkubeconfigを更新でき、ビルドは/tmp/build配下に書き込めます。パスはOSのサンドボックス境界で強制されるため、コマンドが起動する子プロセスも含めて範囲が適用されます。公式ドキュメントは、ツールが特定の場所への書き込みだけを必要とする場合、この方法をexcludedCommandsでツールをサンドボックスごと除外するより推奨される選択肢と位置づけています。除外はサンドボックスの外へ丸ごと逃がす設定で、書き込み場所だけを広げたいときの手段としては強すぎます。

作業ディレクトリがlinked git worktree(git worktree addで作った作業ツリー)のときは、既定の書き込み範囲にもう1つ例外があります。サンドボックスはメインリポジトリが共有する.gitディレクトリへの書き込みも許可し、git commitがrefとインデックスを更新できるようにします。ただしその.gitディレクトリの中のhooks/とconfigへの書き込みは、この例外の対象外として拒否されたままです。

パスの解釈は先頭の記号で決まります。/から始まると絶対パス、~/はホームディレクトリからの相対パス、./または無指定はプロジェクト設定ならプロジェクトルート、ユーザー設定なら~/.claudeが起点になります。絶対パスは//pathの書き方でも解決できます。末尾のスラッシュは取り除かれるため~/.kubeと~/.kube/は同じ扱いで、末尾の/**も除去されるので~/build/**と~/buildは同じ範囲を指します。この記法はRead・Edit権限ルール(/pathがプロジェクト相対、//pathが絶対)とは基準が逆になっているため、sandbox.filesystem側に/pathと書くとそのまま絶対パス扱いになる点に注意します。

excludedCommandsにkubectlやterraformを加えても、対象から外れるのはコマンド全体が条件に一致したときだけです。cdを含む呼び出しや、パイプ以外のリダイレクト、if・forのような制御構文、コマンド置換を伴う呼び出しはサンドボックス内に留まり続けます。docker *というエントリだけではnpm ci && docker build .のようなワンライナーは対象外のままです。書き込み場所だけを広げたい場面でallowWriteが優先される理由の1つはここにあります。

denyWriteで書き込みを狭める

allowWriteとは逆に、sandbox.filesystem.denyWriteは特定パスへの書き込みを拒否します。対象になるのは、他の設定で書き込み可能になっている領域の内側のパスも含みます。

{
  "sandbox": {
    "filesystem": {
      "denyWrite": ["/etc", "/usr/local/bin"]
    }
  }
}

システム設定や$PATH上のバイナリへの書き込みを止めたいときに使う設定です。allowWrite・denyWriteの配列はどちらも、セッションが読み込むユーザー設定・プロジェクト設定・ローカル設定・管理設定のすべてからマージされます。Edit権限ルールの許可エントリはallowWriteに、拒否エントリはdenyWriteに、それぞれ自動で加算されます。

読み取りは狭いルールが勝ち、denyWriteは許可領域の内側にも効く

denyWrite・denyReadで範囲を拒否し、allowReadで拒否領域の中の特定パスだけ読み取りを再許可できます。読み取りのルールが重なったときは、より狭いパスを指すルールが優先されます。たとえばCIキャッシュ用のディレクトリだけホームディレクトリの拒否から再度開けたい場合は、次のように書きます。

{
  "sandbox": {
    "filesystem": {
      "denyRead": ["~/"],
      "allowRead": ["~/ci-cache"]
    }
  }
}

~/ci-cacheだけが読み取り可能になり、ホームディレクトリの残りはdenyReadのまま拒否されます。逆に広いallowReadの内側に個別のdenyReadを置いても、狭い方の拒否は素通りされずに効き続けます。書き込み側では、~/.kube全体をallowWriteで開けつつ、その中の特定パスだけdenyWriteで塞ぐ運用ができます。denyWriteは他の設定で書き込み可能になっている領域の内側にも効くためです。

公式ドキュメントが示す読み取り側の重なりは3パターンです。ホームディレクトリ全体をdenyReadで塞いだ状態で~/projectsだけallowReadに加えると、~/projectsは読める一方でホームディレクトリの残りは塞がれたままです。逆にホームディレクトリ全体をallowReadで開けた状態で~/.envだけdenyReadに加えると、~/.envだけが塞がれ、それ以外は読めます。広い許可があっても個別の拒否は飲み込まれません。denyReadにワイルドカードを使い~/**/.envのようにホームディレクトリ配下のすべての.envを指定した場合も、同じ優先順位で拒否が保たれます。

ワイルドカードの扱いはプラットフォームで割れます。denyRead・allowReadはワイルドカード(*)がどのプラットフォームでも使え、LinuxとWSL2では一致する具体パスへ展開されます。一方allowWrite・denyWriteはmacOSでだけワイルドカードが機能し、LinuxとWSL2では*・?・[を含むエントリを設定しても、そのエントリはそのまま無効になります。Edit権限ルールからallowWrite・denyWriteへ加算されるパスにも同じ制限がかかります。

allowWriteでも越えられない保護パス

allowWriteは書き込み範囲を広げる設定ですが、.claudeの設定ファイルや.claude/skills、.mcp.json、~/.claude配下の大半のような保護パスは対象外です。公式ドキュメントは「allowWriteエントリや対象パスをカバーするEdit許可ルールがあっても、この保護は解除されない」と明記しています。保護を外す唯一の方法はfilesystem.disabledで、これはファイルシステム分離そのものを全パスに対して無効化する設定です。個別パスの保護だけを解除する経路はありません。

git mergeやgit checkoutがunable to unlink oldで失敗するのは、サンドボックスが書き込みを拒否しているファイルを置き換えようとしたときです。原因は保護パスのどれかであることも、自分で書いたdenyWriteエントリであることも、単に書き込み範囲の外であることもあります。LinuxとWSL2ではエラーの末尾がRead-only file systemになります。

allowWriteを広げるときに確認すること

allowWriteで書き込み範囲を広げるときは、その分ネットワーク側の許可まで緩めていないかを合わせて確認します。公式ドキュメントは、ファイルシステム分離とネットワーク分離は独立した層であり、一方を広げるときにもう一方の制限が実質的に打ち消されていないかを確認するよう注意を促しています。ネットワーク分離が働いていない状態で書き込み範囲だけを絞っても、SSH鍵のような機微なファイルを外部へ持ち出す経路が別に残ることがあります。書き込み範囲の設定とドメイン許可リストは別々の設定なので、片方だけを見て安全と判断しないことが重要です。

設定を変更したら/sandboxを実行して確認します。

/sandbox

Configタブを開くと、保護パスの大半が自分で設定したdenyWriteエントリと混ざってDenied within allowedの項目に一覧表示されます。

excludedCommandsやfilesystem.disabledとの使い分け

書き込み範囲を広げる手段はallowWriteだけではありません。目的ごとに適した設定は次のとおりです。

目的使う設定効果と注意点
特定パスへの書き込みだけ許可使う設定allowWrite効果と注意点OSレベルで強制されサンドボックスの隔離は保たれる。公式が推奨する方法
ツールをサンドボックスの外で丸ごと実行使う設定excludedCommands効果と注意点通常の権限フローに戻るだけの便宜でセキュリティ境界ではない。管理専用ロックが無く開発者が自由に追記できる
ファイルシステム分離ごと外す使う設定sandbox.filesystem.disabled効果と注意点ネットワーク分離は残るが読み書きは全開放になる

excludedCommandsは「便宜であってセキュリティ境界ではない」と公式ドキュメントが明言している設定です。ツールが必要としているのが特定の書き込み場所だけなら、範囲を絞れるallowWriteを優先します。

導入時によくあるつまずき

allowWriteを書いても保護パスは開かない

.claude/settings.jsonや.git/hooksのような保護パスはallowWriteの対象外なので、書き込みが必要な場面では別のパスへ逃がすか、そもそもその操作をサンドボックスの外で行う設計に変えます。

LinuxとWSL2ではワイルドカードのallowWrite・denyWriteが無効化される

エラーにはならず、ワイルドカードを含むエントリがそのまま効果を持たないだけなので気づきにくい部分です。対象パスを個別に列挙するか、macOS限定の運用と割り切ります。

excludedCommandsだけでは組織のポリシーを固定できない

管理設定で配布しても、開発者はローカル設定に自分のexcludedCommandsエントリを追記でき、それがそのままマージされます。管理者が意図した境界を保ちたいなら、対象コマンドをできるだけallowWriteで絞り込む運用に寄せ、excludedCommandsのリストは狭く保ちます。

まとめ

allowWrite・denyWrite・denyRead・allowReadはいずれもスコープがAny fileで、プロジェクト設定・ユーザー設定・ローカル設定・管理設定のどこに書いても有効になります。管理設定でしか書けないallowManagedReadPathsOnlyのようなロックとは異なり、これら4つの配列は開発者が自分の設定ファイルに追記した分もそのままマージされる前提で運用します。

sandbox.filesystem.allowWriteは既定の書き込み範囲の外へ、kubectlやterraformのようなサブプロセスの書き込みを許可する設定です。denyWriteは逆に、他の設定で書き込み可能になっている領域の内側でも特定パスを狭めます。読み取り側のdenyRead・allowReadは、複数のルールが重なったとき、より狭いパスを指すルールが勝つという優先順位で動きます。保護パスだけはこの仕組みの外にあり、allowWriteでもEdit許可ルールでも解除できません。書き込み場所を広げる前に、allowManagedReadPathsOnlyによるmanaged専用ロックの範囲や、認証情報ファイルを狙う場合はdenyReadでの遮断、excludedCommandsが効かない既知のバグは別記事の3つの原因も合わせて確認しておくと、意図しない穴を残さずに済みます。

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