こんにちは、インフラエンジニアのryuです。
RHELやRocky Linux、AlmaLinuxなどのRHEL系のサーバーで、こんな状況に出会ったことはないでしょうか。ファイルの権限は 644、所有者も合っている。それなのに、Webサーバーがそのファイルを読めずにエラーを返す。ディレクトリを丸ごとコピーし直しても変わらない。
この場合、原因はファイルの権限ではなく、SELinux(Security-Enhanced Linux)による拒否であることがよくあります。SELinuxは、通常のパーミッションとは別の仕組みでアクセスを判定しているので、chmod をいくら直しても結果は変わりません。
検索すると「setenforce 0 で直った」という情報がたくさん出てきます。たしかに症状は消えますが、それはSELinuxの保護を止めただけで、原因は残ったままです。この記事では、拒否が本当にSELinuxによるものかを記録から確かめ、原因に合った方法で直す手順を紹介します。
対象は、SELinuxが有効なRHEL 9系のサーバーです。コマンドの多くは root 権限で実行します。
SELinuxの拒否は、普通のPermission deniedと何が違うのか?¶
Linuxの基本的なアクセス制御は、ファイルの所有者・グループ・その他に対する読み書き実行の権限です。この仕組みはLinuxのファイル権限の解説で詳しく説明しています。
SELinuxは、この判定の後にもう1つ別の判定を加えます。プロセスとファイルのそれぞれに「ラベル」と呼ばれる印が付いていて、ポリシーに書かれた組み合わせだけを許可するという考え方です。
両者の違いを表にまとめます。
| 項目 | 通常のパーミッション | SELinux |
|---|---|---|
| 判定に使う情報 | 所有者・グループ・rwx | プロセスとファイルのラベル(タイプ) |
| 確認するコマンド | ls -l |
ls -Z、ps -eZ |
| 拒否の記録 | 多くの場合、アプリのエラーだけ | 監査ログに AVC として残る |
| root 権限で動くサービス | 原則として通る | ポリシーで許可されていなければ拒否される |
最後の行がポイントです。SELinuxが有効な環境では、Webサーバーのように決められたタイプで動くサービスは、root権限を持っていてもポリシーで許されていないアクセスを拒否されます。だからこそ、Webサーバーが乗っ取られても被害を広げにくくなっています。
ls -Z を実行すると、ファイルのラベルが表示されます。
ls -Z /var/www/html/index.html
出力の中の httpd_sys_content_t のような _t で終わる部分が「タイプ」です。Apacheのプロセスは httpd_t というタイプで動いていて、ポリシーでは httpd_t が httpd_sys_content_t のファイルを読むことを許可しています。逆に、ホームディレクトリ用の user_home_t が付いたファイルは読めません。
権限が正しいのに読めないときは、このタイプの組み合わせが合っていない可能性が高いです。
まず、SELinuxが原因かどうかを確かめる¶
いきなり設定を変える前に、SELinuxが本当に拒否しているのかを確かめます。原因がほかにあるのにSELinuxの設定を触ると、かえって状況が分かりにくくなるからです。
getenforceで今のモードを見る¶
最初に、SELinuxがどのモードで動いているかを確認します。
getenforce
結果は Enforcing・Permissive・Disabled のどれかです。それぞれの意味は次のとおりです。
| モード | 動き |
|---|---|
| Enforcing | ポリシーに反するアクセスを拒否し、記録する |
| Permissive | 拒否はせず、記録だけ残す |
| Disabled | SELinuxが動いていない |
Disabled なら、SELinuxは原因ではありません。通常の権限や、アプリケーションの設定を調べ直します。権限まわりの基本的な切り分けはファイル削除時のPermission deniedの記事も参考になります。
Enforcing のときは、次の手順で拒否の記録を探します。なお、Amazon Linux 2023のように、SELinuxが有効でも初期状態が Permissive の環境もあります。この違いはAmazon Linux 2023への移行で変わる点の記事で触れています。
ausearchで拒否の記録を探す¶
SELinuxの拒否は、監査デーモン(auditd)によって /var/log/audit/audit.log に記録されます。ファイルを直接 grep してもよいのですが、ausearch を使うと時間や種類で絞り込めて便利です。
ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts recent
-m は記録の種類、-ts recent は直近10分を意味します。エラーを再現した直後に実行すると、関係のある行だけが見つかりやすくなります。今日1日分を見たいときは -ts today にします。
何も出てこないときは、auditd が動いているかを確認します。
systemctl status auditd
auditd が止まっていると、拒否が起きても記録されません。起動してから、もう一度エラーを再現させて確認します。
AVCの行の読み方¶
拒否の記録は、次のような形をしています。ここに載せるのは説明用の例で、実測ではありません。
type=AVC msg=audit(...): avc: denied { read } for pid=... comm="httpd" name="index.html" ... scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
長いですが、読むところは限られています。
| 部分 | 意味 |
|---|---|
denied { read } |
拒否された操作(ここでは読み込み) |
comm="httpd" |
拒否されたプロセスの名前 |
name="index.html" |
対象のファイルやディレクトリの名前 |
scontext=...httpd_t... |
アクセスしようとしたプロセスのラベル |
tcontext=...user_home_t... |
アクセスされたファイルのラベル |
tclass=file |
対象の種類(file・dir・tcp_socket など) |
permissive=0 |
0 なら実際に拒否された、1 なら記録だけ |
この例なら、「httpd_t のプロセスが、user_home_t のラベルが付いたファイルを読もうとして拒否された」と読めます。ファイルのラベルが Web 用になっていないことが原因だと見当がつきます。
sealertで解説を読む¶
setroubleshoot-server パッケージを入れると、sealert というツールで拒否の記録を分かりやすく説明してもらえます。
dnf install setroubleshoot-server
sealert -a /var/log/audit/audit.log
sealert は、拒否ごとに考えられる原因と対処の候補を英語で出してくれます。Red Hat のドキュメントでも、多くの場合は sealert の提案が正しい直し方を示すとされています。ただし、提案が「audit2allow でルールを追加する」だけのときは、後で説明するように慎重に判断します。
原因1:ファイルのラベルが合っていない¶
実務でいちばん多いのが、ファイルのラベルの不一致です。よくあるのは次の2つの場面です。
1つ目は、ホームディレクトリなどで作ったファイルを mv で Web 用のディレクトリに移した場合です。mv は元のラベルをそのまま持っていくので、user_home_t のまま /var/www/html に置かれてしまいます。cp で新しく作ったファイルは、通常はコピー先に合ったラベルになりますが、-a などで属性を保ったままコピーすると元のラベルが残ることがあります。
2つ目は、/srv/web のように標準とは違う場所をドキュメントルートにした場合です。その場所には Web 用のラベルの規則が無いので、Apache から読めません。
restoreconで標準のラベルに戻す¶
1つ目の場面なら、restorecon でポリシーが決めている標準のラベルに付け直します。まず -n を付けて、何が変わるかだけを確認します。
restorecon -Rnv /var/www/html
-R は再帰、-n は実際には変更しない、-v は変更内容を表示する、という意味です。変わる予定の行を確認して問題がなければ、-n を外して実行します。
restorecon -Rv /var/www/html
実行後に ls -Z でタイプが httpd_sys_content_t になっていることを確かめ、エラーを再現させて解消したかを見ます。
semanage fcontextで新しい場所の規則を足す¶
2つ目の場面では、restorecon だけでは直りません。その場所に付けるべきラベルの規則が、まだポリシーに無いからです。semanage fcontext で規則を追加してから、restorecon で反映させます。
semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"
restorecon -Rv /srv/web
semanage は規則を登録するだけで、ファイルのラベルそのものは変えません。そのため、2行目の restorecon を忘れると何も変わらない点に注意してください。semanage コマンドは policycoreutils-python-utils パッケージに含まれています。
chcon でもラベルは変えられますが、restorecon を実行したときやファイルシステムの再ラベル付けのときに元に戻ってしまいます。恒久的に使う場所は、semanage fcontext で規則として登録しておくほうが安全です。
原因2:ブール値がオフになっている¶
ファイルのラベルは正しいのに拒否されるときは、SELinuxの「ブール値」を確認します。ブール値は、よく使われる動きをポリシーの書き換えなしでオン・オフできるスイッチです。
たとえば、Apache をリバースプロキシとして使い、裏のアプリケーションサーバーにつなぐ構成では、Apache から外へのネットワーク接続が必要になります。この許可は httpd_can_network_connect というブール値で管理されています。データベースへの接続には httpd_can_network_connect_db があります。
今の値は getsebool で確認できます。
getsebool -a | grep httpd_can_network
必要なものだけをオンにします。-P を付けると、再起動後も設定が残ります。
setsebool -P httpd_can_network_connect_db on
ブール値は名前が似ているものが多いので、AVC の記録や sealert の提案を見て、本当に必要なものだけを選ぶようにします。「とりあえず関係ありそうなものを全部オンにする」と、守りが必要以上に弱くなります。
原因3:標準以外のポートを使っている¶
Web サーバーを 9876 のような標準とは違うポートで待ち受けさせると、起動に失敗することがあります。SELinuxは、どのタイプのプロセスがどのポートを使えるかも管理しているからです。
この場合、AVC の記録では tclass=tcp_socket となり、name_bind(ポートを開く操作)が拒否されています。ファイルのラベルの話ではないので、semanage fcontext では直りません。
まず、Web 用として登録されているポートを確認します。
semanage port -l | grep -w http_port_t
使いたいポートが一覧に無ければ、追加します。
semanage port -a -t http_port_t -p tcp 9876
サービスが起動しないときの全体的な調べ方は、systemdのサービスが起動しないときの記事にまとめています。journalctl でアプリケーション側のエラーを見て、同じ時刻の AVC の記録と突き合わせると、原因をはっきりさせやすくなります。
setenforce 0とaudit2allowを使うときの注意点¶
ここまでの3つで、多くの拒否は解決します。最後に、検索するとよく出てくる2つの方法の扱い方を整理しておきます。
setenforce 0 は、SELinuxを一時的に Permissive にするコマンドです。再起動すると元に戻ります。「SELinuxを止めたら直るか」を確かめる切り分けには使えますが、直し方ではありません。 確認が終わったら setenforce 1 で戻し、原因に合った対処をします。
設定ファイルで SELinux を Disabled にするのはさらに勧められません。Red Hat のドキュメントによると、Disabled から有効に戻すときにはファイルシステム全体のラベルの付け直しが必要になり、その間は時間がかかります。止めている間に作られたファイルには正しいラベルが付かないためです。
audit2allow は、拒否の記録から「その操作を許可するポリシーのモジュール」を作るツールです。手軽ですが、記録に残った操作をそのまま許可するので、本来は拒否すべきアクセスまで通してしまうおそれがあります。Red Hat のドキュメントでも、ラベルの修正や設定の調整で直らない場合に限って使い、作られた内容を確認してから適用するよう案内されています。
もう1つ知っておきたいのが、記録に残らない拒否です。ポリシーには、よく起きる無害な拒否を記録しない dontaudit という規則があります。AVC が何も見つからないのに SELinux が怪しいときは、次のコマンドで一時的に dontaudit を外して記録させられます。
semodule -DB
調べ終わったら、semodule -B で元に戻します。そのままにすると、無害な記録でログがあふれます。
うまくいかないときの確認ポイント¶
ここまでの流れを、症状ごとに表にまとめておきます。手順に迷ったときの確認用に使ってください。
| 症状 | 見るところ | 主な対処 |
|---|---|---|
ファイルが読めない(tclass=file) |
ls -Z のタイプ |
restorecon、semanage fcontext |
| 別のサーバーにつなげない | getsebool -a |
setsebool -P |
ポートを開けない(name_bind) |
semanage port -l |
semanage port -a |
| AVC が何も出ない | auditd の状態、dontaudit |
systemctl status auditd、semodule -DB |
restorecon しても変わらない |
semanage fcontext -l の規則 |
規則を追加してから restorecon |
ログの基本的な読み方に不安がある方は、ログの読み方入門から読むと、監査ログの見方もつかみやすくなります。
InfraAcademyで、Linuxの権限まわりを体系的に学び直す¶
SELinuxのトラブルは、SELinuxだけを覚えても解けないことが多いです。ファイルの権限、プロセス、サービスの管理、ログの読み方がつながって、初めて「どこで止まっているか」が見えてきます。
InfraAcademy のLinuxロードマップでは、権限やユーザー管理、systemd とログの扱いといった基礎を順番に学べるようにしています。現場で出会ったエラーをきっかけに、土台から学び直したい方はのぞいてみてください。
まとめ¶
権限も所有者も正しいのに Permission denied になるときは、SELinuxの拒否を疑います。まず getenforce でモードを確認し、ausearch で AVC の記録を探して、どのプロセスが何に拒否されたのかを読みます。
原因の多くは、ファイルのラベル・ブール値・ポートの3つのどれかです。それぞれ restorecon と semanage fcontext、setsebool -P、semanage port で、SELinuxを止めずに直せます。setenforce 0 は切り分けに使うだけにとどめ、audit2allow は最後の手段にしておきましょう。
次に Permission denied に出会ったら、chmod の前に ls -Z を1回打ってみてください。それだけで、調べる方向がかなり絞れるはずです。
参考¶
- Using SELinux - Changing SELinux states and modes(Red Hat Enterprise Linux 9)
- Using SELinux - SELinux 拒否メッセージの分析(Red Hat Enterprise Linux 9)
- Using SELinux - Troubleshooting problems related to SELinux(Red Hat Enterprise Linux 10)
- Using SELinux - Fixing analyzed SELinux denials(Red Hat Enterprise Linux 10)
- Troubleshooting Problems Related to SELinux(Fedora Docs)



