Laravel×Docker×AWSチームのClaude Code権限設計|composer・artisan・sail・docker compose exec・aws CLIのallow/ask/denyを共有設定と個人設定に分けて決める

技術ノウハウ

※本記事にはアフィリエイト広告が含まれます。

Claude Codeの .claude/settings.json をリポジトリにコミットしてチームで共有しようとすると、npmやgitを例にしたおすすめ設定はすぐに見つかります。一方、Laravelのチームで必要になる composer・php artisan・Laravel Sail・docker compose exec・aws については、公式のルールを自分たちのコマンドに当てはめて考える必要があります。

この記事では、PHP/Laravel・Docker・AWSを使う開発チームを想定して、どのコマンドをallow・ask・denyにするかの設計指針を、Claude Code・Laravel・Dockerなどの公式ドキュメントの記述だけをもとに整理します。ここで挙げるルールは公式の構文に沿った書き方の例で、実際に動かして確かめた結果ではありません。内容は確認日(2026-10-09)時点の公式ドキュメントに基づいています。Claude Codeは更新が頻繁なので、導入前に最新の公式ページもあわせて確認してください。

結論:Laravel×Docker×AWSチームの権限は『ランナー+内部コマンド』単位でallowし、データとクラウドを変える操作はask/denyにする

先に早見表を載せます。理由は後の章で順に説明します。

対象allow(確認なし)ask(毎回確認)deny(禁止)
artisantest・route:list をランナーごとに個別指定migrate 系全般。先頭に * を置いて呼び方の違いを覆う(例:Bash(* artisan migrate*))migrate:fresh / reset / refresh。先頭に * を置いて、php artisan/sail artisan/docker compose exec … php artisan をまとめて覆う(例:Bash(* artisan migrate:fresh*))
composervalidate など読み取り寄りrequire / update。ランナー経由用の Bash(* composer require *) と、ランナーなしの直接呼び出し用の Bash(composer require *) の両方を書く—
Sail / docker compose exec「ランナー+内部コマンド」の組み合わせだけ——
aws CLI共有には書かない(各自がローカルで読み取り系だけ)削除などの変更系(例:aws s3 rm)—
秘密情報——.env・~/.aws/** の Read

表の注記:artisanは、直接呼ぶときも php artisan と php が前に付くので、先頭の * を置いた1行で覆えます。composerは直接呼ぶと composer がコマンドの先頭に来て、* に当たる部分が残りません。先頭の * が空の文字列にも一致するかは公式に明記がないため、composerだけはランナーなしの1行を別に書きます(詳しくはSailの章で説明します)。また、Sail / docker compose exec では Bash(./vendor/bin/sail *) や Bash(docker compose exec *) のような広いallowを書かないようにします。

ルールは deny → ask → allow の順に評価され、最初に一致したものが結果を決めます。また、Bashのルールはコマンドの文字列に一致させる仕組みで、公式も「プログラムの周りのセキュリティ境界ではありません」と明記しています。この2点の詳細は公式の日本語ドキュメントにまとまっているので、本記事では前提として扱い、Laravelのコマンドへの当てはめに絞ります。

前提:この記事で使う公式ルールの要点(最小限)

Laravelのコマンドに当てはめるときに効いてくる公式の記述は、次の4つです。

  • 末尾の * の前のスペースはルールの一部:公式の例では Bash(ls *) は lsof に一致せず、スペースのない Bash(ls*) は lsof にも一致します。:* は末尾の * と同等の書き方で、パターンの末尾でだけ認識されます。また、最初の * より前の部分は、書かれたとおりに照合されます
  • ストリップされるラッパーは固定:Claude Codeは、Bashルールと照合する前に、決まったラッパーをコマンドから取り除き(ストリップし)ます。公式の例では、Bash(npm test *) というルールは timeout 30 npm test にも一致します。取り除かれるのは timeout・time・nice・nohup・stdbuf、シェルビルトインの command・builtin、zshの noglob で、このリストは設定では変えられません。docker exec・npx・devbox run などの開発環境ランナーはこのリストに含まれません
  • 複合コマンドは分割して判定:&& などで分割され、各サブコマンドがそれぞれルールに一致する必要があります
  • Read/Editのパスの書き方://path はファイルシステムのルートから、~/path はホームから、/path は設定ソースからの相対、./path は現在のディレクトリからの相対です

composer と php artisan:どれをallowし、どれをask/denyにするか

allowの候補は、読み取り寄りで何度も実行するコマンドです。Laravel公式では route:list を、アプリケーションで定義されたすべてのルートの概要を表示するコマンドとして紹介しています。Composer公式は validate を、composer.json をコミットする前に必ず実行すべきコマンドとしています。

askにしたいのは依存関係とDBを変えるコマンドです。Composer公式によると、require は composer.json に新しいパッケージを追加し、update は依存関係を最新にして composer.lock を更新します。どちらもレビュー対象の差分が生まれるので、毎回確認を挟む価値があります。

denyにしたいのはデータを消すコマンドです。Laravel公式は、migrate:fresh は「データベースのすべてのテーブルを削除してから migrate を実行する」、migrate:reset はすべてのマイグレーションをロールバックする、migrate:refresh は実質的にデータベース全体を作り直す、と説明しています。本番DBでは破壊的な操作の前に確認を求める仕組みがありますが、--force を付けると確認なしで実行されるとも書かれています。

「migrate」と「migrate:fresh」は別のルールになる

ここがLaravel特有の落とし穴です。前提の章の「末尾の * の前のスペースはルールの一部」という公式の記述を当てはめると、Bash(php artisan migrate *) は migrate の後にスペースが必要なので、php artisan migrate:fresh には一致しない、ということになります。:* は * と同等なので、Bash(php artisan migrate:*) と書いても同じです。

そのため、migrate 系をまとめて確認したい場合はスペースなしの migrate* にし、消したくない操作はサブコマンド名まで書いてdenyします。

もう1つ、呼び方の違いにも注意が要ります。Bash(php artisan migrate:fresh*) のようにプログラム名から書いたルールは、./vendor/bin/sail artisan migrate:fresh や docker compose exec app php artisan migrate:fresh には一致しません。公式の説明どおり、最初の * より前の部分は書かれたとおりに照合されるからです。そこで、artisanのルールは先頭に * を置いた Bash(* artisan migrate:fresh*) の形で書きます。この形なら、先頭の * が php にも ./vendor/bin/sail にも当たるので、直接の php artisan migrate:fresh も含めて1行で覆えます。composerだけは事情が違い、ランナーなしの行も必要です。理由は次のSailの章で説明します。

"ask": [
  "Bash(* artisan migrate*)",
  "Bash(composer require *)",
  "Bash(* composer require *)",
  "Bash(composer update *)",
  "Bash(* composer update *)"
],
"deny": [
  "Bash(* artisan migrate:fresh*)",
  "Bash(* artisan migrate:reset*)",
  "Bash(* artisan migrate:refresh*)"
]

評価はdenyが先なので、migrate:fresh はaskの * artisan migrate* にも一致しますが、denyで止まります。逆に Bash(php:*) や Bash(composer:*) のような広いallowは、公式の書式どおりに読むと、php artisan migrate:fresh や composer update を含むすべての呼び出しに一致します。deny・askを置いていない環境では確認なしで通るので、共有設定には書かないほうが安全です。

Laravel Sail と docker compose exec:コンテナ内実行は『ランナー+内部コマンド』で書く

Sailやdocker composeで開発しているチームでは、artisanやcomposerを直接呼ぶより、コンテナ経由で呼ぶことのほうが多いはずです。ここで3つの公式の記述を組み合わせます。

  • Laravel公式:Sailの中身は compose.yaml と sail スクリプトで、sail artisan・sail composer・sail php でコンテナ内のコマンドを実行します。さらに sail shell・sail root-shell では、コンテナ内で任意のシェルコマンドを実行できます
  • Docker公式:docker compose exec は「Composeのサービスを対象にした docker exec 相当」のコマンドで、実行中のコンテナでコマンドを実行します
  • Claude Code公式:開発環境ランナーはストリップされないため、Bash(devbox run *) のようなルールは run の後に続くもの(devbox run rm -rf . を含む)に一致します。ランナー内の作業を許可するには、ランナーと内部コマンドの両方を含むルールを、内部コマンドごとに書くよう推奨されています

この3つを合わせると、Bash(./vendor/bin/sail *) や Bash(docker compose exec *) をallowした場合、sail shell や、コンテナ内での任意のコマンドまで確認なしの対象に入る、という結論になります。これは公式の記述を組み合わせた推論で、実際に動かして確かめたものではありません。それでも、「sailは便利だから丸ごと許可」という書き方は避けたほうがよいと考えます。

書き方の例は次のとおりです。Sailのアプリケーションコンテナは、Laravel公式の説明では laravel.test サービスです。自前のcompose構成なら、自分たちのサービス名(例では app)に置き換えてください。

"allow": [
  "Bash(./vendor/bin/sail artisan test *)",
  "Bash(./vendor/bin/sail artisan route:list *)",
  "Bash(./vendor/bin/sail composer validate *)",
  "Bash(docker compose exec app php artisan test *)",
  "Bash(docker compose exec app php artisan route:list *)"
]

Laravel公式によると、sail test は sail artisan test と同じ動作です。ただしルールは文字列で一致させるので、両方の呼び方を使うならルールも両方必要です。同じ理由で、公式が案内している sail エイリアスを使う人と ./vendor/bin/sail と書く人がいれば、Claudeが書くコマンドの文字列も変わります。Bashルールは「Claudeが記述したコマンドテキスト」に一致するので、呼び方ごとにルールが要ります。

denyとaskは先頭の * で呼び方の違いを覆う

allowは呼び方ごとに狭く書きますが、denyとaskは逆に、呼び方の違いをまとめて覆いたいところです。公式によると、* はその場所にあるテキストの代わりになり、Bash(* --version) では * がプログラムの代わりになるので、node --version のような任意のプログラムに一致します。同じ考え方で Bash(* artisan migrate:fresh*) と書けば、先頭の * が php・./vendor/bin/sail・docker compose exec app php の部分の代わりになり、どの呼び方にも一致する形になります。composer章のコード例で、artisanのルールを先頭に * を置いた形だけにしたのは、このためです。

先頭に * を置くことについて、公式には起動時の警告の記述があります。公式のワイルドカードの説明では、Claude Codeは Bash(git * main) のようにサブコマンドの前に * を持つ許可(allow)ルールについて起動時に警告する、とされています。エラー一覧のページでは、この警告は設定の警告として ... has a wildcard before the rest of the command というメッセージで載っています。公式がこの警告の対象として書いているのはallowルールで、deny・askに先頭の * を書く場合についての記述はありません。

注意が要るのは、プログラム名がルールの * より後ろに来ていて、そのプログラムをランナーなしで直接呼ぶ場合だけです。たとえば Bash(* composer require *) に対して、Claudeが composer require を直接実行すると、コマンドの先頭がすぐ composer なので、先頭の * に当たる文字が何もありません。* が空の文字列にも一致するかは公式に明記がなく、この場合に一致するかは公式の記述からは判断できません。そのため、composerはランナーなしの呼び出し用に、プログラム名から書いた Bash(composer require *)・Bash(composer update *) を別に書いておくと確実です。

artisanにはこの問題がありません。直接呼ぶときも php artisan migrate:fresh のように artisan の前に php が付くので、Bash(* artisan migrate:fresh*) の * は php に当たります。空の文字列に一致する必要がないので、artisanは先頭に * を置いた1行で足ります。

aws CLI:読み取りだけ通し、変更系は確認する

aws CLIについては、公式ドキュメントにそのまま使える例があります。Bash(aws *) のような広いdenyは、Bash(aws s3 ls) のような狭いallowに一致する呼び出しも含めてすべてブロックします。allowでdenyの例外を作ることはできず、askとallowの関係も同じです。つまり「aws全体を禁止して、lsだけ通す」という書き方はできません。

そこで、共有設定ではawsのallowを書かず、変更系だけを名前でaskにします。たとえば aws s3 rm は、AWS公式で「S3オブジェクトを削除する」コマンドと説明され、--recursive を付けると指定したディレクトリやプレフィックス配下のすべてのファイル・オブジェクトが対象になります。

"ask": [
  "Bash(aws s3 rm *)"
],
"deny": [
  "Read(~/.aws/**)"
]

読み取り系(aws s3 ls など)を確認なしにしたい人は、各自のローカル設定でallowします。共有のaskは個人のallowより先に評価されるので、誰かがローカルで Bash(aws s3 *) を許可していても、aws s3 rm には確認が入ります。

認証情報のdenyは ~/ 形式で書きます。公式によると、ユーザー設定(~/.claude/settings.json)で / 始まりのパスを書くと ~/.claude/ 配下を指します。Read(/.aws/**) と書くと ~/.aws を守れないことになります。

もう1つ大事なのが、Bashルールはフルパスでの呼び出し(例:/bin/rm)や bash -c の中の呼び出しには一致しないことです。Read/Editのdenyも、サブプロセスが間接的にファイルを開く場合には効かないと公式に書かれています。コンテナの中から .env を読むケースもこれに当たると考えられます。クラウドへの操作を本当に止めたいなら、ローカルで使うIAMの権限そのものを絞ってください。OSレベルの制限が必要なら、公式が案内しているサンドボックスを組み合わせます。IAMまわりの基礎は、AWS認定アソシエイト3冠の攻略ガイドでも学習の流れを紹介しています。

共有設定と個人設定の分け方:settings.json / settings.local.json / managed settings

公式の設定スコープは次の4つです。

スコープファイル用途(公式の記述)
ユーザー~/.claude/settings.json個人設定、独自の権限ルール
共有プロジェクト.claude/settings.jsonチーム権限・hooks など。コミットして共有
プロジェクトローカル.claude/settings.local.json1プロジェクトの個人的なオーバーライド。Claude Codeが作成したときはgitから除外される
管理managed-settings.json などセキュリティポリシー・コンプライアンス要件

権限の設計で押さえたいのは、permissions.allow のようなリストは上書きではなくマージされることです。また、どのスコープのdenyでもallowより先に評価されます。個人のローカル設定でチームのdenyを消すことはできません。

これを踏まえて、Laravelチームでは次のように分けるのがよいと考えます。

  • 共有(settings.json):.env・secrets のRead deny、データを消すartisanのdeny(先頭に * を置いてランナー違いも覆う)、依存・DB・クラウドを変えるコマンドのask、チームで標準にしている実行形態(例:./vendor/bin/sail)の「ランナー+内部コマンド」allow
  • 個人(settings.local.json):エイリアスの sail や独自のcompose構成など、自分だけの呼び方に合わせたallow、aws の読み取り系allow
  • ユーザー(~/.claude/settings.json):Read(~/.aws/**) のように、全プロジェクトで守りたいホーム配下のdeny

組織として強制したい場合は管理設定を使います。公式の設定例では、管理設定の allowManagedPermissionRulesOnly: true で、ユーザー・プロジェクト・ローカル設定の権限ルールを無視させています。permissions.disableBypassPermissionsMode: "disable" ではバイパスモードを使えなくしています。後者は任意のスコープで機能するので、個人が自分の設定に入れて自分をロックアウトする使い方もできます。

『共有したのに効かない』ときの切り分け(チーム導入の注意点)

チームに設定を配ったあとに出やすい「効かない」の原因を、公式の記述から4つ挙げます。

  1. ワークスペース信頼がまだ:共有 .claude/settings.json の permissions.allow は、各メンバーがそのフォルダのワークスペース信頼ダイアログを受け入れるまで適用されません。denyとaskは制限だけを行うので、すぐに適用されます。「denyは効くのにallowだけ効かない」ときは、まずここを疑います
  2. 「はい、今後は聞かない」を押したのにまた聞かれる:この選択はローカルファイルにallowとして保存されます。そのallowは、プロジェクトや管理設定のaskより優先されません。チームで * artisan migrate* をaskにしていれば、個人が何度承認しても確認は出続けます。これは設計どおりの動きです
  3. defaultModeを共有で配ったのに変わらない:permissions.defaultMode の auto と bypassPermissions は、プロジェクト設定やローカル設定からは有効になりません。ユーザー設定か管理設定に書くか、セッションごとに --permission-mode を渡します(公式によると、v2.1.257より前は bypassPermissions がどのファイルからでも有効でした)
  4. そもそも読み込まれていない:/status の Setting sources 行で、読み込まれた設定ファイルを確認できます。共有の .claude/settings.json はセッションのプライマリ作業ディレクトリから読み込まれるので、リポジトリルートで起動しているかも確認します

まとめ:Laravelチーム向け導入チェックリスト

  • 共有:.env・secrets のRead deny、migrate:fresh・migrate:reset・migrate:refresh のdeny(migrate * ではなくサブコマンド名まで書く。先頭に * を置いた Bash(* artisan migrate:fresh*) の形にすると、php artisan/sail artisan/docker compose exec … php artisan を1行で覆える)
  • 共有:migrate*・composer require・composer update・aws s3 rm などのask(artisanは Bash(* artisan migrate*) の1行。composerは直接呼ぶと先頭の * に当たる文字がなく、空の文字列への一致は公式に明記がないため、Bash(* composer require *) とランナーなしの Bash(composer require *) の両方を書く)
  • 共有:Bash(./vendor/bin/sail *)・Bash(docker compose exec *)・Bash(php:*) のような広いallowは書かない
  • 個人:自分の実行形態に合わせた「ランナー+内部コマンド」のallow、awsの読み取り系allow
  • ユーザー:Read(~/.aws/**) を ~/ 形式でdeny
  • 組織:必要なら管理設定で allowManagedPermissionRulesOnly・disableBypassPermissionsMode を強制
  • 導入後:メンバーにワークスペース信頼を案内し、/status で読み込みを確認

権限ルールは、コマンドの文字列でClaudeの通常の呼び出しを制御する仕組みです。本当に守りたいもの(本番DB・AWSの本番アカウント)は、IAMや接続先の分離で守るのが前提になります。その土台として、Laravel・Docker・AWSを体系的に学べる技術書が1冊手元にあると、どのコマンドが何を変えるかを判断しやすくなります。

Laravelの技術書をAmazonで探す場合はこちらです。

Amazonで「Laravel 本」を探す

楽天ポイントを使いたい人は、楽天市場からも技術書を探せます。

楽天市場で「Laravel 本」を探す

フリーランスエンジニア向けのツール・サービスは、フリーランスエンジニアが実際に使っているツール・サービス全一覧にまとめています。

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