Red Hat Certified System Administrator (RHCSA) #13: SELinux 深掘り: contexts、booleans、troubleshooting (audit2allow)

読了 12分

#12 firewalld と SSH 鍵認証 でポートを開いて鍵でログインする作業を扱いました。ところがファイアウォールを開いてサービスを起動したのに接続がブロックされるなら、次に疑う対象はほぼ常に SELinux です。SELinux は RHCSA 受験者が最も多く足を取られる領域であり、同時にパターンさえ掴めば最も確実に点数を稼ぐ領域でもあります。今回はコンテキスト・boolean・ポートラベル・トラブルシューティングを実技の観点で最後まで整理します。

SELinux の核心は単純です。すべてのプロセスとファイルにラベル (context) を付け、ポリシーが許可した組み合わせだけを通す というものです。標準 Linux のパーミッション (rwx) をすべて通った作業でも、SELinux ポリシーが拒否すればその作業は失敗します。RHCSA で出会うほぼすべての SELinux 問題は「パーミッションは合っているのに動かない」形で現れます。

SELinux モード: enforcing、permissive、disabled #

SELinux には 3 つのモードがあります。

モード動作
enforcingポリシーを強制。拒否対象はブロックされ audit ログに記録
permissiveポリシーを強制しない。ブロックはしないが拒否事項はログにのみ記録
disabledSELinux 無効。ラベリングも停止

permissive は「ブロックはしないが何がブロックされたはずかをログで見せてくれる」モードなので、トラブルシューティングの段階で非常に役立ちます。

現在のモード確認と一時切り替え #

現在のモードは getenforce で見ます。sestatus はより詳細な状態を見せてくれます。

getenforce
# Enforcing

sestatus
# SELinux status:                 enabled
# Current mode:                   enforcing
# Mode from config file:          enforcing
# Policy from config file:        targeted

setenforce で enforcing と permissive を即座に切り替えられます。ただしこの切り替えは 再起動すると消える一時設定 です。

setenforce 0   # permissive に切り替え
setenforce 1   # enforcing に切り替え
getenforce
# Permissive

setenforce では disabled へは行けません。enforcing と permissive の間だけを行き来します。

永続モード: /etc/selinux/config #

再起動後も維持されるモードは /etc/selinux/configSELINUX= の値で決めます。RHCSA で「SELinux を enforcing で永続設定せよ」のような問題が出たら、このファイルを直す必要があります。

cat /etc/selinux/config
# SELINUX=enforcing
# SELINUXTYPE=targeted

SELINUX= の値を enforcingpermissivedisabled のいずれかに変えると次の起動から適用されます。試験では普通 enforcing を永続で要求するので、setenforce 1 で即座に適用し、config ファイルにも enforcing を書いておくのが安全です。

setenforce 1
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
grep '^SELINUX=' /etc/selinux/config
# SELINUX=enforcing

disabled から enforcing に変えるときはファイルシステム全体のラベルが抜けている場合があり、次の起動で自動 relabel が起きます。relabel を強制するには touch /.autorelabel 後に再起動します。

コンテキスト (context): ラベルの構造 #

SELinux はすべてのオブジェクトにコンテキストを付けます。コンテキストはコロンで区切られた 4 つの部分から成ります。

user:role:type:level
system_u:object_r:httpd_sys_content_t:s0

RHCSA で実際に扱う部分はほぼ type (3 番目のフィールド) 一つです。ポリシー判断のほとんどが type 間の許可ルール (type enforcement) で行われるためです。例えば Web サーバープロセスの type は httpd_t で、Web コンテンツファイルの type は httpd_sys_content_t です。ポリシーが「httpd_thttpd_sys_content_t を読める」と許可するので、Web サーバーがそのファイルをサービスします。

ファイルコンテキストを見る: ls -Z #

-Z オプションは SELinux コンテキストを見せてくれます。ls -Z でファイルのコンテキストを確認します。

ls -Z /var/www/html/
# unconfined_u:object_r:httpd_sys_content_t:s0 index.html

プロセスコンテキストを見る: ps -Z #

ps -Z で実行中のプロセスのコンテキストを見ます。

ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0 1234 ? 00:00:00 httpd

id -Z は現在のシェルのコンテキストを、ss -Znetstat -Z はソケットのコンテキストを見せてくれます。

コンテキストが間違うと起きること #

RHCSA で最も多いシナリオです。Web コンテンツを標準の場所ではない所に置いたり、別の所からファイルを移してきたりするとコンテキストがずれます。例えばホームディレクトリで作ったファイルを /var/www/htmlmv すると、そのファイルは元の場所のコンテキスト (user_home_t など) をそのまま持ってきます。その結果、Web サーバープロセス (httpd_t) が読めず 403 になります。

ここに核心となるルールがあります。

  • cp でコピーすると コピー先ディレクトリの既定のコンテキスト を新たに受け取ります。
  • mv で移動すると 元のコンテキストをそのまま維持 します。

なのでファイルを移した後はコンテキストを直さなければなりません。

コンテキストを直す: chcon vs restorecon vs semanage #

コンテキストを変える方法は 3 つあり、何を使うかが RHCSA の点数を分けます。

chcon: 一時変更 (試験では避ける) #

chcon はファイルのコンテキストを直接変えます。即座に適用されますが、ポリシーデータベースには反映されません。したがって後で restorecon が走ったりファイルシステムの relabel が起きたりすると元に戻ります。

chcon -t httpd_sys_content_t /var/www/html/index.html
chcon -R -t httpd_sys_content_t /web/content   # 再帰変更

chcon は素早くテストするときだけ使い、試験の解答としては推奨しません。採点前にシステムが relabel されると解答が解けてしまうことがあるためです。

semanage fcontext + restorecon: 永続変更 (正解) #

永続的にコンテキストを直す正しい方法は 2 段階です。

  1. semanage fcontext -a -tポリシーに既定のコンテキストルールを追加 します。
  2. restorecon でそのルールを 実際のファイルに適用 します。

例えば /web 以下を Web コンテンツとして使うには次のようにします。

# 1) ポリシーにルール追加: /web 以下すべてを httpd_sys_content_t と定義
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"

# 2) ルールを実際のファイルに適用
restorecon -R -v /web

"/web(/.*)?" は正規表現で、/web ディレクトリ自体とその下のすべてのパスを意味します。こうするとポリシー自体にルールが残るので、relabel が起きてもコンテキストが維持されます。これが RHCSA で要求される永続適用です。

semanagepolicycoreutils-python-utils パッケージに入っています。なければインストールします。

dnf install -y policycoreutils-python-utils

ルールを確認したり削除したりするときは次のようにします。

semanage fcontext -l | grep '/web'   # 追加したルールを確認
semanage fcontext -d "/web(/.*)?"    # ルールを削除

restorecon: 既定のコンテキストに戻す #

restorecon はポリシーに定義された既定のコンテキストをファイルに再適用します。mv で移してきてコンテキストがずれた標準の場所のファイルは、semanage なしで restorecon だけで直ります。/var/www/html はすでにポリシーに httpd_sys_content_t として定義されているためです。

restorecon -R -v /var/www/html
# Relabeled /var/www/html/index.html
#   from unconfined_u:object_r:user_home_t:s0
#   to   unconfined_u:object_r:httpd_sys_content_t:s0

整理すると、標準の場所なら restorecon だけ、非標準の場所なら semanage fcontext でルールを追加した後に restorecon です。

boolean: ポリシーの動作をオン/オフする #

コンテキストが全部合っていてもブロックされる場合があります。SELinux ポリシーには boolean という on/off スイッチがあり、特定の動作をポリシーレベルで許可したりブロックしたりします。例えば Web サーバーがユーザーのホームディレクトリをサービスしたり、外部へネットワーク接続を結んだりする動作は既定で boolean でオフになっています。

boolean 一覧を見る: getsebool #

すべての boolean は getsebool -a で見ます。semanage boolean -l は既定値と説明まで一緒に見せてくれます。

getsebool -a | grep httpd
# httpd_can_network_connect --> off
# httpd_enable_homedirs --> off
# httpd_use_nfs --> off

semanage boolean -l | grep httpd_can_network_connect
# httpd_can_network_connect (off , off)  Allow httpd to ...

boolean 設定: setsebool -P #

setsebool で boolean をオン/オフします。ここで -P オプションが核心 です。-P なしでオンにするとメモリにだけ適用され再起動するとオフになります。-P を付けてはじめてポリシーに永続保存されます。

setsebool httpd_can_network_connect on      # 一時。再起動するとオフ
setsebool -P httpd_can_network_connect on   # 永続。正解

RHCSA で「データベースに接続する Web アプリを動作させよ」のような問題は十中八九 httpd_can_network_connect-P でオンにするのが答えです。-P を抜くと採点時点で再起動され失点します。

ポートラベル: semanage port #

SELinux はポートにもラベルを付けます。サービスを標準ではないポートで動かすと、そのポートのラベルがサービスの type と合わずバインドが拒否されます。例えば sshd を 22 番ではなく 2222 番に変えると、2222 番には ssh_port_t ラベルがないので sshd がそのポートを開けません。firewalld でポートを開いても無駄です。SELinux の段でブロックされるためです。

現在のポートラベルを見る #

semanage port -l | grep ssh
# ssh_port_t   tcp   22

semanage port -l | grep http_port_t
# http_port_t  tcp   80, 81, 443, 488, 8008, 8009, 8443, 9000

ポートラベル追加: semanage port -a -t #

非標準ポートを使うには、当該ポートに適した type を付与します。

# sshd を 2222 番で使うには、そのポートに ssh_port_t を付与
semanage port -a -t ssh_port_t -p tcp 2222
semanage port -l | grep ssh
# ssh_port_t   tcp   2222, 22

すでに別の type が占有しているポートなら -a (追加) ではなく -m (修正) を使います。追加するときに already defined エラーが出たら -m に変えるのが定石です。

semanage port -m -t http_port_t -p tcp 8888

非標準ポートにサービスを移す RHCSA の問題は firewalld のポート開放 + SELinux のポートラベル追加 を一括で処理してはじめて完成します。どちらか一方だけでは接続できません。

トラブルシューティング: 拒否ログを追跡してポリシーを作る #

SELinux の拒否はすべて AVC (Access Vector Cache) denial として audit ログに記録されます。何がなぜブロックされたかをこのログから読み取ることがトラブルシューティングの出発点です。ログは /var/log/audit/audit.log にあります。

ausearch: audit ログから拒否を探す #

ausearch で最近の AVC 拒否をまとめて見ます。

ausearch -m AVC -ts recent
# type=AVC msg=audit(...): avc:  denied  { read } for
#   pid=1234 comm="httpd" name="index.html"
#   scontext=system_u:system_r:httpd_t:s0
#   tcontext=unconfined_u:object_r:user_home_t:s0  tclass=file

ここで読むべき部分は明確です。comm="httpd" (誰が)、denied { read } (何を)、tcontext=...user_home_t (どのラベルの対象に) です。httpd_t プロセスが user_home_t ファイルを読もうとしてブロックされたという意味なので、コンテキストが間違っている典型的な事例です。答えは restoreconsemanage fcontext でラベルを直すことです。

sealert: setroubleshoot の勧告を読む #

setroubleshoot-server パッケージをインストールすると、拒否が生じるたびに /var/log/messages に人が読みやすい分析と推奨コマンドが記録されます。sealert で詳しく見ます。

dnf install -y setroubleshoot-server
sealert -a /var/log/audit/audit.log

sealert はしばしば「この問題は次のコマンドで解決せよ」と setsebool -P ...restorecon ... のような具体的なコマンドを提示します。ただし勧告をそのまま従う前に、その勧告が本当に意図した動作なのかを判断する必要があります。

audit2allow: 拒否ログでポリシーモジュールを作る #

標準の boolean やコンテキスト修正で解けない、カスタムポリシーが必要な拒否には audit2allow を使います。拒否ログを入力として受け取り、その動作を許可するポリシールールを生成します。

まず何を許可することになるのかをルールで目視確認します。

ausearch -m AVC -ts recent | audit2allow
# allow httpd_t user_home_t:file { read };

ルールが妥当だと判断したら、モジュールにして読み込みます。

# myhttpd という名前のポリシーモジュール生成
ausearch -m AVC -ts recent | audit2allow -M myhttpd

# 生成されたモジュールを読み込み
semodule -i myhttpd.pp

audit2allow -M.te (ポリシーソース) とコンパイルされた .pp (ポリシーパッケージ) ファイルを作り、semodule -i でその .pp をシステムポリシーに追加します。読み込まれたカスタムモジュールは semodule -l で確認し、semodule -r myhttpd で削除します。

トラブルシューティングの優先順位 #

audit2allow は強力ですが 最後の手段 です。拒否を無条件で許可するモジュールを作ることは、本当の原因 (間違ったコンテキストやオフになった boolean) を覆い隠してしまう恐れがあるためです。RHCSA での推奨順序は次のとおりです。

  1. コンテキスト確認ls -Z でラベルを見て、ずれていたら restorecon または semanage fcontext で直します。
  2. boolean 確認。動作自体が boolean でオフになっているかを見て、そうなら setsebool -P でオンにします。
  3. ポートラベル確認。非標準ポートなら semanage port でラベルを付与します。
  4. 上で解けない本当にカスタムな動作にだけ audit2allow でモジュールを作ります。

permissive にしばらく変えてみるのも診断に役立ちます。permissive でサービスが正常に動作すれば原因は明らかに SELinux で、その間に溜まった拒否ログを一度にまとめて分析できます。診断が終わったら必ず enforcing に戻します。

試験ポイント #

  • chcon vs restoreconchcon は一時で relabel すると解けます。永続の解答は標準の場所なら restorecon、非標準の場所なら semanage fcontext -a -t 後に restorecon です。試験の解答として chcon は使いません。
  • setsebool の -P-P を抜くと再起動時に消えて採点で失点します。boolean をオンにするときは常に setsebool -P です。
  • semanage fcontext の正規表現。ディレクトリ以下すべては "/path(/.*)?" で書きます。引用符で囲んでシェルがアスタリスクを解釈しないようにします。
  • 非標準ポートは 2 か所。firewalld でポートを開き、semanage port -a -t で SELinux ラベルも付与してはじめて接続が完成します。
  • 永続モードは /etc/selinux/configsetenforce は一時です。永続 enforcing は config ファイルの SELINUX=enforcing まで確認します。
  • トラブルシューティングツール。ログは /var/log/audit/audit.log、拒否検索は ausearch -m AVC、勧告は sealert、ポリシー生成は audit2allow -M 後に semodule -i です。
  • semanage がなければ policycoreutils-python-utils を、sealert がなければ setroubleshoot-server をインストールします。

まとめ #

この記事で押さえたこと:

  • SELinux モードgetenforce/setenforce で一時切り替え、/etc/selinux/config で永続設定。enforcing・permissive・disabled
  • コンテキストls -Z/ps -Z で確認。核心は type フィールド。mv はラベルを持ってきて cp はコピー先の既定を受け取る
  • コンテキストを直すchcon は一時、永続は semanage fcontext -a -t + restorecon。標準の場所は restorecon だけで十分
  • booleangetsebool -a で確認、setsebool -P で永続設定。動作単位の on/off スイッチ
  • ポートラベルsemanage port -a -t で非標準ポートに type を付与。firewalld と一括
  • トラブルシューティングaudit.logausearch/sealertaudit2allow でポリシーモジュール。ただしコンテキスト・boolean・ポートを先に点検

SELinux は RHEL 実務トラック で触ってみた概念を試験の観点で締め直す領域です。拒否が見えたら慌てず、コンテキスト・boolean・ポートの順に点検する習慣を付ければ、RHCSA で SELinux はむしろ確実な得点源になります。

次へ: コンテナ管理 #

セキュリティ領域まで仕上げました。これで RHCSA の出題範囲の最後の大きな軸であるコンテナへ移ります。

#14 コンテナ管理: Podman、systemd integration (quadlet) では、root なしでコンテナを動かす Podman の基本コマンドから、イメージを取得して実行しストレージをマウントする方法、そしてコンテナを systemd サービスとして登録し起動時に自動起動させる quadlet 統合まで、自分で打ち込みながら整理します。

X