SELinuxシリーズ
本記事は、SELinuxシリーズの4記事目です。
- Linuxプロセスアクセス制御の概要
- SELinuxの概要
- SELinux Type Enforcement
- SELinuxの実践 ←今ココ
- (参考) SELinuxのRBAC、UBAC、MLS、MCS
- (参考) SELinux Module Policyのソースコード読解、ビルド
- 参考URL
1〜3記事目は、4記事目を理解するための前提知識をカバーしています。
4記事目が最も重要で、SELinuxの具体的な操作方法やコマンド、トラブルシューティング手順を紹介しています。
5記事目以降は参考情報です。
SELinuxの関連記事は、SELinuxタグから探せます。
一連の記事はFedora環境を前提として書いています。
FedoraやRHELに類するディストリビューションであればほぼ同等の挙動になると思いますが、他のディストリビューションでは挙動に差異がある可能性があるのでご注意ください。
お伝えしたいこと
前回のType Enforcementの記事では、デフォルトのtargeted policyに限定しつつもSELinuxを理解するために必要な理論を一通り網羅しました。
本記事では、前回の記事の内容をベースにSELinuxの実践的な知識についてカバーしたいと思います。
具体的には、「SELinuxでアクセス制御エラーのログを検知したときの対処法」を中心に紹介します。
網羅性を重視した結果、本記事には滅多に使わない知識も含まれています。
そういった部分にはセクション名に "(参考)" をつけましたので、関連性が薄いと感じたら必要に応じて読み飛ばし、次に進んでください。
- SELinuxシリーズ
- お伝えしたいこと
- (参考) SELinuxの運用に必要な知識
- RPMパッケージ
- SELinuxのステータス
- エラー切り分けの流れ
- SELinuxのエラーログ確認
- File Contextとの差分確認
- (参考) restorecond
- ポート番号と紐づくSecurity Contextの確認
- Boolean有無の確認
- (参考) Custom Policyのビルド
- (参考) SELinuxの確認コマンド
- (参考) manの横断検索
- (参考) Local Policyのexport/import
- (参考) Apacheのアンインストール
- まとめ
- 次の記事
(参考) SELinuxの運用に必要な知識
SELinuxの運用に必要な知識とは、主に「アクセス拒否の対処法」です。
私がそう考えた理由について、以下に補足します。
SELinuxは、ホワイトリスト形式でプロセスのアクセス制御を実装します。
allowルールで許可されないアクセスパターンは、全て拒否されます。
SELinuxのデフォルトの動作モードは、targeted policyです。
targeted policyにおいては、代表的なデーモンプロセスがデフォルトのファイルパスやポート番号で動作するためのアクセス許可設定がある程度網羅的に設定されています。
つまり、特に構成を変更せずにLinuxを使っている限りは、SELinuxのエラーに悩まされることはほぼありません。
Linuxの構成を変更した場合も、多くの場合はsemanage
コマンドによってSELinuxのルールを部分的にカスタマイズすることで、新たなアクセスパターンを許可することができます。
場合によっては、デフォルトで許可されたallowルールを取り消して、自分たちが使うルールのみ有効化したいと思うこともあるかもしれません。
しかし、SELinuxにおいて一度許可したルールを後から拒否することはできません。
denyルールというものは存在しませんし、デフォルトでインストールされたModule Policyをアンインストールすることもできません。
(※) Module Policyについては、後続記事の(参考) SELinux Module Policyのソースコード読解、ビルド - Base PolicyとModule Policyに説明があります
SELinuxは「あるプロセスが本来必要なアクセスをすべて許可し、不要なアクセスを拒否する」という動作をします。
脆弱性を突いてroot権限を持つデーモンプロセスの制御を奪取しても、そのデーモンプロセスが本来想定していないような用途のアクセス (※) は拒否されます。
(※) 例えばバックドアのための不正なポート番号のlistenや、無関係なファイルへのアクセスなど
こういった動作によって、システムに侵入された後のリスクを軽減するのがSELinuxの役割であると私は理解しています。
デフォルトで許可されたルールは「そのプロセスにとって本来必要なアクセスを許可するもの」であるため、これを拒否するdenyルールのような仕組みは必要ないと思います。
許可されたアクセスを新たに拒否できない以上、SELinuxの運用においてできることは基本的には「追加でアクセス許可すること」のみです。
言い換えると、SELinux起因のアクセス拒否を直せるようになれればSELinuxの運用知識としては十分だと私は考えています。
アクセス拒否を直す方法は複数ありますが、適切な方法を選択できるようにSELinuxの既存のルール構成も自力で調査できるようになるとなお良いです。
本記事の序盤ではSELinuxの基本操作を扱いますが、メイントピックはアクセス拒否の直し方です。
#エラー切り分けの流れ以降のセクションにて、アクセス拒否の原因調査、そして修正方法について取り扱います。
RPMパッケージ
以下のパッケージはデフォルトで入っていない可能性があるので、必要に応じて導入してください。
Fedoraにおいては、policycoreutils-python-utils
とsetroubleshoot-server
は初めから入っていました。
特に、policycoreutils-python-utils
とsetools-console
は、SELinuxを操作するために必須とも言えるツールです。
またsetroubleshoot-server
やpolicycoreutils-restorecond
も運用上役に立つので、必要に応じて導入をご検討ください。
パッケージ名 | 内容 |
---|---|
policycoreutils-python-utils |
semanage , audit2allow コマンドを含む |
setools-console |
seinfo , sesearch コマンドを含む→ #(参考) SELinuxの確認コマンド |
setroubleshoot-server |
通常のアクセス拒否の監査ログ発生直後に、わかりやすい説明文と解決策をシステムログに出力する → #setroubleshoot-serverが出力する追加情報 |
selinux-policy-doc |
*_selinux というmanページを大量に含む→ #(参考) manの横断検索 |
policycoreutils-restorecond |
restorecond デーモンを含む→ #restorecond |
policycoreutils-devel |
Custom Policyをソースコードから記述してビルドするために必要 → #(参考) Custom Policyのビルド |
SELinuxのステータス
enforcing/permissive/disabled
SELinuxは3つのステータス値のいずれかを取ります1。
デフォルトはenforcing (有効) です。
ステータス | ラベル付け | 監査ログ出力 | アクセス制御 |
---|---|---|---|
enforcing | する | する | する |
permissive | する | する | しない |
disabled | しない | しない | しない |
基本的には、セキュリティを保つためにenforcingのままにしておくべきです。
以下の場合においてはpermissive (拒否しない) が便利です。
permissiveは基本的にSELinux有効なのですが、アクセス拒否をしない部分のみenforcingとは異なります。
アクセス拒否はしませんが、アクセス許可されない場合は監査ログに出力します。
- permissiveが便利な場面
- 構築したサーバーの単体試験の一環で、一通りの操作をしてSELinuxのエラーログを洗い出したいとき。エラーログが出たら、出なくなるようにチューニングした上で再度動作確認する
- 必要なアクセスが拒否されて運用に支障が出ているとき (セキュリティが損なわれるので、あくまで一時的な復旧策として)
disabledは、SELinuxの完全な無効化です。
disabledの状態で作成されたObjectには、Security Contextが割り当てられないということに注意が必要です。
disabledからenforcingに切り替える際は、必ずrelabelしてください。
さもないとファイルのSecurity Contextが想定外の値になり、大量のアクセス拒否が発生する恐れがあります。
relabelの手順は、#全ファイルをrelabelするにて説明します。
disabledにすることはおすすめしません。
上記の通りラベル付けされなくなり、トラブルを誘発するリスクがあるためです。
どうしてもSELinuxによるアクセス拒否を全て止めたい場合には、permissiveを推奨します。
disabledは、「SELinuxを無効化して二度と有効化するつもりはない」ときに使うものです。
SELinuxによるアクセス制御を無効化すると、セキュリティ観点でリスクが上がります。
具体的には、脆弱性を突かれるなどしてroot権限を持つプロセスの制御権を奪われた時の防御手段が1つ失われます。
参考: Linuxプロセスアクセス制御の概要 - #MAC (Mandatory Access Control)
設定ファイルによるステータス変更
/etc/selinux/config
を書き換えてOS再起動することで、SELinuxのモードを変更できます。
SELINUX=enforcing # SELINUX=permissive # SELINUX=disabled → この設定値は今後サポートされなくなる可能性あり
この設定ファイルを使って設定を書き換えることは、恐らくあまりないでしょう。
あるとしたら、SELinuxによるアクセス拒否を常時無効化したい場合にSELINUX=permissive
に書き換えるぐらいです。
一時的にenforcingからpermissiveに書き換える場合は、sudo setenforce 0
コマンドを使う方が便利です。
setenforce
コマンドはSELinuxがenforcingかpermissiveの状態であれば使えます。
sudo setenforce 1
でenforcingに、sudo setenforce 0
でpermissiveになります。
最後に一点補足しますが、SELINUX=disabled
というオプションは今後サポートされなくなる可能性があります。
経緯の詳細と代わりの手順については、#(参考) Kernel Command Line ParameterによるSELinuxの無効化を参照してください。
繰り返しになりますが、disabledにすることはそもそもおすすめしません。
ステータス確認
SELinuxのステータスは、2通りのコマンドで確認できます。
どちらを使っても良いです。
getenforce # Enforcing sestatus # SELinux status: enabled # SELinuxfs mount: /sys/fs/selinux # SELinux root directory: /etc/selinux # Loaded policy name: targeted # Current mode: enforcing # Mode from config file: enforcing # Policy MLS status: enabled # Policy deny_unknown status: allowed # Memory protection checking: actual (secure) # Max kernel policy version: 33
(参考) Kernel Command-Line ParameterによるSELinuxの無効化
Fedora34、及びRHEL9よりSELINUX=disabled
によるSELinuxの無効化はサポートされなくなりました。 2,3
RHEL8においてはまだサポートされていますが、既に非推奨とされています4。
なお繰り返しになりますが、SELinuxの無効化はそもそもおすすめしません。
一時的に無効化する方法
今後もサポートされる方法は、Linux KernelのCommand-Line Parameterにselinux=0
を渡すことです5。
Linux起動時にこのパラメータを指定するには、Linuxの起動中にコンソール画面で以下の操作を行います。
(1) LinuxのBootloaderのKernel選択画面で、e
を押すことでBoot Entryの編集画面に入る
(2) linux
で始まる行にselinux=0
というオプション指定を書き加える。既存のパラメータとはスペースで区切る
(3) Ctrl+xを押下してLinuxを起動する
Bootloaderにおけるパラメータ指定はLinux起動前に行うため、SELinuxのエラーによってLinuxの起動自体が失敗する事態においても有効です。
selinux=0
の代わりにenforcing=0
を指定するとpermissiveモードで起動することも覚えておくと、いざというときに便利です。
永続的に無効化する方法
Kernel Command-Line Parameterのデフォルト値にselinux=0
を指定すると、OS再起動してもSELinuxの無効化が持続します。
今回は、grubby
コマンドを使ってBoot Loader (GRUB2) の設定を変更します。
grubby
コマンドを使うには、grubby
RPMパッケージが必要です。
まずは現状の設定を確認します。
注目すべきはargs=
の行で、デフォルトではselinux=0
が入っていません。
ALL
を指定すると全Boot Entry分の設定を表示します。
DEFAULT
を指定すると、Linux起動時にデフォルトでカーソルのあっているBoot Entryの設定のみ表示します。
1
などの数字を指定すると、特定のindex
の設定のみ表示します。
# /boot/loader/entries sudo grubby --info ALL # (一部抜粋) # index=0 # kernel="/boot/vmlinuz-5.14.18-300.fc35.x86_64" # args="ro resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet" # index=1 # kernel="/boot/vmlinuz-5.14.18-100.fc33.x86_64" # args="ro resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet"
今回の設定変更手順では、以下のファイルも更新されます。
grep GRUB_CMDLINE_LINUX /etc/default/grub # GRUB_CMDLINE_LINUX="resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet"
以下のコマンドで全てのBoot Entryにselinux=0
オプションをデフォルトで追記します。
ALL
の代わりにDEFAULT
やindex番号を指定することもできますが、Boot Entryごとにパラメータを分ける使い方をしないのであればALL
で良いと思います。
sudo grubby --update-kernel ALL --args 'selinux=0'
変更後のパラメータを確認します。
オプションの末尾にselinux=0
が追加されています。
# /boot/loader/entries sudo grubby --info ALL # (一部抜粋) # index=0 # kernel="/boot/vmlinuz-5.14.18-300.fc35.x86_64" # args="ro resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet selinux=0" # index=1 # kernel="/boot/vmlinuz-5.14.18-100.fc33.x86_64" # args="ro resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet selinux=0" grep GRUB_CMDLINE_LINUX /etc/default/grub # GRUB_CMDLINE_LINUX="resume=/dev/mapper/fedora_fedora-swap rd.lvm.lv=fedora_fedora/root rd.lvm.lv=fedora_fedora/swap rhgb quiet selinux=0"
OS再起動すれば、SELinuxが無効化されます。
sudo reboot
selinux=0
オプションを消すには、以下のコマンドを実行します。
sudo grubby --update-kernel ALL --remove-args 'selinux'
Bootloader、/etc/default/grub
、grubby
に関するより詳細な情報は、Red Hat社のマニュアルに書いてあります。
RHEL8 - System Design Guide - Configuring kernel command-line parameters
(参考) Boot Entryとは
Boot Entryとは、Linux起動画面に出てくる項目のことです。
下記画像では4つのBoot Entryが存在します。
grubbyがインストールされていれば、sudo grubby --info ALL
でも確認できます。
Boot EntryはKernel Command-Line Parameterと紐づく概念で、Linux Kernelを更新するたびに1つ追加されます。
更新後のLinux Kernelに不具合があるとLinuxを起動できなくなる恐れがあります。
不具合が起こった場合は、過去バージョンのKernelと紐づくBoot Entryを選択することでLinuxを起動し、復旧操作を行える仕組みになっているようです。
最近のFedora系のディストリビューションでは、systemdが提供するレスキューモードもBoot Entryから選択可能になっています。
Linux Kernelは複数バージョン同時に存在できるようになっており、更新のたびにkernel
パッケージが増えていきます。
dnf list kernel --installed # Installed Packages # kernel.x86_64 5.8.15-301.fc33 anaconda # kernel.x86_64 5.14.18-100.fc33 updates # kernel.x86_64 5.14.18-300.fc35 @updates
エラー切り分けの流れ
SELinuxによるアクセス拒否を検知した時、以下の流れで切り分けを行います。
切り分けの方法は固定化されているわけではありません。
ここで紹介するのは、あくまで私のやり方です。
- SELinux以外の確認
- SELinuxのエラーログ確認
- Permissiveで再現させる
- File Context周りの確認
- ポート番号と紐づくSecurity Contextの確認
- Boolean有無の確認
- Custom Policyの自力開発の検討
各工程の概要を次のセクションで説明します。
詳細手順は別セクションに書きましたので、リンクから飛んでください。
1. SELinux以外の確認
アクセス拒否された場合、そのアクセスを行っているアプリケーションで問題が発生しているはずです。
まずはそのアプリケーションの状態を確認します。
アプリケーションがPermission denied
かそれに類するエラーを出している場合は、SELinuxによるアクセス拒否が発生している疑いが強まります。
しかし、SELinuxの観点に映る前に、先にDAC (File Permissions + POSIX ACL)の問題が発生していないかを確認しましょう。
過去記事のLinuxプロセスアクセス制御の概要 - DACとMACは両方使われるでも取り上げましたが、LinuxにおいてはDAC→MACの順にアクセス可否が評価されます。
DACに問題がある場合は、SELinuxに処理が渡るより先にエラーになり、SELinuxのエラーログが出ません。
DACに問題がないことを確認できたら、次に進みます。
2. SELinuxのエラーログ確認
SELinuxがアクセス拒否をすると、syslogと監査ログが出力されます。
以下のコマンドでそれぞれのログを確認できます。
ログが出ていた場合、SELinuxがアクセス拒否していることが確定します。
sudo ausearch -m avc,user_avc
(監査ログの表示)journalctl -qen all -t setroubleshoot
(setroubleshoot-serverによるヒントログの表示)
前者の監査ログからは以下の情報がわかります。
- 拒否されたアクセスの詳細 (source_type, target_type, class, permission)
- sourceのプロセス名、プロセスID
- targetがファイルだった場合はファイル名
後者のsetroubleshootのログには、以下の情報が含まれます。
個人的にはファイルのフルパスを確認できるのが一番便利だと感じます。
- sourceのプロセス名
- targetがファイルだった場合はフルパス
- 解決方法の提案
最後の解決方法の提案ですが、必ずしも最適な解決方法が提案されるとは限りません。
あくまで提案は提案として参考にしつつ、最終的には自分で適切な解決方法を判断することが重要です。
詳細については、#SELinuxのエラーログ確認にてログの読み方も含めて説明します。
3. Permissiveで再現させる
SELinuxでエラーが発生していることを確認できた場合、可能であればPermissiveモードに変更してからエラーメッセージを再現することをおすすめします。
単一コマンドの実行であっても、細かく見れば複数のアクセスが発生しているケースがあります(例: ファイルのopen,read)。
Enforcingの場合、openに失敗するとその時点で処理が止まります。
仮にopenを許可した場合、今度はreadに失敗します。
このように、Enforcingのまま切り分けを進めると時間がかかることがあります。
Permissiveであればエラーが発生しても処理を中断しないので、上記の例で言えばopenとreadの両方について監査ログを出力します。
両方の原因に対処して動作確認すれば、次は一発で処理が通ります。
具体的なコマンドは、以下のとおりです。
# EnforcingからPermissiveに変更 sudo setenforce 0 # (エラーの再現) # (エラーログの確認) # PermissiveからEnforcingに変更 sudo setenforce 1
セキュリティ観点で本番環境のサーバーをPermissiveにすることは難しいと思います。
少々手間はかかりますが、原則としては検証環境などでこの工程を実施することをおすすめします。
4. File Contextとの差分確認
SELinuxの拒否ログで、targetがファイルである場合はFile Contextとの差分が発生しているかを疑ってください。
ファイルのSecurity ContextがFile Contextと差分がないかを確認します。
最もよく使うコマンドは、以下の2つです。
matchpathcon -V ファイル名
(File Contextとの差分チェック)restorecon -v
(relabelによる修正)
ファイルのSecurity Contextがアクセス制御ルールの設計と異なる場合、allow Statementとマッチしなくなることでアクセス拒否されてしまいます。
File Context通りのTypeを持っていれば問題ないことがほとんどなので、多くの場合はrelabelによって問題を解決できます。
運用上は、mv
コマンドによってファイル移動した時にこの状況がしばしば発生します。
mv
はSecurity Contextを含めてファイルの属性情報を全て保持してファイルを移動します。
ホームディレクトリで作ったファイルを/var
や/etc
配下に移動すると、これらのフォルダ配下にuser_home_t
Typeのファイルが生成し、アクセス拒否の原因となります。
詳細については、#File Contextとの差分確認にて上記以外のコマンドや修正例も含めて説明します。
5. ポート番号と紐づくSecurity Contextの確認
SELinuxの拒否ログで、targetがTCP/UDPポート番号 (*_port_t
) の場合は、ポート番号がデフォルト値から変更されていないかを思い返してみてください。
正しく設定されていれば、source (*_t
) とtarget (*_port_t
) の*
に入る文字列が似たものになるはずです。
エラーが発生している場合は、上記の文字列が異なっていることが多いです。
例えば、source=httpd_t, target=http_port_t
が正しい姿です。
source=httpd_t
に対して、target=ssh_port_t
のように明らかに異なる場合は、ポート番号とSecurity Contextの紐付けを修正するか、ポート番号そのものを変更することをご検討ください。
ポート番号とSecurity Contextの紐付けを確認するには、主に以下のコマンドを使用します。
seinfo --portcon
ポート番号とSecurity Contextの紐付けを変更して問題を解決するには、以下のコマンドを実行します。
sudo semanage port -a ...
詳細については、#ポート番号と紐づくSecurity Contextの確認にて上記以外のコマンドや修正例も含めて説明します。
6. Boolean有無の確認
Object (target) に割り当てられたSecurity Contextが誤っている疑いは、この時点でほぼ払拭されています。
以降は既存のアクセス制御ルールを変更するアプローチになります。
自力でルールを記述するよりも先に、まずは今の状況にマッチしたBooleanが存在しないかを先に確認しましょう。
メジャーなケースであれば、専用のBooleanが用意されていることが少なくありません。
Booleanを探すには、以下のコマンドが有効です。
sesearch -A -s ... -t ... -c ... -p ...
sudo semanage boolean -l
またはgetsebool -a
まずは、SELinuxの拒否ログと全く同じ条件でsesearchでアクセス許可ルールを検索します。
実際に拒否されているのでアクセス許可ルールは通常ヒットしないのですが、Booleanが存在する場合は以下のような出力を得られます。
以下の例では、httpd_read_user_content
というBooleanがTrueの場合のみ有効化されるallowルールが検索にヒットしました。
sesearch -A -s httpd_t -t user_home_t -c file -p read,open # allow httpd_t user_home_type:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True
候補となるBooleanが見つかった場合は、sudo semanage boolean -l
などでBooleanの意味を確認します。
そしてBooleanの意味をより厳密に確認するために、sesearch -b
で確認します。
Boolean値が今回の状況にマッチしそうな場合は、以下のいずれかのコマンドでBoolean値を変更します。
sudo setsebool -P ...
sudo semanage boolean -m ...
詳細については、#Boolean有無の確認にて修正例も含めて説明します。
7. Custom Policyのビルド
上述のいずれの切り分けでも対処できない場合、「最後の手段として」Custom Policyのビルドという手段があります。
自力でallow Statementやtype_transition Statementを記述したソースコードを書き、それをビルド・インストールする方法です。
この方法を使えば、ほぼどんな定義やルールでも追加できます (※)。
(※) SID (Security ID) など、Base Policyにしか書けない定義やルールもあります。ここで可能なのは、あくまでModule Policyのソースコードに書けるルールのみです。とはいえ、ほぼ何でもできると思って差し支えありません
具体的には、Custom Policyによって以下のようなことができます。
- Typeの定義
- Attributeの定義
- type_transition Statementの追加
- allow Statementの追加
- Booleanの定義
- File Contextの定義
BooleanやFile Contextについては、semanage
コマンドによっても追加できます。
使い分けとしては、Custom PolicyのType定義やallow Statementに関連するものであれば、Custom Policyに含めるのが一般的です。
そうすることで、関連する一連の定義やルールをひとまとめに管理できます。
Custom Policyを追加する状況は、例えば以下が挙げられます。
- 既存のルール設定にallow Statementを追加したい場合
- 自作のアプリケーションをSELinuxでアクセス制御したい場合
具体的な手順については、#(参考) Custom Policyのビルドで紹介します。
SELinuxのエラーログ確認
本セクションでは、#2. SELinuxのエラーログ確認の詳細な確認手順を説明します。
一番よく使うのは、以下のコマンドです。
sudo ausearch -m avc,user_avc
journalctl -qen all -t setroubleshoot
ログの表示コマンド
SELinuxのエラーログは2種類出力されます。
2のログはsetroubleshoot-server
RPMパッケージがインストールされている場合のみ出力されます。
「事象の把握」という意味では、1のAVCから出るログが有用です。
しかし、1のログではファイルへのアクセスが拒否された時のファイルのフルパスが書いてないことがあるので、必要に応じて2のログも確認します。
1のログはaudit2allow
コマンドの入力値として利用することがあります。
この使い方については、#(参考) Custom Policyのビルドで説明します。
AVCが出力する監査ログ
SELinuxでアクセス拒否が発生すると、AVCが監査ログ (/var/log/audit/auditd
) を出力します。
このログは、sudo ausearch -m avc,user_avc
で確認できます。
sudo ausearch -m avc,user_avc # ---- # time->Sun Nov 21 17:29:41 2021 # type=AVC msg=audit(1637483381.836:583): avc: denied { read } for pid=10602 comm="mandb" name="anki.1" dev="dm-0" ino=919333 scontext=system_u:system_r:mandb_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
上記のログから、以下のような情報を読み取れます。
allow Statementと同等の情報が出力されていることがわかると思います。
- source (
mandb_t
) - target (
user_home_t
) - class (
file
) - permission (
read
)
sourceについてはPID (10602
) とプロセス名 (mandb
) も表示されます。
targetについてはファイル名が表示されますが、フルパスはわかりません (anki.1
)。
フルパスを確認したい場合は、次のセクションで扱うsetroubleshootのログを見ます。
以下のコマンドは、auditdが停止しているなどの理由でausearch
が利用できない場合に使います6。
auditdが停止していることは基本ないので、こちらのコマンドを覚える必要はありません。
# dmesg: auditdが動作していない時の代替手段 dmesg | grep -i -e type=1300 -e type=1400
また、Fedora環境の場合は監査ログが/var/log/messages
にも出力されていたので、以下のようにjournalctl
でもSELinuxの拒否ログを確認できました。
RHELやCentOS Streamでは/var/log/messages
やjournalctl
での拒否ログの確認はできませんでした。
journalctl -qen all -t audit -g denied # Nov 21 17:29:41 pc audit[10602]: AVC avc: denied { read } for pid=10602 comm="mandb" name="anki.1" dev="dm-0" ino=919333 scontext=system_u:system_r:mandb_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
(※) -g avc
よりも-g denied
の方がより必要な情報に絞ってフィルタしてくれます
setroubleshoot-serverが出力する追加情報
setroubleshootのログはsetroubleshoot-server
RPMパッケージがインストールされている場合のみ出力されます。
AVCから監査ログが出力された少し後のタイミングで、/var/log/messages
に出力されます。
以下のサンプルでは、2つのログが出力されています。
1つ目が発生したエラーの説明、2つ目が解決案の提示です。
journalctl -qen all -t setroubleshoot # Nov 21 17:29:45 pc setroubleshoot[11309]: SELinux is preventing mandb from read access on the file /usr/local/share/man/man1/anki.1. For complete SELinux messages run: sealert -l 5fe15040-2828-45d4-9e8c-ba8ef5781937 # Nov 21 17:29:45 pc setroubleshoot[11309]: SELinux is preventing mandb from read access on the file /usr/local/share/man/man1/anki.1. # ***** Plugin restorecon (99.5 confidence) suggests ************************ # If you want to fix the label. # /usr/local/share/man/man1/anki.1 default label should be usr_t. # Then you can run restorecon. The access attempt may have been stopped due to insufficient permissions to access a parent directory in which case try to change the following command accordingly. # Do # # /sbin/restorecon -v /usr/local/share/man/man1/anki.1 # ***** Plugin catchall (1.49 confidence) suggests ************************** # If you believe that mandb should be allowed read access on the anki.1 file by default. # Then you should report this as a bug. # You can generate a local policy module to allow this access. # Do # allow this access for now by executing: # # ausearch -c 'mandb' --raw | audit2allow -M my-mandb # # semodule -X 300 -i my-mandb.pp
1つ目のログにはあまり目新しい情報はありませんが、ファイルのフルパスが書いてあります。
2つ目のログでは、アクセス拒否を修正するための方法を2つ提示しています。
今回の場合は、以下の内容が書かれています。
restorecon /usr/local/share/man/man1/anki.1.
で直る (99.5%の確度)- allowルールを追加し、Custom Moduleとしてインストールする (1.49%の確度)
上記の提案に従えばエラーは解消できますが、最適な解決策とは限りません。
あくまで参考情報として扱い、最終的には自分で判断する必要があります。
setroubleshootが提案しなかった解決策を選択することもしばしばあります。
journalctl
が使えない環境では、/var/log/messages
を直接開いてsetroubleshoot
のログを確認します。
(参考) journalctlの利便性
journalctlは、journaldデーモンが収集したログを一括表示するコマンドです。
以下の点で使い勝手が良いです。
- journalctlは
adm
、wheel
、またはsystemd-journal
グループに所属する一般ユーザーでも使える。一方、ログファイルを直接開くにはroot権限が必要 /var/log/messages
や/var/log/audit/audit.log
など複数ファイルのログ情報を横断的に確認できる- rotateされず、かなり昔のログまで遡れる
- コマンドラインオプションのフィルタが便利
- (SELinuxの文脈では関係ないが、) systemd管理下のサービスの詳細ログが見える
今回のSELinuxのログ確認においても、ログファイルを直接開くよりも扱いやすいのでjournalctlを積極的に活用しています。
(参考) journalctlのオプション
journalctl
は複数のログファイルを一元的に表示します。
そのままだとログの量が多くて扱いづらいので、オプションで出力をフィルタして使います。
各種オプションの使い方を下表に示します。
オプション | 詳細 |
---|---|
-q |
|
-e |
|
-n [num] |
|
-t <id> |
|
-g <msg> |
|
--no-pager |
デフォルトのless形式のpagerを抑制する |
-f |
tail -f のようにログが発生するたびに画面を更新する |
(※) SELinuxとは無関係ですが、-u
で特定サービスの詳細ログを確認するオプションもよく使います。
例: journalctl -u NetworkManager
File Contextとの差分確認
本セクションは、#4. File Contextとの差分確認に関連して、より詳細な確認/修正手順を紹介します。
一番よく使うのは、以下の2コマンドです。
matchpathcon -V ファイルパス
restorecon -v ファイルパス
本セクションの手順を理解する前提として、File Contextとrelabelの仕組みを理解している必要があります。
参考: SELinux Type Enforcement - #File Contextとrelabel
File Contextの確認
File Contextの定義は、semanage fcontext -l
で確認します。
sudo semanage fcontext -l # SELinux fcontext type Context # / directory system_u:object_r:root_t:s0 # /.* all files system_u:object_r:default_t:s0 # (以下略)
特定ファイルのFile Context差分確認
ファイルのSecurity ContextとFile Contextの差分を確認するコマンドとして、matchpathcon -V
があります。
このコマンドにより、指定したファイルのSecurity ContextがFile Contextと一致するかを確認できます。
touch ~/a matchpathcon -V ~/a # /home/endy/a verified. # Security Contextを変えるとエラーとして検知される chcon -t tmp_t ~/a matchpathcon -V ~/a # /home/endy/a has context unconfined_u:object_r:tmp_t:s0, should be unconfined_u:object_r:user_home_t:s0 rm ~/a
基本matchpathcon -V
だけで事足りますが、他のコマンドも参考情報として紹介します。
ファイルのSecurity Contextを確認するには、ls -Z
を使います。
touch ~/a ls -Z ~/a # unconfined_u:object_r:user_home_t:s0 /home/endy/a
特定ファイルのFile Contextを確認するには、オプション無しでmatchpathcon ファイルパス
を実行します。
正規表現で書かれているsemanage fcontext -l
では探すのが難しいので、この用途ではmatchpathcon
の方が向いています。
matchpathcon /etc/hosts
# /etc/hosts system_u:object_r:net_conf_t:s0
matchpathcon
には、ファイルそのものではなく「File Contextの検索ワード」を指定します。
matchpathcon
は、File Contextの正規表現で引数の文字列 (ファイルパス) を検索した結果、ヒットしたSecurity Contextを返します。
したがって、matchpathcon
には、必ずフルパスを指定するようにしてください。
matchpathcon -V
には相対パスを指定しても良いですが、matchpathcon
には絶対パスを指定する必要があります。
# フルパスを指定しないと想定外の結果になる cd /etc matchpathcon hosts # hosts <<none>>
(参考) selabel_lookup -k
selabel_lookup -k
は、matchpathcon
と同じ意味を持ちます。
役割が被るので、selabel_lookup -k
を使うことは基本ないと思います。
selabel_lookup -k /etc/hosts # Default context: system_u:object_r:net_conf_t:s0
特定ファイルのrelabel
restorecon -v
によって、特定ファイルをrelabelします。
relabelとは、Security ContextをFile Contextの値と揃える処理のことです。
# 事前準備 touch ~/a chcon -t tmp_t ~/a # restorecon ls -Z ~/a # unconfined_u:object_r:tmp_t:s0 /home/endy/a restorecon -v ~/a # Relabeled /home/endy/a from unconfined_u:object_r:tmp_t:s0 to unconfined_u:object_r:user_home_t:s0 ls -Z ~/a # unconfined_u:object_r:user_home_t:s0 /home/endy/a # relabelが発生しない場合は、何も出力されない restorecon -v ~/a # (出力なし)
-v
オプションを指定しない場合、relabelが発生した場合も何も出力されません。
chcon -t tmp_t ~/a restorecon ~/a # (出力なし)
restorecon
は、fixfiles
と同様にデフォルトではTypeしかrelabelしません。
restorecon -F
によりUser, Role, Rangeも含めてrelabelできますが、実用上-F
オプションを使うことはないと思います。
全ファイルのFile Context確認
全ファイルのFile Contextを一括確認するには、fixfiles check
を実行します。
restorecon -nvR /
とほぼ同じ意味です。
sudo fixfiles check
# Would relabel /home/endy/a from unconfined_u:object_r:tmp_t:s0 to unconfined_u:object_r:user_home_t:s0
デフォルトではTypeの差分しか確認しません。
-F
オプションによってUser, Role, Rangeも差分チェックの対象にできますが、実用上使うことはないと思います。
ディレクトリやファイルを指定することで、対象を絞り込むことができます。
ディレクトリを指定した場合、配下のファイルを再帰的に確認します。
sudo fixfiles check ~
# Would relabel /home/endy/a from unconfined_u:object_r:tmp_t:s0 to unconfined_u:object_r:user_home_t:s0
(参考) 複数ファイルのrelabel
全ファイルに対して一括でrelabelを行う手順もありますが、基本的にこの操作を行うことはありません。
relabelは一見すると思考停止で実行すると良さそうにも思えますが、稀に新規作成されてから一度もrelabelされていないファイルのTypeが、relabelによって変化してしまうこともあります。
ファイル新規作成時のLabelとFile Contextの定義は多くの場合整合性が取れていますが、100%とは限りません。
何らかの理由で実行する場合も、事前にfixfiles check
によって影響範囲を確認することをおすすめします。
主なユースケースは、SELinuxをdisabledからenforcing/permissiveに変更した時です。
disabledの場合はファイルがラベル付けされていないので、有効化するタイミングでrelabel処理が必要となります。
システム全体をrelabelする手順はいくつかありますが、代表的なものは以下です。
以下を実行すると、Linuxの再起動中に全ファイルをrelabelします。
sudo fixfiles onboot sudo reboot
Linuxの起動中に、内部的に以下のコマンドを実行しています。
sudo fixfiles restore
sudo rm /.autorelabel
sudo fixfiles restore
を単体で使うことも可能です。
このコマンド自体はOS再起動を要求しません。
-v
オプションをつけることで、restoreconと同様にrelabel処理の詳細を表示します。
sudo fixfiles restore
は、sudo restorecon -R /
と同等です。
fixfilesについて、詳細は以下のリンクを参照してください。
参考: SELinux Type Enforcement - #fixfiles
(参考) File Contextの追加
レアケースではありますが、切り分けの結果File Contextの定義が足りないと判明することもあります。
そういった場合は、semanage fcontext -a ...
コマンドでFile Contextの定義をLocal Policyとして追加します。
その後、restorecon
などによってrelabelすることで、新しいFile ContextをファイルのSecurity Contextに反映します。
Local Policyで定義したFile Contextを削除するには、sudo semanage fcontext -d ...
を実行します。
Module PolicyとしてインストールされたFile Contextを変更/削除することはできないので、そこはご注意ください。
Local Policyの良いところは、ソースコードの記述・ビルド・インストールが不要なところです。
semanage
コマンドを1行実行するだけで手軽に実装できます。
Local Policyに対応するのは、semanage
のサブコマンドに含まれる以下の要素のみです。
詳細はman semanageを参照してください。
- File Context
- Boolean
- Port
- Login
- User
- 他
例: 特定ファイルのみ別のSecurity Contextを割り当てたい場合
以下のFile Context定義が示すとおり、デフォルトでは/tmp/
直下のファイルはrelabel対象外です。
<<none>>
とはrelabel時に対象外とすることを表します7。
sudo semanage fcontext -l | grep '^/tmp/\.\*' # SELinux fcontext type Context # /tmp/.* all files <<None>>
今回の例では、/tmp/a
をuser_home_t
にrelabelするようにFile Contextを追加定義します。
(※) 良いユースケースを思いつかず、実用性のない例となってしまいました。
(※) 構文について補足ですが、relabel対象外にするためのルールは右記のように指定します sudo semanage fcontext -a -t '<<none>>' '/tmp/a'
sudo semanage fcontext -a -t user_home_t '/tmp/a'
作成したFile Contextを確認します。
Local Policyはsemanage fcontext -l
の一番下に表示されます。
今回は、-C
オプションを追加で指定することでLocal Policyのみ表示します。
sudo semanage fcontext -lC # SELinux fcontext type Context # /tmp/a all files system_u:object_r:user_home_t:s0
実際にrelabelを試してみます。
touch /tmp/a ls -Z /tmp/a # unconfined_u:object_r:user_tmp_t:s0 /tmp/a restorecon /tmp/a ls -Z /tmp/a # unconfined_u:object_r:user_home_t:s0 /tmp/a
検証が終わったので元の構成に戻します。
semanage fcontext -d
でLocal Policyを削除できます。
rm /tmp/a sudo semanage fcontext -d '/tmp/a' sudo semanage fcontext -lC # (出力なし)
今回の例ではTypeのみ指定しましたが、追加のオプションによってUser, Role, Rangeなども指定してFile Contextを定義できます。
基本的には、Typeのみの指定で十分です。
例: File Contextとの差分によるSELinuxのエラー
File Contextとの差分によってSELinuxのエラーが発生している例を挙げます。
このようなエラーは、mvコマンドでファイル移動した後にrestoreconの実行を忘れることでしばしば発生します。
事前準備として、Apache (WEBサーバ) を事前にインストール・起動しておきます。
sudo dnf install httpd
sudo systemctl start httpd
a.html
ファイルを作成し、curlでアクセス確認します。
以下のやり方であれば問題なく通ります。
sudo bash -c 'echo "aaa" > /var/www/html/a.html' cat /var/www/html/a.html # aaa curl localhost/a.html # aaa
しかし、以下のように一旦ホームディレクトリでb.html
を作成してから/var/www/html/
にmvで配置した場合はアクセス失敗します。
sudo bash -c 'echo "bbb" > /home/endy/b.html' sudo mv /home/endy/b.html /var/www/html/ # a.htmlとb.htmlでPermissionに差はない ls -l /var/www/html # -rw-r--r--. 1 root root 4 Jan 15 17:35 a.html # -rw-r--r--. 1 root root 4 Jan 15 17:39 b.html curl localhost/b.html # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> # <html><head> # <title>403 Forbidden</title> # </head><body> # <h1>Forbidden</h1> # <p>You don't have permission to access this resource.</p> # </body></html>
403 Forbiddenは、アクセス権限がないことを表します。
ログは割愛しますが、sudo chmod o-r /var/www/a.html
を実行し、apacheユーザーからa.htmlにアクセスできない状態にしてからcurl /var/www/a.html
を実行したときも同じエラーになります。
Apacheのログを確認すると、Permission Deniedが発生していました。
先ほどのls -l
の実行結果から通常のFile Permissionsはa.htmlとb.htmlに差がないので、SELinuxが原因である疑いが強まります。
sudo tail -1 /var/log/httpd/error_log # [Sat Jan 15 17:40:44.293322 2022] [core:error] [pid 2795:tid 2895] (13)Permission denied: [client 127.0.0.1:37052] AH00132: file permissions deny server access: /var/www/html/b.html
続いて、SELinuxの監査ログを確認します。
httpd (httpd_t
) からb.html (user_home_t
) へのread
アクセスが拒否されていることがわかります。
このことから、今回のPermission Deniedエラーは、SELinuxが原因であったことがわかります。
journalctl -qen all -t audit -g denied --no-pager # AVC avc: denied { read } for pid=2795 comm="httpd" name="b.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
setroubleshootのログ確認は、ここでは割愛します。
また、Permissiveでの動作確認も省略します。
targetがファイルなので、File Contextとの差分を確認します。
matchpathcon -V
により、File Contextとの差分が存在することがわかりました。
matchpathcon -V /var/www/html/b.html # /var/www/html/b.html has context unconfined_u:object_r:user_home_t:s0, should be system_u:object_r:httpd_sys_content_t:s0
mvでファイル移動した場合、Security Contextが維持されます。
~/b.html
にファイル生成した段階ではuser_home_t
Typeを持っていて、それをmvで移動したためにFile Contextとの差分が生まれています。
relabelによって今回の事態が直ると想定されますが、念のためallowルールを確認します。
上述の監査ログをヒントに、拒否されるアクセスパターンからルール検索してみます。
検索にヒットしているように見えますが、httpd_read_user_content
というBooleanがFalseなのでこのルールは無効化されています。
このBooleanを有効化することでもエラーは解消しますが、アクセス許可の範囲が広すぎるのでセキュリティ観点では好ましくありません。
sesearch -A -s httpd_t -t user_home_t -c file -p read # allow httpd_t user_home_type:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True getsebool httpd_read_user_content # httpd_read_user_content --> off
続いて、relabelした後の条件でルール検索します (-t httpd_sys_content_t
)。
今度はヒットしたので、relabelによって今回のエラーが解消できるとわかります。
sesearch -A -s httpd_t -t httpd_sys_content_t -c file -p read # (一部抜粋) # allow httpd_t httpd_content_type:file { getattr ioctl lock map open read };
relabelして再度アクセスしてみます。
今度はアクセスに成功しました。
sudo restorecon -v /var/www/html/b.html # Relabeled /var/www/html/b.html from unconfined_u:object_r:user_home_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 curl localhost/b.html # bbb
File Contextとの差分によるSELinuxのエラーは、大抵の場合は操作ミスによって起こります。
今回の場合、以下のいずれかで対策可能です。
mv
した後はrestorecon
でrelabelするmv
の代わりにcp
を使う
または、次のセクションに示すとおりrestorecondを使うという手もあります。
restorecondによる利便性向上
restorecondデーモンを起動していると、対象のファイルが新規作成/移動されたタイミングで自動的にrestorecon
によってrelabelしてくれます。
ユーザーが頻繁にファイルを手動アップロードをするような環境でSELinuxを動作させる場合は便利かもしれません。
restorecondを起動していれば、mvコマンドでファイルを移動した後にrestoreconの実行を忘れても、SELinuxがエラーにならないという効果を見込めます。
詳細は#(参考) restorecondを参照してください。
(参考) restorecond
restorecondとは
restorecondとは、ファイルの更新を検知して自動的にrestorecon
を実行するデーモンプロセスです8。
/etc/selinux/restorecond.conf
に書いたファイルのみが対象です。
restorecondは、思いつく範囲では以下のような状況で役に立ちます。
- SELinuxを知っている管理者が構築したサーバをSELinuxに詳しくないユーザーが操作する場合
- 例: WEBサーバのファイルアップロードを手動で行う場合、
/var/www/
配下をrestorecondの監視対象としておくことで「mvの後のrestorecon忘れ」によるエラーを防ぐことができる
- 例: WEBサーバのファイルアップロードを手動で行う場合、
- SELinuxを意識していないソフトウェアをインストールしている場合
いずれもマイナーなケースかもしれませんが、運用にハマると便利だと思います。
restorecondは、デフォルトではインストールされていません。
policycoreutils-restorecond
RPMパッケージをインストールすることで導入できます。
ファイル更新の検知は、inotify9という仕組みを利用しています。
restorecond.conf
restorecondによる監視対象は、/etc/selinux/restorecond.conf
で指定します10。
ファイルの中は、デフォルトで以下のように指定されていました。
/etc/services /etc/resolv.conf /etc/samba/secrets.tdb /etc/updatedb.conf /run/utmp /var/log/wtmp /root/* /root/.ssh/*
ファイルの記載方法について公式情報はほとんどないのですが、実機で試したところ以下がわかりました。
- ディレクトリを指定しても、再帰的に配下を監視することはない
*
によるワイルドカード指定が可能 (例:/var/www/html/*
)*
は隠しファイルも含む (/var/www/html/*
を設定ファイルに書くと、/var/www/html/.c
も監視対象になる)#
で始まる行はコメント扱いになる**
による/
を含むワイルドカード指定は不可/home/endy/a a
のようにスペースを含むファイル名も認識される"
や'
も純粋に文字列として認識され、特殊な意味を持たない。"/home/endy"
のような指定は不可\
はエスケープ文字として扱われる。/home/endy/a a
と/home/endy/a\ a
は同じ意味restorecond.conf
に関しては、~
は使えない- 同一フォルダ内で
*
と他の文字列の組み合わせは不可 (例:/var/www/htm*
のような指定は、ワイルドカードとして動作しない) *
を2箇所以上使用すると動作しない (例:/var/www/html/*/*
、/var/www/htm*/*
)
なお、htm*
や/*/*/
のように、対応できない形式を指定した上でrestorecondを起動すると、以下のようなエラーが出ます。
sudo systemctl restart restorecond journalctl -eu restorecond # Jan 15 23:37:59 fedora35.test restorecond[5387]: Unable to watch (/var/www/htm*/*) No such file or directory # Jan 15 23:37:59 fedora35.test restorecond[5387]: Unable to watch (/var/www/*/*) No such file or directory
実際には上記のようなエラーが出ていても、restorecondを再起動した直後だけはrelabelしてくれました。
その後は、ログに書いてある通りWatchできていないため、mv
などで該当パスにファイルを移動してもrelabelしてくれなくなります。
このように、restorecond.confの書き方にはかなり癖があります。
restorecondを使う際は、設定変更のたびにこまめにjournalctl -eu restorecond
からエラーが出ていないか確認するようにしましょう。
(参考) restorecond_user.conf
restorecondは一般ユーザー権限でも起動できます。
しかし、手元の環境ではsystemctl --user start restorecond
で起動できるような一般ユーザー用のUnitファイルは自動生成していませんでした。
ユーザー権限で常時起動させたい場合は、自前でUnitファイルを作成する必要があります。
一般ユーザー権限で起動した場合、/etc/selinux/restorecond_user.conf
が読み込まれます。
このファイルでは~
がユーザーのホームディレクトリを意味する記号として利用できます。
restorecond
をユーザー権限で起動するユースケースは、あまり思いつきませんでした。
強いて言うなら、デスクトップ用途でLinuxを利用している場合に便利かもしれません。
一般ユーザー権限でrestorecondを起動するには、以下のコマンドを実行します。
restorecond -u
restorecondによってrelabelされるタイミング
restorecondによってrelabelされるタイミングは、実機検証で観測できた範囲では以下の通りです。
- restorecondが起動/再起動したとき
mv
によってファイル移動したときcp --archive
によってSecurity Contextも含めて保持しつつコピーしたときtouch
などによってファイルを新規作成したとき
一方で、既に存在するファイルに書き込んだり、touchによってmodified timeを更新しただけではrelabelされませんでした。
【重要】File Contextを更新したら、restorecondを再起動する
restorecondは、起動したタイミングでFile Contextをメモリ上に読み込んでいるようです。
そして、デーモンが再起動するまで再び読みに行くことはありません。
恐らく処理効率化のためだと思います。
ここで注意なのですが、semanage fcontext
コマンドなどによってFile Contextを更新した後、restorecondデーモンを再起動する必要があります。
再起動するまでの間、restorecondは古いFile Contextの定義に基づいて動作してしまいますので、くれぐれもご注意ください。
このことはドキュメントには特に書いていないのですが、私が実機操作中に本事象にあたったことで気づきました。
【重要】restorecond起動時に監視対象のファイルが存在すること
restorecondが起動したタイミングで設定ファイルに記述したファイルが存在しなかった場合、後追いでファイルを作成しても監視が開始されません。
この場合、restorecondのログとして以下のエラーメッセージが出ます。
systemctl status restorecond # (一部抜粋) # Jan 22 16:07:13 fedora35.test restorecond[770]: Unable to watch (/var/www/html/*) No such file or directory
このエラーが出た場合、ファイルを後追いで作成した上でrestorecondを再起動する必要があります。
例: restorecondによってmv直後に自動relabelする
#例: File Contextとの差分によるSELinuxのエラーと同様の操作を行った時、restorecondが有効の場合にどう変わるかを説明します。
事前準備として、restorecondをインストールします。
sudo dnf install policycoreutils-restorecond
続いて、/etc/selinux/restorecond.conf
を編集し、管理対象のファイルを追加します。
/var/www/html/*
なお、/var/www/html/dir/index.html
のようなファイルを監視するには、restorecond.conf上の記述でも/var/www/html/dir/*
のように階層を表現する必要があるのでご注意ください。
restorecondを起動、または再起動します。
sudo systemctl start restorecond # またはsudo systemctl restart restorecond
前回の例で使ったファイルが残っている場合は、一旦削除しておきます。
sudo rm /var/www/html/b.html
続いてサンプルファイルを設置します。
一旦ホームディレクトリ配下にファイルを作成してからmvで移動します。
mvはSecurity Contextを保持しますが、今回はrestorecondによってファイル配置直後にrelabelされています。
sudo bash -c 'echo "bbb" > /home/endy/b.html' ls -Z /home/endy/b.html # unconfined_u:object_r:user_home_t:s0 /home/endy/b.html sudo mv /home/endy/b.html /var/www/html/ ls -Z /var/www/html/b.html # unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/b.html
b.htmlにHTTPアクセスすると、今回は成功します。
curl localhost/b.html
# bbb
このようにrestorecondの働きにより、Apacheの基本オペレーションをSELinuxを意識することなく実施できました。
運用担当者が手動で配置/削除するファイルをrestorecondに監視させると、SELinuxに詳しい人とそうでない人が混在した現場においてもシームレスにLinuxサーバを運用できるようになります。
ポート番号と紐づくSecurity Contextの確認
本セクションは、#5. ポート番号と紐づくSecurity Contextの確認に続けてより具体的なコマンドを説明します。
アプリケーションの設定ファイルを書き換えてデフォルト値以外の待受ポート番号を利用する場合には、本セクションの手順の実施が必要かご検討ください。
ポート番号のSecurity Contextの確認コマンド
seinfo --portcon
によってポート番号のSecurity Contextを確認できます。
seinfo --portcon # Portcon: 652 # portcon sctp 1-511 system_u:object_r:reserved_port_t:s0 # (以下略)
seinfo --portcon
の後にポート番号を指定することで、表示内容を絞り込みできます。
seinfo --portcon 80 # Portcon: 4 # portcon sctp 1-511 system_u:object_r:reserved_port_t:s0 # portcon tcp 1-511 system_u:object_r:reserved_port_t:s0 # portcon tcp 80 system_u:object_r:http_port_t:s0 # portcon udp 1-511 system_u:object_r:reserved_port_t:s0
semanage port -l
でも確認できますが、root権限が必要です。
また、ポート番号の検索機能もありません。
基本的には、seinfo --portcon
の方がおすすめです。
sudo semanage port -l # SELinux Port Type Proto Port Number # afs3_callback_port_t tcp 7001 # (以下略)
ポート番号とSecurity Contextの紐付け追加コマンド
semanage port
コマンドにより、semanage fcontext
と同様にLocal Policyを定義してポート番号とSecurity Contextの紐付けを追加定義できます。
以下は、TCP10080をhttp_port_tに紐付けるコマンドです。
-a, --add
は追加、-t, --type
はType指定、-p, --proto
はTCP/UDPのプロトコル指定、最後のコマンドライン引数はポート番号の指定です。
-r
オプションでRangeも指定できますが、Type Enforcmentのみ利用する環境ではRangeの指定は不要です。
sudo semanage port -a -t http_port_t -p tcp 10080
作成したLocal Policyは、seinfo
またはsemanage
で確認できます。
semanage
に-C
オプションをつけることで、Local Policyのみに限定して表示できるので便利です。
sudo semanage port -lC # SELinux Port Type Proto Port Number # http_port_t tcp 10080
他の表示方法の場合、既存ルールと並んで表示されるので少々紛らわしいかもしれませんね。
seinfo --portcon 10080 # Portcon: 6 # portcon sctp 1024-65535 system_u:object_r:unreserved_port_t:s0 # portcon tcp 10080 system_u:object_r:http_port_t:s0 # portcon tcp 10080-10083 system_u:object_r:amanda_port_t:s0 # portcon tcp 1024-32767 system_u:object_r:unreserved_port_t:s0 # portcon udp 10080-10082 system_u:object_r:amanda_port_t:s0 # portcon udp 1024-32767 system_u:object_r:unreserved_port_t:s0 sudo semanage port -l | grep 10080 # SELinux Port Type Proto Port Number # amanda_port_t tcp 10080-10083 # amanda_port_t udp 10080-10082 # http_port_t tcp 10080, 80, 81, 443, 488, 8008, 8009, 8443, 9000
次のセクションで、具体例を示します。
例: httpdの待受ポート番号を10080に変更する
httpdの待受ポート番号をデフォルトのTCP80からTCP10080に変更する例で、ポート番号のSecurity Contextを変更する必要があるケースの例を示します。
事前準備として、httpdをインストールしておきます。
sudo dnf install httpd
そして/etc/httpd/conf/httpd.conf
を編集します。
もともとListen 80
と書かれていた部分をListen 10080
に書き換えます。
#Listen 80 Listen 10080
そしてhttpd.serviceを起動 (または再起動) します。
sudo systemctl start httpd # または、sudo systemctl restart httpd
すると、httpdの起動に失敗します。
httpdのログを確認すると、Permission deniedによってネットワークソケットのbindに失敗していることがわかります。
root権限でhttpdを起動していることから、SELinuxが怪しいということがわかります (※)。
(※) DACの世界では、root権限で起動したプロセスによる操作がPermission Deniedで拒否されることはありません。Permission Deniedになっているということは、MAC、つまりSELinuxによって拒否されているということです
# またはsystemctl status httpd journalctl -eu httpd # Jan 16 16:16:24 fedora35.test httpd[1274]: (13)Permission denied: AH00072: make_sock: could not bind to address [::]:10080 # Jan 16 16:16:24 fedora35.test httpd[1274]: (13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:10080
SELinuxのログを確認します。
httpd_t---[name_bind]--->amanda_port_t
というアクセスが拒否されていることがわかります。
ポート番号のTypeがhttp_port_t
ではないことから、ポート番号とSecurity Contextの紐付けに問題があると推測できます。
# 拒否内容: httpd_t---[name_bind]--->amanda_port_t journalctl -qen all -t audit -g denied # Jan 16 16:16:24 fedora35.test audit[1274]: AVC avc: denied { name_bind } for pid=1274 comm="httpd" src=10080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:amanda_port_t:s0 tclass=tcp_socket permissive=0
調べてみると、TCP10080は確かにamanda_port_tでした。
seinfo --portcon 10080 # (一部抜粋) # portcon tcp 10080-10083 system_u:object_r:amanda_port_t:s0
http_port_tにはTCP10080が含まれません。
sudo semanage port -l | grep ^http_port_t # http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
AVCのログを参考に、targetをhttp_port_tに変更した場合のallowルールが存在するかを確認します。
当然ながら、httpd_tはhttp_port_tをbindする権限を持っています。
sesearch -A -s httpd_t -t http_port_t -c tcp_socket -p name_bind # (一部抜粋) # allow httpd_t http_port_t:tcp_socket name_bind;
これまでの調査で、TCP10080ポートをhttp_port_tに紐付ければ問題が解決することがわかりました。
以下のコマンドでポート番号とSecurity Contextの紐付け設定を変更します。
sudo semanage port -a -t http_port_t -p tcp 10080 sudo semanage port -lC # SELinux Port Type Proto Port Number # http_port_t tcp 10080
今回は既存ファイルではなく、まだbindされていないポート番号のSecurity Context定義変更なので、relabelのような反映操作は不要です。
次回ポート番号が生成するときに、上記定義を参照してTCP10080がhttp_port_tと紐づきます。
では、改めてhttpdを起動します。
今度はちゃんと成功します。
sudo systemctl start httpd curl localhost:10080 # <!doctype html> # (以下略)
Security Contextの設定を元に戻します。
sudo semanage port -d -p tcp 10080
最後に補足ですが、今回追加したTCP10080
はamanda_port_t
のport rangeの一部として含まれていました。
今回の場合、既存のTCP10080-10083
という定義とTCP10080
は「範囲が完全に一致するわけではない」ためうまく定義できたようです。
既存のポート番号の範囲と完全に一致するような定義の仕方をしようとすると、以下のようにエラーになります。
sudo semanage port -a -t http_port_t -p tcp 10080-10083 # ValueError: Port tcp/10080-10083 already defined
Boolean有無の確認
本セクションは、#6. Boolean有無の確認に続けてより具体的なコマンドを説明します。
一番よく使うコマンドは、以下の3つです。
sesearch -A --type_trans ...
semanage boolean -l
setsebool -P ...
1つ目は既存のアクセス許可ルール等を検索するコマンドですが、Booleanの検索にも使えます。
拒否されたときの条件を指定して検索すると、「BooleanがTrueなら有効になるルール」がヒットすることがあります。
その場合、Booleanの有効化によってエラーを解消できると気づくことができます。
具体的な使用例は、#例: BooleanによるSELinuxエラー対応で紹介します。
本セクションの手順を理解する前提として、Booleanの概念について理解している必要があります。
参考: SELinux Type Enforcement - #Boolean
Booleanの一覧表示
Booleanの一覧表示をするコマンドは2種類あります。
getsebool -a
は一般ユーザー権限で実行可能なのに対し、semanage boolean -l
は特権が必要です。
semanage boolean -l
の方が詳細な情報を表示できるので、権限があるのであればこちらのほうがおすすめです。
getsebool -a # abrt_anon_write --> off # (以下略) sudo semanage boolean -l # SELinux boolean State Default Description # abrt_anon_write (off , off) Allow ABRT to modify public files used for public file transfer services.
semanage boolean -l
によって以下の情報がわかります。
- Boolean名
- 現在値
- OS起動直後にセットされる値 (※)
- 簡単な説明
(※) コマンドライン上はDefault
と表記されていますが、これは初期インストールされたLinuxのデフォルト値ではなく、PolicyかLocal Policyに書き込まれたBoolean値、つまりLinux再起動時にセットされるBoolean値を表しています。後述のsemanage boolean -m
によってBoolean値を変更するLocal Policyを追加すると、ここに表示されるDefault値も変化します。インストール直後の初期値を知りたい場合は、#manの横断検索を参考にしつつ、manコマンドからご確認ください
以下のようにgrepと組み合わせて使うことも多いです。
httpdではなくhttpでフィルタすることで、なるべく多くの情報にヒットするようにしています。
sudo semanage boolean -l | grep -i http # (一部抜粋) # awstats_purge_apache_log_files (off , off) Determine whether awstats can purge httpd log files. # httpd_can_connect_zabbix (off , off) Allow http daemon to connect to zabbix # mysql_connect_http (off , off) Allow mysqld to connect to http port
検索結果より、ApacheをZabbixやMySQLと連携するときに便利そうなBooleanが存在することがわかりました。
また検索ワードをhttpとしたことで、mysql_connect_http
も含めてヒットしました。
Booleanの意味を調べる
Booleanのざっくりとした意味であれば、semanage boolean -l
で知ることができます。
しかし、1行の説明でBooleanの意味を正確に理解することは難しいと思います。
sesearch -b Boolean 名
を実行することで、Booleanと紐づくアクセス許可ルールを表示できます。
これによりBooleanを変更することによって、どのルールが変化するのか最も直接的に理解できます。
sesearch -A --type_trans -b httpd_can_connect_zabbix # allow httpd_t zabbix_port_t:tcp_socket name_connect; [ httpd_can_connect_zabbix ]:True
上記出力より、httpd_can_connect_zabbix
というBooleanはApache (httpd_t
) がZabbixのポート番号 (zabbix_port_t
) に対して接続 (name_connect
) するallow Statementを有効化していることがわかりました。
Booleanの変更
Booleanを変更するコマンドは2種類あります。
setsebool
とsemanage boolean -m
です。
(※) Booleanの場合はもともと定義された値を書き換えることになるので、semanageのオプションは-a,--add
ではなく-m,--modify
になります
setsebool
とsemanage boolean -m
の間にあまり違いはないのですが、細かい機能に若干の違いがあります。
やりたいことに合わせて両者を使い分ける必要があります。
ただ、多くの場合はどちらを使っても問題ありません。
まず、両者に共通する機能から紹介します。
Local PolicyにBoolean値を保存しつつ、現在の挙動も変えるには以下のコマンドを実行します。
以下に2つのコマンドを記載していますが、どちらも全く同じ意味を持ちます。
# -1でBooleanに1をセット。-0でBooleanに0をセット sudo semanage boolean -m -1 httpd_can_connect_zabbix sudo setsebool -P httpd_can_connect_zabbix 1
設定の変更結果は以下のように確認できます。
semanage boolean -lC
のDefault列も書き換わっているので注意してください。
semanage
で表示されるDefaultは、SELinux起動時に読み込まれる値のことであり、Security Policyのソースコード上に定義されたデフォルト値のことではありません。
# -CでLocal Policyのみ表示 sudo semanage boolean -lC # SELinux boolean State Default Description # httpd_can_connect_zabbix (on , on) Allow http daemon to connect to zabbix # getsebool -a | grep httpd_can_connect_zabbixとほぼ同じ getsebool httpd_can_connect_zabbix # httpd_can_connect_zabbix --> on
続いて、semanage
とsetsebool
の細かな違いを一部だけ紹介します。
先ほどはsetsebool -P
でBoolean値を設定しましたが、-P
は、Persistentを意味します。
-P
を指定するとLocal Policyに書き込むことで設定を永続化します。
-P
を省略すると現在のBoolean値のみ書き換え、Local Policyは書き換えません。
-P
を指定しないsetsebool
は、一時的な試験をしたいときに便利かもしれません。
...が、setseboolを-P
なしで使うことは実用上ほぼないと思います。
Booleanの設定を戻したいときは、以下のように再度変更することで元に戻すのが基本です。
sudo semanage boolean -m -0 httpd_can_connect_zabbix # または、sudo setsebool -P httpd_can_connect_zabbix 0
設定をもとに戻しても、Local Policyには0
という値で記録されてしまいます。
sudo semanage boolean -lC # SELinux boolean State Default Description # httpd_can_connect_zabbix (off , off) Allow http daemon to connect to zabbix
semanage boolean
には、-d
で特定のLocal Policyを削除する機能がありません。
あるのは、-D
によってBooleanに関する「全ての」Local Policyを削除する機能のみです。
当然、影響範囲が大きいので気軽には実行できません。
# 「全ての」Booleanに関するLocal Policyを削除する sudo semanage boolean -D sudo semanage boolean -lC # (出力なし)
semanage boolean -D
を実行しても、現在のBooleanの値が書き換わるわけではないのでご注意ください。
事前にBooleanをデフォルト値に手動で戻した上で実行しないと、思わぬ挙動になる懸念があります。
最も確実なのは、semanage boolean -D
を実行した後にOSを再起動することです。
それによってBooleanがPolicyから再読込されて、確実に意図した値に収束します。
例: BooleanによるSELinuxエラー対応
ここでは、httpd_read_user_content
というBooleanを例に紹介します。
(※) セキュリティの観点ではこの機能を使うこと自体望ましくないとは思うのですが、主目的はSELinuxの検証なので目をつぶってください。
事前準備として、Apacheをインストールしておきます。
sudo dnf install httpd
そして/etc/httpd/conf.d/userdir.conf
を以下のように書き換えます。
元々はuserDir disabled
が有効でUserDir public_html
がコメントアウトされていますが、これら2行のコメントを反転させます。
<IfModule mod_userdir.c> #UserDir disabled UserDir public_html </IfModule>
この設定により、以下のようにホームディレクトリのHTMLファイルをWEB公開できるようになります。
$HOME/public_html/
配下に配置したhtmlファイルがhttpdに公開される- 上記に公開したファイルは、
http://xxx.com/~user/xxx.html
のようにHTTPアクセスすることで参照できる
具体例は後ほど示します。
ちなみに設定行がIfModule
に囲われていますが、以下の通りデフォルトでモジュールをロードしているので、上記設定はアクティブになります。
httpd -M | grep userdir # userdir_module (shared)
ここで、httpdを起動します。
特にエラーは起こりません。
sudo systemctl start httpd
続いて、ファイルを配置してコンテンツを公開します。
また、ACLによってapacheユーザーがホームディレクトリ配下を見えるようにrx
権限を付与しておきます (※)。
(※)セキュリティホールなので、本番環境ではやらないでください
mkdir ~/public_html chgrp apache ~/public_html echo aaa > public_html/a.html setfacl -m u:apache:rx ~
公開したコンテンツにアクセスを試みます。
すると、403 Forbidden応答が返ります。
403応答は、アクセス権限がないことを表します。
curl localhost/~endy/a.html # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> # <html><head> # <title>403 Forbidden</title> # </head><body> # <h1>Forbidden</h1> # <p>You don't have permission to access this resource.</p> # </body></html>
エラーの詳細をerror_log
から確認します。
Permission Deniedになっていることがわかります。
sudo tail /var/log/httpd/error_log # (一部抜粋) # [Sat Jan 22 22:49:19.460845 2022] [core:error] [pid 6034:tid 6077] (13)Permission denied: [client 127.0.0.1:50336] AH00035: access to /~endy/a.html denied (filesystem path '/home/endy/public_html') because search permissions are missing on a component of the path
SELinuxの監査ログを確認します。
ログが出ていることから、原因がSELinuxにあることがわかります。
journalctl -qen all -t audit -g denied --no-pager # Jan 22 23:26:51 fedora35.test audit[6034]: AVC avc: denied { getattr } for pid=6034 comm="httpd" path="/home/endy/public_html/a.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_user_content_t:s0 tclass=file permissive=0
必要なアクセス権限が上記のgetattr
のみとは限らないので、Permissiveにしてから再度アクセスを試みます。
そして監査ログを再度確認します。
監査ログから、getattr以外にもread, open, mapが必要であることがわかりました。
sudo setenforce 0 curl localhost/~endy/a.html # aaa sudo setenforce 1 journalctl -qen all -t audit -g denied --no-pager # Jan 22 23:28:20 fedora35.test audit[6034]: AVC avc: denied { getattr } for pid=6034 comm="httpd" path="/home/endy/public_html/a.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_user_content_t:s0 tclass=file permissive=1 # Jan 22 23:28:20 fedora35.test audit[6034]: AVC avc: denied { read } for pid=6034 comm="httpd" name="a.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_user_content_t:s0 tclass=file permissive=1 # Jan 22 23:28:20 fedora35.test audit[6034]: AVC avc: denied { open } for pid=6034 comm="httpd" path="/home/endy/public_html/a.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_user_content_t:s0 tclass=file permissive=1 # Jan 22 23:28:20 fedora35.test audit[6034]: AVC avc: denied { map } for pid=6034 comm="httpd" path="/home/endy/public_html/a.html" dev="dm-0" ino=131994 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_user_content_t:s0 tclass=file permissive=1
setroubleshootのログを見ると、今回はBooleanの有効化を提案しています。
確度は32.5%と低めです。
今回は練習なので、この提案がなかったものとして先に進みます。
journalctl -qen all -t setroubleshoot # (一部抜粋) # **** Plugin catchall_boolean (32.5 confidence) suggests **** # setsebool -P httpd_enable_homedirs 1
File Contextを確認してみます。
今回はmvで移動していないので、基本的に差分はないはずです。
実際に、Object Transitionが正しく働いたことで~/public_html
作成時に適切なTypeが割り当てられていることがわかりました。
matchpathcon -V ~/public_html/ # /home/endy/public_html verified. matchpathcon -V ~/public_html/a.html # /home/endy/public_html/a.html verified. sesearch --type_trans -s unconfined_t -D httpd_user_content_t # type_transition unconfined_t user_home_dir_t:dir httpd_user_content_t public_html;
次にBooleanの存在を確認します。
sesearchで拒否されたアクセスパターンを条件に指定してルール検索します。
複数のBooleanが出てきますが、httpd_enable_home_dirsが名前的に適切そうで、なおかつ全てのPermissionをカバーできていることがわかります。
sesearch -A --type_trans -s httpd_t -t httpd_user_content_t -c file -p getattr,read,open,map # allow httpd_t httpd_user_content_type:file map; [ httpd_enable_homedirs ]:True # allow httpd_t httpd_user_content_type:file { getattr ioctl lock open read }; [ httpd_enable_homedirs ]:True # allow domain file_type:file map; [ domain_can_mmap_files ]:True # allow httpd_t httpdcontent:file { append create getattr ioctl link lock open read rename setattr unlink watch watch_reads write }; [ ( httpd_builtin_scripting && httpd_unified && httpd_enable_cgi ) ]:True # allow httpd_t httpdcontent:file { execute execute_no_trans getattr ioctl map open read }; [ ( httpd_builtin_scripting && httpd_unified && httpd_enable_cgi ) ]:True
Booleanの意味を2通りの方法で確認します。
まずは、semanage
でざっくり確認します。
「httpdがホームディレクトリを読み込めるようにする」とあるので、今回の意図に合ってそうです。
sudo semanage boolean -l | grep httpd_enable_homedirs # httpd_enable_homedirs (off , off) Allow httpd to read home directories
続いて、Booleanの正確な効果をsesearchで確認します。
-b
でBoolean名を指定することで、特定のBooleanに関連するルールのみ表示するようフィルタしています。
合計で56行ものルールがヒットしました。
一部のみ抜粋していますが、他のBooleanと組み合わせることでNFSなどにも対応できるようにルールが書かれていることがわかります。
sesearch -A --type_trans -b httpd_enable_homedirs # (一部抜粋) # allow httpd_t httpd_user_content_type:file map; [ httpd_enable_homedirs ]:True # allow httpd_t httpd_user_content_type:file { getattr ioctl lock open read }; [ httpd_enable_homedirs ]:True # allow httpd_t nfs_t:dir { ioctl lock read }; [ use_nfs_home_dirs && httpd_enable_homedirs ]:True
以下のコマンドでBooleanを有効化します。
すると、今度はアクセスに成功することがわかります。
sudo setsebool -P httpd_enable_homedirs 1 curl localhost/~endy/a.html # aaa
以下のコマンドでBooleanを削除します。
0をセットするだけでは、httpd_enable_homedirs=0
という情報がLocal Policyに残ってしまうため、全Boolean設定をクリアしています。
非常に影響が大きいコマンドなので、意味を理解した上で実行してください。
このコマンドによって「全ての」Boolean「設定」値がデフォルトに戻ります。
現在のBoolean値は変わりません。
なお、以下の手順の場合は1コマンド目のsetseboolの-P
オプションは無くても結果は変わりません。
sudo setsebool -P httpd_enable_homedirs 0 #または、sudo semanage boolean -m -0 httpd_enable_homedirs sudo semanage boolean -D
ここで要注意なのですが、sudo semanage boolean -D
を実行しただけでは現在のBoolean値は書き換わりません。
必ず、setseboolによって現在の値もセットで書き換えるようにしてください。
また、途中で設定したACLももとに戻しておきます。
setfacl -b ~
Apacheアンインストールは最後にまとめて行います。
#(参考) Apacheのアンインストール
(参考) Custom Policyのビルド
本セクションは、#7. Custom Policyのビルドに続けてより具体的なコマンドを説明します。
Custom Policyを書く状況は滅多に発生しません。
本セクションに書いてある情報は、「必要なときに改めて詳細を思い出せれば良い」という気持ちで楽に読んでいただけますと幸いです。
Custom Policyのビルドの手順やソースコードの読み方について、詳細は別の記事に詳しくまとめています。
参考: SELinux Module Policyのソースコード読解、ビルド
本セクションでは、具体的なコマンドにフォーカスしてお伝えします。
どんなケースでビルドが必要となるのか?
Custom Policyを書く状況は、主に以下の2つがあると思います。
- 既に制御されているプロセス用にallowルールを追加する
- 全く制御されていないアプリケーションを新たに制御する
まずは1のケースです。
多くの場合、あるアクセスパターンが拒否されたことをきっかけに検討を始めると思います。
そして既存のType定義やallow Statementなどを確認し、必要なallow Statementが足りていないことを確認します。
そして、relabelやBoolean値の変更によってもこの問題を解決できなかったものとします。
この場合、既存のルールにallow Statementを追加定義することになります。
足りていないallow Statementは、自分で一から書くよりもaudit2allow
というプログラムから自動生成するのが簡単です。
audit2allow
は、AVC (Access Vector Cache) から発行されたアクセス拒否のログから自動的にallow Statementを発行するプログラムです。
詳細な手順は、#例1: 既に制御されているプロセス用にallowルールを追加するにて説明します。
続いて2のケースです。
自分でソースコードを書き、アプリケーションを開発したとしましょう。
このアプリケーション用のアクセス制御ルールは、デフォルトでは当然存在しません。
今回新たに開発したアプリケーションは、一部のアクセスについては既存のルールで許可されるかもしれません。
例えば、アプリケーションをbashからのコマンド実行によって起動する場合には、unconfined_t
Typeが割り当てられるので、大半のリソースには問題なくアクセスが可能です。
しかし、targeted policyによってアクセス制御の対象となっているリソース (※) については、デフォルトでアクセスが拒否されることも考えられます。
また、systemdから起動するように構成した場合、Type Transitionを別途定義しなければsystemdと同じinit_t
Typeになってしまうことでほとんどのアクセスに失敗します。
(※) 例えばsshdの設定ファイルなど
こういったケースに対応するために、2の場合は以下のように実装します。
- 専用のTypeやAttributeを定義する
- type_transitionルールによってTypeを割り当てる
- allowルールによってアクセスを許可する (1の例と同じ)
- File Contextを定義する
こちらの例は非常に応用的な内容であり、私のスキルも超えているので本記事ではビルドの手順のみお伝えします。
ソースコードの記述については触れません。
詳細な手順は、#例2: 全く制御されていないアプリケーションを新たに制御するにてお伝えします。
例1、例2の手順は、以下のRHEL8のマニュアルを参考にしました。
RHEL8 - Using SELinux - 7.2. Creating and enforcing an SELinux policy for a custom application
では、具体的な内容に入っていきます。
例1: 既に制御されているプロセス用にallowルールを追加する
本セクションでは、既存で足りていないallowルールを追加する手順をお伝えします。
正直、デフォルトのルールに足りていない設定を見つけるのは大変です...。
本セクションでは手順をお伝えすることを重視し、実用性のない具体例となっていますがご了承ください。
今回は、「httpd_tからtmp_tへのread権限を追加する」ことを例に説明します。
まずは事前準備として、Apacheをインストールしておきます。
sudo dnf install httpd
続いて、DocumentRootにtmp_t Typeを持つファイルを配置し、Apacheを起動します。
sudo bash -c 'echo aaa > /var/www/html/a' sudo chcon -t tmp_t /var/www/html/a ls -lZ /var/www/html/a # -rw-r--r--. 1 root root unconfined_u:object_r:tmp_t:s0 4 Jan 22 16:43 /var/www/html/a sudo systemctl start httpd
作成したファイルにアクセス確認します。
アクセス成功すればaaa
というファイルの中身が表示されるはずですが、Permission Deniedになります。
curl localhost/a # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> # <html><head> # <title>403 Forbidden</title> # </head><body> # <h1>Forbidden</h1> # <p>You don't have permission to access this resource.</p> # </body></html>
一旦Permissiveにして再度アクセスを実施し、エラーメッセージを全て出しておきます。
sudo setenforce 0 curl localhost/a # aaa sudo setenforce 1
続いて、SELinuxのログを確認します。
journalctl -qen all -t audit -g denied --no-pager # Jan 22 17:17:14 fedora35.test audit[1714]: AVC avc: denied { open } for pid=1714 comm="httpd" path="/var/www/html/a" dev="dm-0" ino=655778 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:tmp_t:s0 tclass=file permissive=1 # Jan 22 17:17:14 fedora35.test audit[1714]: AVC avc: denied { map } for pid=1714 comm="httpd" path="/var/www/html/a" dev="dm-0" ino=655778 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:tmp_t:s0 tclass=file permissive=1
ログから、以下のアクセスが拒否されていることがわかりました。
- Subject:
httpd_t
- Object:
tmp_t
- Class:
file
- Permission:
{ open map }
sesearchで確認すると、確かに該当するルールがないことがわかります。
mapのルールのみ1行表示されていますが、BooleanがFalseなので該当しません。
sesearch -A -s httpd_t -t tmp_t -c file -p open,map # allow domain file_type:file map; [ domain_can_mmap_files ]:True getsebool domain_can_mmap_files # domain_can_mmap_files --> off
allow Statementで表現すると、以下のようになります。
このルールをPolicy Packageファイル (.pp
) に変換し、インストールすることでエラーを解消できます。
allow httpd_t tmp_t : file { open map };
今回はエラー件数が少ないので自分でも簡単に書けますが、これをAVCから自動生成する方法もあります。
以下のようにAVCのログをaudit2allow
コマンドに渡します。
すると、AVCログに書いてある情報をパースしてallow Statementを表示してくれます。
以下はjournalctl
で行っていますが、もちろんsudo ausearch -m avc,user_avc | audit2allow
でも同様にallow Statementの表示が可能です。
journalctl -qen all -t audit -g denied --no-pager | tail -2 | audit2allow # #============= httpd_t ============== # allow httpd_t tmp_t:file open; # #!!!! This avc can be allowed using the boolean 'domain_can_mmap_files' # allow httpd_t tmp_t:file map;
必要に応じてこの情報をファイルに出力し、更にallow httpd_t tmp_t:file { open map };
のようにまとめて書くことでallowルールを楽に記述できます。
さて、ここからModule Policyをビルドしてインストールする手順に入ります。
以下の手順を行うために、前提としてpolicycoreutils-devel
RPMパッケージをインストールしておきます。
sudo dnf install policycoreutils-devel
今回はコマンドのみ示しますが、処理の中身やソースコードの書き方に興味のある方は後続記事のSELinux Module Policyのソースコード読解、ビルド - #Module Policyのビルドの流れをご覧ください。
上記リンクは参考情報のため、スキップしても問題はありません。
まず、作業用フォルダを作って入っておきます。
mkdir ~/work
以下の内容を持つ~/work/my_httpd.te
ファイルを作成します。
my_httpd
はModule Policyの名前にもなるので、必要に応じて名前を変更してください。
policy_module(my_httpd, 1.0.0) gen_require(`type httpd_t, tmp_t;') allow httpd_t tmp_t : file { open map };
以下のコマンドでビルドします。
makeによって必要な中間ファイルが自動生成していることがわかります。
今回はmy_httpd.fc
とmy_httpd.if
に何も書いていないので、自動生成したファイルの中身も空っぽです。
cd ~/work make -f /usr/share/selinux/devel/Makefile my_httpd.pp ls -F # my_httpd.fc my_httpd.if my_httpd.pp my_httpd.te tmp/
生成したmy_httpd.pp
ファイルがCustom PolicyのPolicy Packageです。
このファイルをインストールすることで、Security Policyが更新されて効果を発揮するようになります。
sudo semodule -i my_httpd.pp # または、sudo semanage module -a my_httpd.pp sudo semanage module -l | grep my_httpd # my_httpd 400 pp
再びcurlアクセスし、httpd_tからtmp_tにアクセスできるか確認します。
今度はうまくいきました。
curl localhost/a
# aaa
確認できたので、my_httpdをアンインストールしておきます。
sudo semodule -r my_httpd # libsemanage.semanage_direct_remove_key: Removing last my_httpd module (no other my_httpd module exists at another priority). # または、sudo semanage module -r my_httpd
作業フォルダも削除しておきます。
rm -rf ~/work
例2では、上記とは異なる方法でビルド・インストールします。
例1についても、例2と同様の方法でインストールすることは可能です。
その場合、以下のようなコマンドでビルド・インストールします。
manファイルやrpmファイルを自動生成したいときには便利かもしれません。
こちらのビルド手順については、次のセクションで詳細に説明します。
sudo dnf install policycoreutils-devel rpm-build mkdir ~/work cd ~/work sepolicy generate --init my_httpd vi my_httpd.te # (上述のmy_httpd.teと同じ内容に書き換える) # デフォルトの記述を削除 echo > my_httpd.fc echo > my_httpd.if # ビルド、インストール sudo ./my_httpd.sh # 確認 sudo semodule -l | grep my_httpd # my_httpd # 後片付け sudo semodule -r my_httpd sudo rm -rf ~/work
例2: 全く制御されていないアプリケーションを新たに制御する
既存で制御されていないアプリケーション用のCustom Policyを記述するには、allow Statementだけでなく以下のような定義が必要になります。
- Type, Attribute
- type_transitionルール (Domain Transition, Object Transition)
- allowルール (例1と同じ)
- File Context
こちらの例では、ソースコードの書き方までは触れません。
ビルドの手順のみざっとお伝えします。
ご参考までに、ソースコード記述の基礎については後続の記事で軽く触れています。
ソースコードを記述する場面はほぼないと思いますが、必要な方はご覧いただければと思います。
参考: SELinux Module Policyのソースコード読解、ビルド
では、手順の紹介に入ります。
例1と同様、手順の実施にはpolicycoreutils-devel
RPMパッケージが必要になります。
またビルド・インストールそのものには必須ではありませんが、RPMファイルの自動生成も行う場合はrpm-build
も必要になります。
sudo dnf install policycoreutils-devel rpm-build
先ほどと同様に、作業フォルダを作ります。
mkdir ~/work
作業フォルダに入り、必要なファイルを一括自動生成します。
本来であれば、引数には制御対象のプロセスを起動するための実行ファイルのパスを指定します。
ただ今回は練習ということで、「カレントディレクトリにあるmy_appファイル」を実行ファイルとして扱い、スクリプトを起動します (※)。
(※) 実運用で使う際は、こちらのファイルパスを正確に指定する必要があります (/usr/local/bin/my_app
など)。rpmファイルでインストールしたときに配置するファイルパスや、manページの記述、te/if/fcファイルの記述、シェルスクリプト中で自動実行されるrestoreconの対象ファイルなどに影響します。
my_appファイルは実際に存在しなくても以下の--init
コマンドは通りますが、後続のmy_app.sh
のrestoreconコマンドが失敗してしまいます。
それを防ぐため、今回はtouchコマンドによって事前にmy_app
ファイルを作っておきます。
touch my_app sepolicy generate --init my_app # *************************************** # Warning /home/endy/work/my_app does not exist # *************************************** # Created the following files: # /home/endy/work/my_app.te # Type Enforcement file # /home/endy/work/my_app.if # Interface file # /home/endy/work/my_app.fc # File Contexts file # /home/endy/work/my_app_selinux.spec # Spec file # /home/endy/work/my_app.sh # Setup Script
本来であれば、ここでte, if, fcファイルにアクセス制御ルールを記述します。
また、RPMファイルを活用する場合はspecファイルに細かいオプションを指定します。
ただ、今回はビルド手順の説明なのでその工程はスキップします。
ソースコード記述の流れとしては、以下のように進めるのが効率的かなと想定しています。
- allowルール以外を先に全て記述する
- Permissiveモードで動作確認する
- 監査ログを
audit2allow
に渡してallowルールを自動生成させたものを参考にallowルールも記述する
ですが、今回はソースコードの記述についても割愛します。
デフォルトの自動生成したソースコードを利用し、このままビルド・インストールします。
sudo ./my_app.sh # Building and Loading Policy # + make -f /usr/share/selinux/devel/Makefile my_app.pp # make: 'my_app.pp' is up to date. # + /usr/sbin/semodule -i my_app.pp # + sepolicy manpage -p . -d my_app_t # ./my_app_selinux.8 # + /sbin/restorecon -F -R -v /home/endy/work/my_app # ++ pwd # + pwd=/home/endy/work # + rpmbuild --define '_sourcedir /home/endy/work' --define '_specdir /home/endy/work' --define '_builddir /home/endy/work' --define '_srcrpmdir /home/endy/work' --define '_rpmdir /home/endy/work' --define '_buildrootdir /home/endy/work/.build' -ba my_app_selinux.spec # setting SOURCE_DATE_EPOCH=1642896000 # Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.GYx2lV # + umask 022 # + cd /home/endy/work # + '[' /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 '!=' / ']' # + rm -rf /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 # ++ dirname /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 # + mkdir -p /home/endy/work/.build # + mkdir /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 # + install -d /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/selinux/packages # + install -m 644 /home/endy/work/my_app.pp /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/selinux/packages # + install -d /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/selinux/devel/include/contrib # + install -m 644 /home/endy/work/my_app.if /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/selinux/devel/include/contrib/ # + install -d /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/man/man8/ # + install -m 644 /home/endy/work/my_app_selinux.8 /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/usr/share/man/man8/my_app_selinux.8 # + install -d /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64/etc/selinux/targeted/contexts/users/ # + /usr/lib/rpm/check-buildroot # + /usr/lib/rpm/redhat/brp-ldconfig # + /usr/lib/rpm/brp-compress # + /usr/lib/rpm/brp-strip /usr/bin/strip # + /usr/lib/rpm/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump # + /usr/lib/rpm/redhat/brp-strip-lto /usr/bin/strip # + /usr/lib/rpm/brp-strip-static-archive /usr/bin/strip # + /usr/lib/rpm/check-rpaths # + /usr/lib/rpm/redhat/brp-mangle-shebangs # + /usr/lib/rpm/redhat/brp-python-bytecompile '' 1 0 # + /usr/lib/rpm/redhat/brp-python-hardlink # Processing files: my_app_selinux-1.0-1.fc35.noarch # Provides: my_app_selinux = 1.0-1.fc35 # Requires(interp): /bin/sh /bin/sh # Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 # Requires(post): /bin/sh policycoreutils selinux-policy-base >= 35.7-1 # Requires(postun): /bin/sh policycoreutils # Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 # Wrote: /home/endy/work/my_app_selinux-1.0-1.fc35.src.rpm # Wrote: /home/endy/work/noarch/my_app_selinux-1.0-1.fc35.noarch.rpm # Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.CVjduT # + umask 022 # + cd /home/endy/work # + /usr/bin/rm -rf /home/endy/work/.build/my_app_selinux-1.0-1.fc35.x86_64 # + RPM_EC=0 # ++ jobs -p # + exit 0
シェルスクリプトの中身では、主に以下の処理を実行しています。
make -f /usr/share/selinux/devel/Makefile my_app.pp || exit
- ソースコードをビルド・パッケージ化してmy_app.ppを生成
/usr/sbin/semodule -i my_app.pp
- my_app Moduleのインストール
sepolicy manpage -p . -d my_app_t
- manページファイルを生成。配置はしない
/sbin/restorecon -F -R -v /home/endy/work/my_app
rpmbuild ...
- RPMファイルの生成
シェルスクリプトによって、my_appモジュールがインストールされています。
sudo semodule -l | grep my_app # my_app
RPMパッケージとmanファイルが生成しています。
file my_app_selinux.8 # my_app_selinux.8: troff or preprocessor input, ASCII text file noarch/my_app_selinux-1.0-1.fc35.noarch.rpm # noarch/my_app_selinux-1.0-1.fc35.noarch.rpm: RPM v3.0 bin i386/x86_64 my_app_selinux-1.0-1.fc35
manファイルはカレントディレクトリにありますが、/usr/share/man/man8
には配置されていないため、mandbには登録されていません。
man my_app_selinux # No manual entry for my_app_selinux man ./my_app_selinux.8 # ファイルを直接指定すれば見える
一旦Moduleをアンインストールして、RPMをインストールしてみます。
すると、Moduleがインストールされ、manも表示できるようになります。
Moduleを配布するときはRPM形式が便利です。
sudo semodule -r my_app sudo dnf install noarch/my_app_selinux-1.0-1.fc35.noarch.rpm sudo semodule -l | grep my_app # my_app man my_app_selinux # (表示される)
RPMをアンインストールすると、my_app Moduleもアンインストールされます。
sudo dnf remove my_app_selinux sudo semodule -l | grep my_app # (出力なし) man my_app_selinux # No manual entry for my_app_selinux
my_app_selinux.spec
を見るとインストール/アンインストール時に実行されるコマンドが書いてあります。
また、rpm -ql my_app_selinux
からRPMインストール時に配布するファイルを確認できます。
興味のある方はぜひ実機で試してみてください。
最後に、workフォルダを削除しておきます。
sudo rm -rf ~/work
(参考) SELinuxの確認コマンド
ここでは、他のセクションで言及されなかったコマンドや調査方法を中心にここで紹介します。
また、#sesearchと#seinfoの詳しい使い方にも軽く触れます。
sesearch
sesearchによって、SELinuxのアクセス制御ルールを検索できます。
検索対象を指定するオプションを1つ、または複数指定する必要があります。
検索対象を指定するオプションは、主に以下です。
オプション | 意味 |
---|---|
-A |
allow, allowxpermルールを検索する |
--type_trans |
type_transitionルールを検索する |
デフォルトでは、全てのルールを表示します。
フィルタしないと、数万行ものルールが表示されてしまいます。
フィルタを指定するオプションは、主に以下があります。
オプションに指定するパラメータは、カンマ区切りで複数指定できます。
オプション | 意味 |
---|---|
-s Type |
source Typeでフィルタする。Attributeも指定可 |
-t Type |
target Typeでフィルタする。Attributeも指定可 |
-c Class |
Object Class でフィルタする |
-p Permission |
Permission でフィルタする |
-b Boolean |
Boolean に関連するもののみにフィルタする |
-D Default |
--type_trans などと一緒に使い、Default Typeなどでフィルタする |
デフォルト動作では、例えば-s Type
を指定するとsourceに指定したType、またはそのTypeを内包するAttributeが指定されたルールのみにフィルタされます。
以下のオプションを-s
や-t
と共に指定することで、上記のフィルタ挙動を変更することができます。
オプション | 意味 |
---|---|
-ds |
-s Type と共に指定すると、そのTypeを内包するAttributeがヒットしなくなる |
-dt |
-ds と同様。-t とセットで使う |
-rs |
-s の検索に正規表現を使用する |
-rt |
-t の検索に正規表現を使用する |
他にも色々オプションがあるので、お試しください。
seinfo
seinfoは、Type、Attribute、Class、ポート番号などのObjectの情報を表示するコマンドです。
何も付けずに実行すると、統計情報を表示します。
seinfo # (一部抜粋) # Classes: 134 Permissions: 456 # Types: 4990 Attributes: 251
代表的なオプションは以下の通りです。
オプション | 意味 |
---|---|
-t |
Typeの一覧表示 |
-a |
Attributeの一覧表示 |
-c |
Classの一覧表示 |
--common |
Commonの一覧表示 |
--portcon |
ポート番号とSecurity Contextの紐付け一覧表示 |
-x
と併用すると、オプションによってはより詳細な情報が表示されます。
よく使うのは、以下の組み合わせです。
組み合わせ | 用途 |
---|---|
-xa , -xt |
TypeとAttributeの紐付け |
-xc , -x --common |
ClassとPermissionの紐付け |
例えば、TypeやAttributeの意味を調べたいときは以下のようなコマンドをよく使います。
man -Kw <type|attribute|boolean>
→ #manの横断検索seinfo -xa <attribute>
ClassとPermissionの紐付けを知りたい時はseinfo -xc
とseinfo -x --common
を使います。
更にPermissionの意味を知りたいときは、The SELinux NotebookのAppendix Aを参照します。
現在のSecurity Contextの調べ方
現在のSecurity Contextを調べるコマンドは、以下のとおりです。
調査対象 | コマンド例 |
---|---|
ファイル | ls -Z |
プロセス |
|
ポート番号 |
|
ユーザー(※) | id -Z |
(※) id -Z
はRBACを利用している環境においては便利なこともありますが、基本的には使いません。id -Z
とps -Z
で表示されるログインシェルのコンテキストは同じなので、id -Z
が必須になる場面も実はありません。
(※) ss -Z
というコマンドはほぼ使わないため、表に載せていません
(参考) manの横断検索
selinux-policy-doc
RPMパッケージがインストール済みであれば、*_selinux
というmanページを参照できます。
情報量はmanページによってまちまちですが、少なくともBooleanの初期値は確実に書いてあります。
他の情報はあまり書いてないことが多いので、「見つかったらラッキー」ぐらいの気持ちで利用するのがちょうど良いと思います。
例えば、cron_userdomain_transition
というBoolean値のデフォルト値を知りたかったとすると、以下のようにキーワード検索します。
man -Kw cron_userdomain_transition # /usr/share/man/man8/openshift_app_selinux.8.gz # /usr/share/man/man8/openshift_selinux.8.gz # /usr/share/man/man8/staff_selinux.8.gz # /usr/share/man/man8/sysadm_selinux.8.gz # /usr/share/man/man8/unconfined_selinux.8.gz # /usr/share/man/man8/user_selinux.8.gz # /usr/share/man/man8/crond_selinux.8.gz
続いて、検索にヒットしたmanページを確認します。
試しに一番上にあるman openshift_app_selinux
を確認すると、デフォルトがTrueであることがわかります。
cron_userdomain_transition boolean. Enabled by default.
他にもTypeやAttributeでも検索できますが、ヒットしなかったり、必要な情報が書いていないことがほとんどです。
manで情報が見つからなかった場合は、以下のように別のアプローチに切り替える必要があります。
(参考) Local Policyのexport/import
semanageを活用することで、ソースコードを記述すること無くLocal Policyを定義してSELinuxの動作を変えることができます。
このLocal Policyをexport、importすることで、複数のマシンに同じ設定を適用することが可能です。
exportは以下のコマンドで行います。
出力されているのはsemanage
のサブコマンドです。
最初に-D
で設定をクリアしてから、一番下の行で今の設定を適用するようなスクリプトになっています。
sudo semanage export # boolean -D # login -D # interface -D # user -D # port -D # node -D # fcontext -D # module -D # ibendport -D # ibpkey -D # permissive -D # boolean -m -1 httpd_can_connect_zabbix
exportした内容は、-f
オプションでファイルに保存できます。
sudo semanage export -f semanage.txt cat semanage.txt # boolean -D # login -D # interface -D # user -D # port -D # node -D # fcontext -D # module -D # ibendport -D # ibpkey -D # permissive -D # boolean -m -0 httpd_can_connect_zabbix
exportしたファイルをsemanage import -f
に渡すことで、設定をインポートできます。
exportしたファイルをGit管理して、Ansibleのような自動化ツールによって共通したSELinux設定を配布するような使い方ができるのではないかと思います。
sudo semanage boolean -D sudo semanage boolean -lC # (出力なし) sudo semanage import -f semanage.txt sudo semanage boolean -lC # SELinux boolean State Default Description # httpd_can_connect_zabbix (off , off) Allow http daemon to connect to zabbix
(参考) Apacheのアンインストール
本記事で検証用にインストールしたApacheのアンインストール手順を書いておきます。
sudo dnf remove httpd sudo rm -rf /etc/httpd /var/log/httpd /var/www ~/public_html/
今回編集したファイル+αはアンインストール後も残るので、手動である程度消しています。
まとめ
SELinuxでアクセス拒否エラーが出た時の切り分け手順と、原因調査や修正に必要なコマンドを紹介しました。
また、基本操作としてSELinuxを無効化する手順にも触れました。
次の記事
SELinuxシリーズの本編は、本記事で最後です。
次回以降の記事は、全て参考情報です。
次の記事では、Type Enforcement以外のアクセス制御方式であるRBAC, UBAC, MLS, MCSの概要を紹介します。
MCS以外についてはmls policyでしか基本使われないため、多くの方にとってはあまり知らなくても良い内容だと思います。
「なぜ多くの人は知らなくてよいのか」といった部分が気になる方は、その部分を中心にお読みいただければと思います。
-
Fedora 34 Release Notes - #Support for disabling SELinux through /etc/selinux/config has been removed↩
-
RHEL8 - Using SELinux - Changing SELinux modes at boot time↩
-
The SELinux Notebook - Policy Store Configuration Files - #active/file_contexts↩
-
The SELinux Notebook - Global Configuration Files - #/etc/selinux/restorecond.conf↩