SELinux のアクセス拒否を調べる:AVC 記録とファイルラベルの修復
Web サイトのファイルを元のディレクトリから /srv に移したところ、httpd が 403 を返すようになった。ファイルは読み取り可能で、サービスも動作している。この状況で、安易にパーミッションを広げたり SELinux を無効にしたりしてはいけません。通常のファイルパーミッション、サービスのアクセス許可設定、強制アクセス制御は、それぞれ独立した三つの検査層です。本記事では、AlmaLinux 9 上に構成済みの httpd テスト用仮想ホストを使い、失敗したリクエストと AVC 記録を対応付け、永続的なラベル設定を修復したうえで、サービスの動作とアクセス範囲を検証します。
一、テスト範囲と初期状態を確定する
CloudDDR の物理サーバーでは AlmaLinux 9 を選択できます。支払い後、コンソールで認証情報を確認し、SSH で接続します。アドレスが複数ある場合は、最初のアドレスを使用してください。以下の操作には管理者権限が必要です。隔離されたテスト環境でのみ実施し、既存の業務用ディレクトリを上書きしたり、待ち受けアドレスやファイアウォールを変更したりしないでください。
httpd がインストール済みで、テスト用仮想ホストとアクセス先が設定されていることを前提とします。まず、その仮想ホストの設定を保存し、元の DocumentRoot を記録します。調査ツールが不足している場合は該当パッケージをインストールし、SELinux の動作モードと監査サービスを確認します。
sudo dnf install policycoreutils-python-utils
getenforce
sestatus
sudo systemctl status auditd --no-pager
sudo semanage fcontext -l -C
sudo semanage permissive -l
想定する状態は、システム全体が Enforcing であり、httpd が動作するドメインも個別に permissive に設定されていないことです。監査サービスが正常に動作していなければ、まず証拠を収集できる状態に戻してください。「ログに拒否記録がない」ことは、アクセスが制限されていない証明にはなりません。ローカルのラベルマッピング一覧も保存しておき、既存のルールと今回追加するルールを後で区別できるようにします。
setenforce 0は修復ではありません。強制チェックを無効にした後でリクエストが成功しても、調査の手掛かりになるだけです。enforcing に戻した後も動作することを証明したわけではありません。
二、通常のパーミッションと設定の誤りを先に切り分ける
専用のテストディレクトリを作り、内容は検証用の目印一行だけにします。ディレクトリにはパスをたどるための実行権限、ファイルには読み取り権限を与えます。この権限設定は公開用のテストコンテンツに限るもので、鍵、バックアップ、業務データには使用しません。
sudo install -d -m 0755 /srv/selinux-lab
printf 'selinux-lab-ok\n' | sudo tee /srv/selinux-lab/index.html
sudo chmod 0644 /srv/selinux-lab/index.html
namei -l /srv/selinux-lab/index.html
ls -ldZ /srv/selinux-lab
ls -lZ /srv/selinux-lab/index.html
既存のテスト用仮想ホスト内で、ドキュメントルートと対応するディレクトリのアクセス許可を次のように変更します。
DocumentRoot "/srv/selinux-lab"
<Directory "/srv/selinux-lab">
AllowOverride None
Options -Indexes
Require all granted
</Directory>
変更前に設定ファイルの読み込み関係を確認し、別のディレクトリセクションがアクセス許可を上書きしないようにします。httpd -t で構文を検査し、httpd -S でリクエストを処理する仮想ホストを確認します。検査に問題がないことを確認してから reload してください。
シェル変数 CHECK_URL に実際のテスト用 URL を設定し、パスは /index.html を指すようにします。この URL で対象の仮想ホストにアクセスできる必要があります。URL に認証情報を含めないでください。リクエスト時は、接続できたかどうかだけでなく、レスポンス本文を確認します。
sudo httpd -t
sudo httpd -S
sudo systemctl reload httpd
curl --fail --show-error "$CHECK_URL"
失敗した場合は、まず httpd のエラーログを読みます。親ディレクトリの実行権限不足、別の仮想ホストへのアクセス、アクセス許可ルールによる拒否でも、似た症状が起こります。この段階で再帰的にパーミッションを緩めても、ラベルの問題は解決せず、公開範囲を広げるだけです。
三、AVC から主体・対象・操作を特定する
リクエストを実行した時刻を記録し、失敗する操作を一度だけ再現してから、同じ時間帯の拒否記録を検索します。
sudo ausearch -m AVC,USER_AVC -ts recent -i
ps -eZ | grep '[h]ttpd'
matchpathcon -V /srv/selinux-lab/index.html
AVC では、scontext、tcontext、tclass、拒否された権限に注目します。scontext はアクセスを試みたプロセスのドメイン、tcontext は対象オブジェクトのセキュリティコンテキストを示します。ファイルの読み取りとディレクトリの検索は別の操作なので、プロセス名だけで判断してはいけません。
記録内のパス、プロセス、時刻を、失敗したリクエストと対応付けます。ディレクトリの検索が拒否されている場合は、パスを構成する各階層のディレクトリも確認してください。末端のファイルだけを修復しても、効果がない場合があります。ログにパス情報がない場合は、監査イベントの識別番号を使って、同じイベントに属する他の記録も調べます。
AVC がないからといって、SELinux が無関係だと断定はできません。まず検索対象の時間帯、監査の稼働状態、サービスが実際に動作しているプロセスドメインを確認します。それでも関連する証拠がなければ、アプリケーション設定と通常のパーミッションの調査に戻り、根拠なく許可ルールを追加しないでください。過去の拒否記録をすべてポリシー生成ツールに渡すことも避けます。他の業務に由来する記録が含まれている可能性があるためです。
四、Web コンテンツのラベルを永続的に修復する
httpd が読み取る通常の静的コンテンツには、既存のポリシーで用途に応じたタイプ httpd_sys_content_t が用意されています。テスト用パスのラベルが用途に合っていないと証拠から判断できた場合に限り、対象を絞ったマッピングを追加します。
sudo semanage fcontext -a -t httpd_sys_content_t \
'/srv/selinux-lab(/.*)?'
sudo restorecon -Rv /srv/selinux-lab
matchpathcon -V /srv/selinux-lab/index.html
ls -lZ /srv/selinux-lab/index.html
まったく同じ式がすでに存在する場合は、既存のルールを確認し、必要に応じて -m で変更します。ルールを重ねて追加しないでください。式の対象はテストディレクトリだけとし、/srv 全体を Web コンテンツ用のタイプに変更しません。semanage はパスとタイプの対応を保存し、restorecon が期待されるコンテキストを既存のファイルに適用します。
chcon は現在のラベルを直接変更しますが、永続的なマッピングは作成しないため、この手順の代わりにはなりません。後でラベルを再設定すると、一時的な変更は元に戻る可能性があります。
静的コンテンツのディレクトリには、サービスからの書き込みは必要ありません。一度の拒否を解消するために、すべてのコンテンツを書き込み可能なタイプへ変更したり、無関係なブール値をついでに有効化したりしないでください。アップロードやキャッシュなどで実際に書き込みが必要な場合は、専用のディレクトリを分け、必要な操作に合わせて権限とラベルを個別に設計します。
五、復旧結果を検証し、切り戻し手順を確保する
enforcing を維持したまま同じ URL に再度アクセスし、本文に selinux-lab-ok が含まれることを確認します。同時に、そのリクエストに関連する新しい AVC が発生していないか調べます。その後、もう一度 restorecon を実行してテスト用サービスを再起動し、リクエストを繰り返します。これにより、修復が一時的なラベルや再起動前のプロセス状態に依存していないことを検証します。
getenforce
sudo restorecon -Rv /srv/selinux-lab
sudo systemctl restart httpd
curl --fail --show-error "$CHECK_URL"
sudo ausearch -m AVC,USER_AVC -ts recent -i
検証には、許可されない動作の確認も含めます。存在しないファイルへのリクエストは失敗すること、テストディレクトリでディレクトリ一覧が表示されないこと、サービスに想定外の待ち受けが追加されていないこと、ローカルのラベル変更が合意したパスだけを対象としていることを確認してください。これらはそれぞれ、アプリケーションの動作、設定、変更範囲を検証するもので、互いに代用はできません。
実験を終える際は、まず元の仮想ホスト設定を復元し、構文検査に通ったことを確認して reload します。その後、テストディレクトリを削除します。今回新たに追加したことが確認できるマッピングは削除できます。既存のマッピングを変更した場合は、事前に記録したタイプへ戻してください。後片付けが済んだら、残すパスに対して restorecon を実行します。
最後に、失敗したリクエスト、関連する AVC、変更前後のラベル、マッピングの式、再起動後のレスポンス結果を記録として残します。一度リクエストが成功しただけでは、機能が復旧したことしか確認できません。動作モードを変更していないこと、マッピングからラベルを再現できること、アクセス範囲を広げていないことまで確認して、初めて修復の検証が完了します。
よくある質問
HTTP 403 が返れば SELinux が原因ですか?
断定できません。ディレクトリの通過権限、ファイルの読み取り権限、httpd の認可設定を確認し、失敗したリクエストと同じ時間帯の AVC 記録を対応付ける必要があります。
chcon や許可ポリシーの自動生成では不十分ですか?
chcon の変更は restorecon で戻る場合があります。通常の公開ファイルには semanage fcontext と restorecon を使います。許可ポリシーを生成する前に、ラベル、パス、既存のポリシーブール値を確認します。
AMD EPYC 7543P 搭載の専用物理サーバー。月額$447から。