Rocky LinuxでのSELinux設定:理解する前に無効化してはいけない

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

午前2時とsetenforce 0という罠

CentOS 8のEOL時、1週間以内に5台のサーバーをRocky Linuxへ急ぎで移行しなければならなかった。初日の夜、Nginxが新しいポートにbindできず、chownは正しいはずなのにPHP-FPMがpermissionエラーを出し続けた。手は震え、目はかすむ中でsetenforce 0を叩くと、すべてが動き出した。作業完了、就寝。

翌朝確認して青ざめた:本番サーバーがSELinux無効の状態で動いている。1台じゃない――5台全部だ。このままセキュリティ監査を受けたら、真っ先に減点されることは分かっていた。

そこから自問するようになった:SELinuxは何をブロックしているのか、なぜなのか、そして無効化せずに修正するにはどうすればいいのか。この記事はその答えだ――5台すべてをEnforcingモードに戻した方法で、何も壊さずに済んだ。

SELinuxへの3つのアプローチ――そのうち2つを本番環境で使ってはいけない理由

フォーラムやStackOverflowを見ると、多くのsysadminがSELinuxを3つの方法のどれかで対処している:

方法1:SELinuxを完全に無効化する(SELINUX=disabled

この方法は古いチュートリアル、特に2018年以前のLAMPスタック構築記事でよく見られる。/etc/selinux/configを編集してrebootするだけだ。

# /etc/selinux/config
SELINUX=disabled

唯一のメリット:すぐに動く、デバッグ不要。デメリットの方がはるかに大きい――カーネルレベルのセキュリティレイヤーを完全に無効化し、後で再有効化する際にファイルシステム全体のrelabelが必要になる。150GBのサーバーでこのステップに18分近くかかった。

方法2:Permissiveモード――ログは記録するがブロックしない

SELinuxは動作し続けてpolicyをチェックするが、ブロックする代わりにaudit logに記録するだけだ。簡単に言えばdry run――違反を検出するが何もしない。

setenforce 0        # 一時的(再起動後に元に戻る)
getenforce          # 確認:「Permissive」と表示される

再起動後も設定を維持するには:

vi /etc/selinux/config
# SELINUX=permissive

方法3:Enforcing――真のセキュリティ

これこそが目指すべきモードだ。policy違反は即座にブロックされ、警告も例外もない。Rocky Linuxをクリーンインストールした際のデフォルト設定だ。

setenforce 1
getenforce          # 出力: Enforcing

実践的な比較:どのケースにどのモードを使うか

モード セキュリティ デバッグのしやすさ 用途
Disabled なし 不要 本番環境では絶対に使わない
Permissive ブロックなし 最も簡単 開発/ステージング、またはデバッグ時
Enforcing 完全 auditログの知識が必要 本番環境 — 最終目標

本番サーバーを運用しているなら、Disabledは最悪の選択だ。Permissiveはテスト環境には適しているが、長期的な解決策にはならない。Enforcingこそが目指すべき場所だ。

判断:Permissive → Enforcingへの計画的な移行

5台のサーバーを移行した際に使ったアプローチは次の通りだ:

  1. まずPermissiveを有効にする
  2. 数日間サービスを通常通り動かし、SELinuxにdenialを十分にログさせる
  3. audit2allowでカスタムpolicyを作成する
  4. policyをロードしてEnforcingに切り替える

ログが蓄積されるまで数日かかるが、その分暗中模索せずに済む――Enforcingにする前に、どのサービスが何のpermissionを必要としているか正確に把握できる。

実践的な実装ガイド

ステップ1:現在の状態を確認する

sestatus
# 出力例:
# SELinux status:                 enabled
# SELinuxfs mount:                /sys/fs/selinux
# SELinux mount point:            /sys/fs/selinux
# Loaded policy name:             targeted
# Current mode:                   permissive
# Mode from config file:          enforcing
# Policy MLS status:              enabled
# Policy deny_unknown status:     allowed
# Memory protection checking:     actual (secure)
# Max kernel policy version:      33

「Current mode」(実行時)と「Mode from config file」(再起動後)の違いに注意。setenforceを使ってconfigファイルをまだ変更していない場合、この2つが一致しないことがある。

ステップ2:AVC denial logを読む

Permissiveモードでは、すべての違反が/var/log/audit/audit.logにログされる:

# 最新のdenialを確認
ausearch -m avc -ts recent

# またはgrepで直接確認
grep "avc:  denied" /var/log/audit/audit.log | tail -20

典型的なAVC denialの例:

type=AVC msg=audit(1709712345.123:456): avc:  denied  { read } for  pid=12345 comm="nginx" \
  name="app.sock" dev="tmpfs" ino=67890 \
  scontext=system_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=1

読む順番:どのプロセスか(comm)、何をしているか(read)、どのファイルに対してか(name)、typeが一致しないため拒否された。

ステップ3:sealertで解析する(より読みやすい形式で)

auditログを直接読むのはかなり疲れる。setroubleshoot-serverが人間が読みやすい形式に変換してくれる:

dnf install -y setroubleshoot-server

# audit logに対して実行
sealert -a /var/log/audit/audit.log

sealertは具体的な提案を出してくれる――通常は実行すべきsetseboolsemanageコマンドと、その理由の説明だ。

ステップ4:audit2allowでカスタムpolicyを作成する

サービスに特殊な動作がある場合――たとえばNginxが非標準ディレクトリからファイルを配信する場合――独自のpolicyを作成する必要がある:

# audit logからpolicyモジュールを作成
ausearch -m avc -ts recent | audit2allow -M my_nginx_custom

# 作成したpolicyをロード
semodule -i my_nginx_custom.pp

# ロード済みか確認
semodule -l | grep my_nginx

.teファイルはテキストソース、.ppはコンパイル済みpolicyだ。.teファイルは保存しておくこと――後でpolicyを修正する必要が生じた際に、最初からやり直す代わりにそのファイルを編集すればいい。

ステップ5:新しいpolicyを作る代わりにbooleanを使う

よくあるケースには既製のbooleanが用意されており、使用がより速くシンプルだ:

# httpdが外部接続できるようにする(API呼び出し、プロキシ)
setsebool -P httpd_can_network_connect 1

# httpdがホームディレクトリを読めるようにする
setsebool -P httpd_enable_homedirs 1

# nginx/apacheが非標準ポートにbindできるようにする
semanage port -a -t http_port_t -p tcp 8080

# httpd関連のbooleanをすべて確認
getsebool -a | grep httpd

ステップ6:必要に応じてファイルcontextをrelabelする

ファイルを新しいディレクトリにコピーしたり、標準パス外にファイルを作成したりすると、SELinuxのcontextが間違ってしまうことが多い。修正方法は次の通りだ:

# 現在のcontextを確認
ls -Z /var/www/html/

# Webディレクトリに正しいcontextを設定
semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?";
restorecon -Rv /data/web/

# 1つのファイルだけ修正する場合
chcon -t httpd_sys_content_t /data/web/index.php

ステップ7:Enforcingに切り替える

数日間のPermissiveモードで新しいdenialが出なくなったら、切り替えのタイミングだ:

# 一時的に変更(即時テスト)
setenforce 1

# 30分間ログを監視
tail -f /var/log/audit/audit.log | grep denied

# 問題なければ設定ファイルに永続化
vi /etc/selinux/config
# SELINUX=enforcing

Enforcingに切り替えた直後、別のターミナルタブでtail -f audit.logを実行するようにしている。約30分待ち、アプリの主要な機能をすべてテストする。新しいdenialが出なければ完了だ。

クイックリファレンス:覚えておくべきSELinuxコマンド

# ステータス確認
sestatus
getenforce

# runtimeのmode変更
setenforce 0   # Permissive
setenforce 1   # Enforcing

# contextの確認
ls -Z /path/to/file
ps auxZ | grep nginx

# contextの修正
restorecon -Rv /path/     # policyのデフォルトに復元
chcon -t TYPE /file       # 一時的に変更

# ポート
semanage port -l | grep http
semanage port -a -t http_port_t -p tcp 8443

# ログ
ausearch -m avc -ts recent
sealert -a /var/log/audit/audit.log

# Policy
audit2allow -M mypolicy < /var/log/audit/audit.log
semodule -i mypolicy.pp
semodule -l

まとめ

SELinuxの罠は無効化が簡単で、再有効化が難しいことだ。あの移行作業以来、自分でルールを決めた:SELinux Enforcingになっていないサーバーは本番環境に上げたと言えない。最初のデバッグに1〜2時間余分にかかるが、その代わりに得られるのは本物のセキュリティレイヤーだ――見せかけのセキュリティではなく。

Rocky LinuxはRHELのSELinuxの動作をそのまま維持している。上記のコマンドはすべてAlmaLinuxや他のRHELクローンでもそのまま使える。

Share: