chmod しても Permission denied のまま?原因を切り分ける
chmod を変更しても Permission denied が消えないとき、原因はファイル自身とは限りません。親ディレクトリ、所有者、マウント、SELinux、ACL、特殊ビットを順に確認する方法を説明します。
権限を確認し、焦って chmod 777 まで試したのに、シェルはこう返す。
-bash: ./deploy.sh: Permission denied
この状況でさらにモードを広げても、原因を外すことが多いものです。ファイルのモードはアクセス判定の一層にすぎません。親ディレクトリ、所有者、マウント、追加のセキュリティポリシーが止めているかもしれません。
755 や 644 の意味から確認したい場合は、chmod 755 とは何かを先に読んでください。ここでは、モードが正しく見えるのに足りない理由を調べます。
まず変更せずに見る
最初に確認するのは、ファイル自身が止めているのか、周辺が止めているのかです。chmod 777 を診断に使うべきではありません。状態を変えるうえ、実際に失敗しているアカウントの条件を再現できないからです。まずパス全体を見ます。
namei -l /home/alice/app/deploy.sh
namei -l は / から対象までの各要素について、モード、所有者、グループを表示します。失敗しているユーザーまたはサービスアカウントでの id と、対象の ls -l を合わせれば、多くの場合は何も変更せず原因を絞れます。namei は util-linux のコマンドです。macOS では各親ディレクトリに ls -ld を実行してください。
chmod 777 を「試し」に使わないでください。chmod u+rwx は所有者のビットしか変えず、自分が所有者でない場合は答えになりません。sudo での実行も、root が通れるかしか示さず、実際のユーザーやサービスが通れるかは分かりません。
原因 1:ファイルではなくパスにある
/home/alice/app/deploy.sh を開くには、/、/home、/home/alice、/home/alice/app のすべてに x、つまり通過権限が必要です。どこか一つでも欠けると、その下のファイルのモードを見る前に拒否されます。
ls -ld / /home /home/alice /home/alice/app
other に通過を許可すべき場合だけ、次のようにします。
chmod o+x /home/alice
o+x は中身の一覧表示を許さず、通過だけを許可します。共有環境では、適切なグループへ g+x を付ける方が狭い権限で済むことがよくあります。
もう一つ重要なのは、操作ごとに見る場所が違う点です。既存ファイルを読む/実行するには、ファイル自身の権限と各親ディレクトリの x が必要です。一方で作成、削除、名前変更は親ディレクトリで決まり、親に w+x が必要です。対象ファイル自身が書き込み可能かどうかは、削除できるかには関係しません。
| 操作 | 必要な確認 |
|---|---|
| 既存ファイルを開く、読む、実行する | ファイルの権限と、すべての親ディレクトリの x |
| 項目を作る、削除する、名前変更する | 親ディレクトリの w+x |
| ディレクトリの項目を一覧し、扱う | ディレクトリの r+x |
rm、mv、新規作成での Permission denied は、対象ファイルではなく親ディレクトリを疑うべきことが多いです。削除には sticky bit という追加条件もあります。
原因 2:所有者またはグループが違う
権限は所有者・グループ・その他のどれに当たるかで決まります。どれが適用されるかは、利用者の UID、所属グループ、ファイルの所有者とグループで決まります。
ls -l deploy.sh # -rwxr-x--- 1 root staff ...
id # uid=1000(alice) gid=1000(alice) groups=1000(alice)
この例ではファイルは root:staff、モードは 750 で、Alice は staff にいません。Alice は「その他」として扱われ、750 では何も許可されません。chmod は各クラスに何を許可するかを変えるだけで、所有者、グループ、所属グループは変えません。
id -nG alice # alice が本当に所属するグループを確認する
sudo chown alice deploy.sh # alice を所有者にする、または
sudo chgrp developers deploy.sh # alice が developers に所属する場合だけ有効
sudo usermod -aG staff alice # alice を staff に追加し、再ログインする
ユーザーが所属していないグループへファイルを移しても意味はありません。ここは 777 で開くのではなく、所有者とグループを正しくそろえる場面です。
原因 3:ファイルシステムが拒否している
マウントオプションはモードより強く働くことがあります。代表例は二つです。
noexec:実行ビットがあっても、そのマウント上のファイルを直接実行できません。強化された一時ディレクトリ、リムーバブルメディア、コンテナなどで見かけます。./scriptは拒否されますが、bash ./scriptのようにインタープリタに読ませる操作は別です。ro:読み取り専用マウントです。ファイルにwがあっても書き込めません。
findmnt -T /tmp/deploy.sh
# → TARGET SOURCE FSTYPE OPTIONS
# /tmp tmpfs tmpfs rw,nosuid,nodev,noexec
noexec が原因なら、実行可能なファイルシステムへ移すか、管理できる環境でだけマウント設定を変更します。macOS では引数なしの mount でマウント情報を確認できます。
原因 4:SELinux または AppArmor
RHEL、Fedora 系では SELinux がセキュリティコンテキストに基づく追加のアクセス制御を行います。誰でも読めるモードでも、コンテキストが不適切なら Web サーバーには拒否されます。サービスだけが失敗し、シェルでは成功し、通常のモードが正しく見える場合は疑ってください。
getenforce
ls -Z deploy.sh
sudo ausearch -m avc -ts recent
AVC 拒否が出ていれば、モードは本筋ではありません。既定ラベルを期待するパスなら、次で戻せます。
sudo restorecon -v /var/www/html/deploy.sh
Ubuntu や SUSE では AppArmor が使われることがあります。sudo aa-status と、カーネルログの apparmor="DENIED" を確認します。どちらも chmod ではなくポリシーの調査です。
原因 5:ACL が三つの権限だけでは説明できない
chmod は所有者・グループ・その他の三分類を変えますが、ファイルには POSIX ACL による追加のユーザー/グループ規則が付く場合があります。ls -l の末尾の + が目印です。
ls -l report.csv # -rw-r-----+
getfacl report.csv
ACL があると、単純な三分類だけでは不十分です。追加エントリが個別のアクセスを与える一方、ACL mask は名前付きユーザー、名前付きグループ、所有グループの実効権限を制限します。group::rwx と表示されても mask::r-- なら、実際の権限は r-- です。さらに ls -l のグループ欄の三文字は mask を示すため、所有グループのエントリそのものではない場合があります。まず getfacl の実効権限を確認し、必要な項目だけを修正してください。
setfacl -m u:alice:rx report.csv
setfacl -b を定番の修復方法にしてはいけません。意図して設定された拡張 ACL まで消してしまいます。
原因 6:特殊ビットとファイルシステム属性
モードが無視されているのではなく、意味を取り違えている場合もあります。
- sticky bit:
/tmpの1777のような設定です。共有ディレクトリの項目を削除、名前変更できるのは、その項目の所有者、ディレクトリの所有者、または特権ユーザーだけです。 - Linux の setuid スクリプト:インタープリタスクリプトの setuid/setgid はカーネルに無視されます。
4755 install.shは、setuid バイナリのように所有者として実行されません。 - immutable 属性:対応する Linux ファイルシステムでは、immutable 属性が書き込みや多くのメタデータ変更を止めます。chmod には見えません。
lsattr deploy.sh
sudo chattr -i deploy.sh
macOS に chattr はありません。chflags の uchg/schg や、システムパスの System Integrity Protection が近い仕組みです。
Permission denied ではないこともある
bad interpreter: No such file or directoryは壊れた shebang、または#!行の Windows CRLF が原因です。- ディスク容量または inode の枯渇は通常
No space left on deviceと表示されます。dfとdf -iを確認してください。 root_squashを有効にした NFS では、リモート root が非特権ユーザーへ写されるため、sudoでも書き込みを拒否されることがあります。これはエクスポートポリシーの問題です。
手動実行では動くのに cron だけ失敗するなら、別の環境問題です。cron ジョブが実行されなかった理由を参照してください。
診断チェックリスト
- 失敗するアカウントとして確認する。
namei -l /完全なパス、id、ls -lを使い、まずchmod 777はしません。 - パスをたどる。 祖先ディレクトリの
x欠落はすべてを止めます。作成、削除、名前変更には親のw+xも必要です。 - 所有者とグループを照合する。 所有者でもグループのメンバーでもなければ、モードを広げる前に
chown、chgrp、グループ所属を直します。 - マウントを確認する。
findmnt -T <path>。noexec は直接実行を、ro は書き込みを止めます。 - セキュリティモジュールを確認する。 SELinux は
getenforce/ls -Z、AppArmor はaa-statusとログです。 - ACL を確認する。
ls -lの+を見たらgetfacl、特に mask を確認します。 - 特殊な制約を確認する。 sticky bit、setuid スクリプト、immutable 属性を見ます。
この順番で見れば、本当のゲートは見つかります。多くの場合、それは chmod だけでは直せない場所です。モード自体の意味を確認したいときは、chmod 計算機を使ってください。原因を取り除いたあと、最終的にどのモードを残すか——ツリー全体に適用しても全ファイルが実行可能にならない方法も含めて——はchmod 644 と 755 の選び方で扱っています。