本番環境におけるSELinuxアクセス拒否の対処アプローチ比較
深夜2時、監視システムからアラートが発報されます。Rocky Linux 9上に新規デプロイしたNginxリバースプロキシがカスタムディレクトリから静的配信を行えず、403 Forbiddenや500 Internal Server Errorを返しています。従来のPOSIXパーミッションを確認しても、所有者はnginx:nginx、権限は0755で何の問題もありません。それにもかかわらず、デーモンはファイルにアクセスできません。この原因は、SELinux(Security-Enhanced Linux)による強制アクセス制御(MAC: Mandatory Access Control)の制限にあります。
障害発生時、エンジニアがSELinuxの拒否に対処する際にとるアプローチは主に以下の4つです。
- アプローチ1: SELinuxの無効化(
setenforce 0または/etc/selinux/configの編集) — ホスト全体のMAC保護をすべて無効化する高リスクな近道です。エラーは即座に解消されますが、OSが無防備な状態に晒されます。 - アプローチ2: SELinuxブール値(Boolean)の切り替え(
setsebool -P) — Red Hatによって事前設定されたカーネルレベルのスイッチを切り替えます。リバースプロキシからポート8080のバックエンドソケットへの接続や、ポート5432の外部PostgreSQLインスタンスへのアクセスなど、一般的なワークフローを安全に許可できます。 - アプローチ3: セキュリティコンテキストの再ラベリング(
semanage fcontext+restorecon) — 永続的なファイルおよびディレクトリのラベルを更新し、デーモンが非標準パス(/data/web/など)のファイル群にアクセスできるようにします。 - アプローチ4: カスタムポリシーモジュールの生成(
audit2allow) — AVC(Access Vector Cache)拒否ログを解析し、独自のデーモンや非標準のシステムコール向けに専用のType Enforcementモジュール(.pp)をコンパイルします。
秒間4,000リクエスト以上を処理する高トラフィックなRocky Linux 9ノードにおいて、SELinuxを無効化することは、コマンド1つでSOC2やPCI-DSSのコンプライアンス要件を損なう行為です。体系的なトラブルシューティングワークフローを実施することで、根本原因を特定し、最小限の適切な修正を適用して、本番環境のセキュリティ境界を安全に維持できます。
SELinux修正アプローチのメリット・デメリット
それぞれの修正方法には運用上の明確なトレードオフが存在します。各手法の比較は以下のとおりです。
| 手法 | メリット | デメリット | 本番環境リスク |
|---|---|---|---|
| SELinuxの無効化 / Permissiveモード | 重大な障害発生時に5秒以内でサービストラフィックを復旧可能。 | カーネルレベルの隔離機能が失われ、パーミッションの設定ミスが隠蔽され、監査コンプライアンス(PCI-DSS、SOC2、HIPAAなど)に抵触する。 | 致命的(Critical) |
SELinuxブール値(setsebool -P) |
ベンダー検証済みで再起動後も永続化され、ポリシーをコンパイルすることなく即時反映される。 | アップストリームのポリシーメンテナーによって事前に想定・定義された動作のみに対応。 | 極小(Minimal) |
コンテキストの再ラベリング(semanage + restorecon) |
標準のTargetedポリシー定義を利用して、厳格な最小権限アクセスを適用できる。 | ファイルコンテキストと正規表現パスパターンの理解が必要。 | 非常に低い(Very Low) |
カスタムモジュール(audit2allow) |
組み込みのブール値が存在しない自社製バイナリや独自エージェントのアクセス問題を解決できる。 | フィルタリングされていないログから生成すると、意図せず危険な権限(広範なexecmemアクセスなど)を付与してしまうリスクがある。 |
中程度(Moderate) |
推奨構成:段階的なトリアージ手順
本番サーバーをPermissiveモードのまま放置してはいけません。以下の4段階のエスカレーション手順に沿って対処してください。
- 監査ログの調査:
ausearchを使用して/var/log/audit/audit.logを検索し、ブロックされたシステムコールを特定します。 - 既存ブール値の確認: 実行したい処理に対してRed Hatが既にブール値のスイッチを用意していないか確認します。
- ディレクトリとファイルのラベル修正: カスタム保存パスに適切なタイプコンテキスト(例:静的Webアセット用の
httpd_sys_content_t)が付与されているか確認・修正します。 - カスタムモジュールの構築: ブール値やコンテキストの再ラベリングではデーモンの要件を満たせない場合にのみ、
audit2allowを使用します。
実践ガイド:SELinuxアクセス拒否の診断と解決手順
ステップ1:AVC(Access Vector Cache)拒否の診断
まず、システムの現在の動作モードを確認します。
getenforce
# 出力: Enforcing
sestatus
エラーの原因が本当にSELinuxであるか確認するために、一時的にPermissiveモードへ切り替えて curl でエンドポイントをテストします。
# テスト用に一時的にPermissiveモードへ設定
setenforce 0
# 即座にアプリケーションをテスト。HTTP 200が返ればSELinuxが原因
curl -I http://127.0.0.1:80/
# 直ちにEnforcingモードへ戻す
setenforce 1
次に、監査ログから拒否の詳細を抽出します。
# 直近10分以内のAVC拒否ログを検索
ausearch -m AVC,USER_AVC -ts recent
# audit2whyで監査ログを解析し、根本原因の分かりやすい説明を出力
ausearch -c 'nginx' --raw | audit2why
audit2why は、難解なAVCメッセージを人間が理解できる具体的なアドバイスへと変換します。ブール値を有効化すべきか、ファイルラベルを修正すべきかを分かりやすく提示してくれます。
ステップ2:ファイルコンテキストの不一致の修正(semanage fcontext & restorecon)
サイトのデータを標準の /var/www/html ではなく /data/web/app1 などのカスタムパスに配置すると、ディレクトリには汎用的な default_t や var_t ラベルが継承されます。Nginxは httpd_t ドメインで動作するため、これらのラベルを持つファイルを読み取ることができません。
現在のセキュリティ属性を確認します。
ls -lZ /data/web/app1
# 出力: -rw-r--r--. 1 nginx nginx unconfined_u:object_r:default_t:s0 index.html
管理ツールをインストールし、SELinuxポリシーデータベースに適切なコンテキストルールを登録して、ファイルシステムに適用します。
# semanageが見つからない場合はpolicycoreutilsをインストール
dnf install -y policycoreutils-python-utils
# カスタムディレクトリに対する永続的なコンテキスト定義を登録
semanage fcontext -a -t httpd_sys_content_t "/data/web/app1(/.*)?"
# ディレクトリツリー全体にコンテキストを再帰的かつ詳細出力(verbose)で適用
restorecon -Rv /data/web/app1
# 更新されたコンテキストを確認
ls -lZ /data/web/app1
# 出力: -rw-r--r--. 1 nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 index.html
ステップ3:ブール値によるネットワーク通信の管理
デフォルトでは、Webデーモンはバックエンドのアプリケーションサーバー(ポート9000のPHP-FPMやポート8000のGunicornなど)やリモートデータベースへのネットワーク接続を開くことができません。SELinuxの初期設定では、これらのソケット操作が拒否されます。
Webサービスを制御する関連ブール値を検索します。
getsebool -a | grep httpd
必要なネットワークアクセス経路を有効化します。変更をディスク上の設定に永続化するため、必ず -P フラグを付与してください。
# Nginx/Apacheからアップストリームのネットワークバックエンドへのプロキシ接続を許可
setsebool -P httpd_can_network_connect 1
# Nginx/Apacheからデータベースサーバーへの直接接続を許可
setsebool -P httpd_can_network_connect_db 1
ステップ4:audit2allowによるカスタムポリシーモジュールの作成
独自のマイクロサービスや社内監視エージェント、サードパーティ製ソフトウェアは、デフォルトのブール値ではカバーされていない正当なシステムコールを実行する場合があります。このようなシナリオでは、audit2allow を使用して必要最小限のスコープに絞ったType Enforcementポリシーモジュールを作成します。
まず、コンパイル前に人間が読める形式のType Enforcement(.te)ルールをプレビューします。
# audit2allowが提案する正確な許可ルール(allow rules)を確認
ausearch -c 'custom_agent' --raw | audit2allow -m custom_agent
出力を慎重に確認し、サービスが真に必要としている特定のファイルアクセスやケーパビリティのみが許可されていることを確認してください。問題がなければ、Type Enforcementファイルとコンパイル済みバイナリパッケージ(.pp)の両方を生成します。
# custom_agent.te と custom_agent.pp を生成
ausearch -c 'custom_agent' --raw | audit2allow -M custom_agent
# ソースルールを最終確認
cat custom_agent.te
# コンパイルされたポリシーモジュールを稼働中のカーネルにロード
semodule -i custom_agent.pp
ステップ5:再起動後のポリシー永続性の検証
新しいポリシーモジュールがシステムのポリシーストアでアクティブになっていることを確認します。
semodule -l | grep custom_agent
通常のアプリケーション運用中に監査ログをリアルタイム監視し、すべての拒否エラーが解消されたことを確認します。
tail -f /var/log/audit/audit.log | grep -E "type=AVC.*denied"
永続的なファイルコンテキスト(semanage)、永続ブール値(setsebool -P)、および対象を絞ったポリシーパッケージ(semodule)を適切に組み合わせることで、RHELやRocky Linuxシステムはカーネルアップデートやシステムの再起動を経ても、強固なセキュリティ、安定性、完全なコンプライアンスを維持できます。

