Linux を利用していると、SELinux や AppArmor という名前を目にすることがあります。 RHEL 系ディストリビューションでは SELinux、Ubuntu では AppArmor が標準的に利用されています。どちらも Linux のセキュリティを強化する仕組みですが、運用現場では次のように扱われることも少なくありません。
- SELinux が原因で動かないため無効化する
- AppArmor を complain モードにしたまま運用する
- セキュリティ機能であることは知っているが、何を制御しているかは分からない
これは、単に管理者の知識が不足しているために起きる問題ではありません。 SELinux や AppArmor は、通常の所有者、グループ、パーミッションとは異なる判断基準を Linux へ追加します。そのため、従来のアクセス制御だけを見ていると、拒否された理由が見えにくくなります。 本記事では、Linux のアクセス制御を DAC と MAC から整理した上で、SELinux と AppArmor の違いを解説します。 単なる機能比較ではなく、なぜこれらの仕組みが無効化されやすいのか、その運用上の難しさがどの設計思想から生じているのかまで掘り下げます。
書籍
Linux サーバーセキュリティ徹底入門
Linux サーバーのアクセス制御、権限管理、ログ、攻撃面の削減を体系的に確認したい場合の参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
Linux の標準的なアクセス制御は DAC である
Linux で通常利用するファイルパーミッションは、DAC(Discretionary Access Control:任意アクセス制御)に分類されます。 例えば、次のようなパーミッションです。 -rwxr-x--- Linux では、ファイルやディレクトリに対する権限を、主に次の区分で管理します。
- 所有者
- グループ
- その他のユーザー
ACL を利用すれば、特定のユーザーやグループへ、より細かな権限を追加することもできます。 DAC は柔軟で分かりやすい仕組みですが、アプリケーションが侵害された場合には限界があります。 例えば、Web サーバープロセスの脆弱性を悪用され、攻撃者に任意のコードを実行されたとします。 この場合、攻撃者は Web サーバープロセスに付与された DAC 上の権限を利用できます。プロセスから読み取れるファイルは攻撃者からも読み取れ、書き込める場所には不正なファイルを作成される可能性があります。 DAC が判断するのは、主に次の関係です。
- 実行ユーザーの UID・GID
- 対象の所有者・グループ・パーミッション・ACL
しかし、DAC だけでは次のような判断は困難です。
DAC だけでは、Web サーバープロセスである以上、DAC 上は読めるファイルであってもアクセスさせない、という用途に基づく制御は困難です。
そこで必要になるのが、DAC とは別の判断基準です。
MAC は DAC を通過したアクセスをさらに制限する
SELinux と AppArmor は、どちらも MAC(Mandatory Access Control:強制アクセス制御)を実現する仕組みです。 DAC では、ファイル所有者などがアクセス権を設定します。 これに対して MAC では、システムに定義されたセキュリティポリシーがアクセスの可否を判断します。 ここで重要なのは、DAC と MAC が完全に並列で評価されるわけではないことです。 Linux では、まず DAC による判定が行われます。DAC で拒否された場合、その時点でアクセスは失敗し、SELinux のポリシー判定まで進みません。
- アクセス要求
- DAC による判定
- 拒否 → アクセス失敗
- 許可
- MAC による判定
- 拒否 → アクセス失敗
- 許可 → アクセス成功
そのため、SELinux の AVC ログが記録されていない場合、SELinux より前に DAC で拒否されている可能性があります。 MAC は DAC の代わりになるものではありません。 DAC を通過したアクセスに対して、適用されるポリシーとドメインに応じた追加制約を加える仕組みです。 ただし、SELinux が有効であれば、システム上のすべてのプロセスが同じ強度で制約されるわけではありません。 RHEL 系ディストリビューションで標準的に利用される targeted ポリシーでは、主にネットワークサービスなど、保護対象として選ばれたプロセスが個別のドメインへ閉じ込められます。一方、多くの一般的なユーザープロセスや、専用ポリシーが用意されていないプロセスは、unconfined_t などの非制約ドメインで動作します。 概念的には、次のように分かれます。
- confined なプロセス
- 例:httpd_t、named_t など
- 用途ごとに定義された SELinux ポリシーで制約される
- unconfined なプロセス
- 例:unconfined_t
- SELinux は有効だが、広い操作が認められる
したがって、SELinux が Enforcing であることと、すべてのプロセスが厳密に閉じ込められていることは同じではありません。 本記事では、主に targeted ポリシーによって明示的に制約されているプロセスを前提として説明します。
SELinux はラベルの関係を基準に判断する
SELinux では、プロセスやファイルなどへセキュリティコンテキストと呼ばれるラベルを付与します。 現在のプロセスへ付与された SELinux ドメインは、次のように確認できます。
ps -eZ例えば、Apache HTTP Server などのプロセスは、通常、次のようなドメインで動作します。 httpd_t Web コンテンツ用のファイルには、次のようなセキュリティコンテキストが付与されます。 system_u:object_r:httpd_sys_content_t:s0 このうち、一般的な Type Enforcement で特に重要なのが、次のタイプです。 httpd_sys_content_t SELinux の Type Enforcement では、概念的に次の要素を組み合わせてアクセスを判断します。
- ソースタイプ
- ターゲットタイプ
- オブジェクトクラス
- 要求されたパーミッション
例えば、次のような組み合わせです。
- ソースタイプ httpd_t
- ターゲットタイプ httpd_sys_content_t
- オブジェクトクラス file
- パーミッション read
この組み合わせに対してポリシー上の許可が存在すれば、読み取りが認められます。 これは無数にあるルールの一例であり、httpd_t と httpd_sys_content_t が単純な一対一対応になっているわけではありません。 また、このような細かな Type Enforcement の効果が明確に現れるのは、httpd_t のような confined ドメインで動作するプロセスです。unconfined_t で動作するプロセスに対して、同じ水準の閉じ込めを期待することはできません。 重要なのは、SELinux がファイルパスだけを見て判断しているわけではないことです。 例えば、ファイルが次の場所に存在していたとします。 /var/www/html/index.html 通常の管理者は、このパスを見れば Web コンテンツだと判断するでしょう。 しかし SELinux は、パスそのものではなく、そのファイルへ実際に付与されたラベルを基にポリシーを適用します。 ファイルが /var/www/html に存在していても、ラベルが user_home_t のままであれば、httpd_t で動作する Web サーバープロセスからのアクセスが拒否されることがあります。 SELinux では、ファイルが「どこに置かれているか」だけではなく、何としてラベル付けされているかが重要です。
mv ではパスが変わってもラベルが変わらない
SELinux の設計思想が、運用上の問題として最も分かりやすく現れるのが、mv によるファイル移動です。 例えば、ホームディレクトリで作成した HTML ファイルを、Web コンテンツ用ディレクトリへ移動したとします。
mv /home/user/index.html /var/www/html/index.html同一ファイルシステム内の一般的な mv では、ファイルの実体を作り直すのではなく、ディレクトリエントリが変更されます。 そのため、移動元のファイルへ付与されていた SELinux ラベルも、そのまま保持されます。 移動前のラベルが次のようなものであったとします。 unconfined_u:object_r:user_home_t:s0 mv 後もこのラベルが残れば、パスは次のように変わっていても、 /var/www/html/index.html SELinux から見たファイルのタイプは、依然として user_home_t です。 その結果、次の条件がすべて成立していても、Apache HTTP Server から読み取れないことがあります。
- ファイルが
/var/www/htmlに存在する - 所有者が正しい
- パーミッションが
0644である - DAC 上は Web サーバープロセスから読み取り可能である
管理者がパスとパーミッションだけを確認していると、設定に問題がないように見えます。 しかし、SELinux 上では次の関係になっています。
- httpd_t
- ↓ read
- user_home_t
- ポリシー上で許可されていない
ラベルは次のコマンドで確認できます。
ls -lZ /var/www/html/index.html想定されるラベルへ戻す場合は、restorecon を使用します。
restorecon -v /var/www/html/index.htmlディレクトリ以下を再帰的に確認・修正する場合は、次のように実行します。
restorecon -Rv /var/www/htmlcp では移動先の規則に従ってラベルが付与される
cp は、一般的な同一ファイルシステム内の mv とは異なります。
cp /home/user/index.html /var/www/html/index.htmlcp では、移動先に新しいファイルが作成されます。 セキュリティコンテキストを明示的に保持するオプションを指定していない場合、新しいファイルには、作成元プロセスと移動先ディレクトリに対する SELinux のラベル付与規則に基づいたコンテキストが設定されます。 そのため、/var/www/html に対する標準的なラベル規則が正しければ、コピーされたファイルには httpd_sys_content_t が付与されます。 基本的な違いは次のとおりです。
| 操作 | ファイルの扱い | SELinux ラベルの典型的な挙動 |
|---|---|---|
同一ファイルシステム内の mv | 既存ファイルの配置を変更 | 移動元のラベルを保持する |
通常の cp | 移動先に新しいファイルを作成 | 移動先のラベル付与規則に従う |
cp --preserve=context | ラベルを保持してコピー | コピー元のコンテキストを保持する |
restorecon | 標準ラベルへ修正 | ポリシー上のパス定義に基づいて再設定する |
別ファイルシステムへの mv は、内部的にコピーと元ファイルの削除に相当する動作になる場合があるため、同一ファイルシステム内の mv と同じとは限りません。 ただし、SELinux 運用で頻発する典型的な問題は、同一ファイルシステム内で mv した結果、移動元のラベルが意図せず残ることです。
独自ディレクトリには永続的なラベル規則を定義する
Web コンテンツを標準の /var/www/html ではなく、独自のディレクトリへ配置する場合もあります。 例えば、次のディレクトリを利用するとします。 /opt/www 一時的にラベルを変更するだけであれば、chcon を利用できます。
chcon -R -t httpd_sys_content_t /opt/wwwしかし、chcon による変更は、restorecon の実行やファイルシステムの再ラベル付けによって失われる可能性があります。 独自の配置先を恒久的に利用する場合は、semanage fcontext でラベル規則を定義します。
semanage fcontext -a -t httpd_sys_content_t '/opt/www(/.*)?'
restorecon -Rv /opt/wwwここで行っているのは、現在存在するファイルへ一時的にラベルを付けることではありません。 そのパスに存在するファイルを、今後どのタイプとして扱うかをシステムの規則として定義することです。
AppArmor はプログラムのプロファイルとパスを中心に制御する
AppArmor も MAC を実現しますが、SELinux とは制御モデルが異なります。 AppArmor では、主にプログラムごとのプロファイルを作成し、そのプログラムがアクセスできるパスや実行できる操作を定義します。 例えば、概念的には次のようなルールを記述します。
- /var/www/html/** r,
- /var/www/html/uploads/** rw,
これは、対象プロセスに対して次の操作を許可することを意味します。
/var/www/html以下は読み取り可能/var/www/html/uploads以下は読み書き可能
SELinux との違いを単純化すると、次のように整理できます。
SELinux
- プロセスのラベル
- 対象のラベル
- オブジェクトクラス
- 操作
AppArmor
- プログラムのプロファイル
- 対象パス
- 操作
AppArmor は、管理者が普段利用しているパスの概念をポリシーへ直接記述しやすいため、初期理解は比較的容易です。 一方、パス名やマウント構成を変更した場合には、プロファイルとの整合性を確認する必要があります。 SELinux が対象へ付与されたラベルを中心に考えるのに対し、AppArmor は制約対象となるプログラムとアクセス先のパスを中心に考えます。 AppArmor でも、プロファイルが存在しないプロセスや、プロファイルが適用されていないプロセスは、すべてが自動的に厳密な制約下へ置かれるわけではありません。 SELinux の unconfined_t と完全に同一の仕組みではありませんが、MAC が有効であることと、全プロセスが個別ポリシーで閉じ込められていることは別問題という点は共通しています。
SELinux と AppArmor はポリシーモデルが異なる
SELinux と AppArmor は、どちらも Linux Security Module の仕組みを利用して MAC を実現しますが、ポリシーの表現方法が異なります。
| 項目 | SELinux | AppArmor |
|---|---|---|
| 主な判断基準 | プロセスや対象へ付与されたラベル | プログラムのプロファイルとパス |
| 基本的な管理単位 | ドメイン、タイプ、クラス、権限 | プログラムごとのプロファイル |
| 配置変更の影響 | パスが変わってもラベルが残る場合がある | パス変更に応じてプロファイル確認が必要 |
| ポリシー表現 | Type Enforcement、ロール、ユーザー、MLS/MCS など | パス、Capability、ネットワークなどをプロファイルへ記述 |
| 初期理解 | ラベルとポリシーの関係を理解する必要がある | パス中心で比較的追いやすい |
| 主な採用例 | RHEL、Fedora、CentOS Stream | Ubuntu、SUSE |
SELinux の特徴は、単に細かな設定ができることではありません。 プロセスドメイン、オブジェクトタイプ、オブジェクトクラス、操作権限など、複数の属性を組み合わせてアクセス制御を表現できます。 一方、AppArmor は、アプリケーション単位のプロファイルへ、そのプログラムがアクセス可能なパスや操作を記述します。 したがって、単純にどちらが強力かを比較するよりも、システム全体をラベル関係として表現するか、アプリケーションの振る舞いをパス中心に記述するかという違いとして捉える方が適切です。
なぜ SELinux は無効化されやすいのか
SELinux が無効化されやすい最大の理由は、セキュリティ機能として厳しすぎるからではありません。 管理者が確認している状態と、SELinux が判定に利用している状態が異なるためです。 先ほどの mv の例では、管理者は次の項目を確認します。
- パス
- 所有者
- グループ
- パーミッション
- Web サーバーの設定
しかし、SELinux はさらに次の情報を確認しています。
- プロセスのドメイン
- ファイルのタイプ
- オブジェクトクラス
- 要求された操作
- ポリシー上の許可関係
そのため、次の流れが起きます。
- ファイルを正しい場所へ mv した
- パスとパーミッションは正しい
- ラベルだけが user_home_t のまま残った
- SELinux がアクセスを拒否した
- Permissive にすると動作した
- SELinux そのものが原因だと誤認した
- 無効化した
ここで重要なのは、Permissive に変更して動作したことです。 これは SELinux を無効化すべき証拠ではありません。 SELinux のポリシー判定がアクセス拒否に関係していることを切り分けられたという意味です。 mv によるラベル不整合は具体的な一例ですが、その背後にはより一般的な構造があります。 Linux のアクセス可否を決める状態が、DAC だけでなく MAC によって増えているということです。
- DAC
- UID
- GID
- パーミッション
- ACL
- MAC
- プロセスのドメインまたはプロファイル
- 対象のラベルまたはパス
- 要求された操作
- ポリシールール
ただし、SELinux の targeted ポリシーでは、すべてのプロセスが同じ粒度で制約されているわけではありません。 実際にどの程度の追加制約が適用されるかは、そのプロセスが httpd_t などの confined ドメインで動作しているか、unconfined_t などの非制約ドメインで動作しているかによって異なります。 問題は、状態が増えたこと自体ではありません。 増えた状態と、その適用範囲が、監視、設計書、構築手順、障害対応手順へ取り込まれていないことです。 パスとパーミッションしか確認しない運用手順では、ラベルやプロファイルの不整合は見つかりません。 反対に、SELinux が Enforcing であることだけを確認して、対象プロセスが実際には unconfined_t で動作している事実を見落とせば、存在しない閉じ込め効果を期待することになります。 SELinux の有効・無効だけではなく、どのプロセスへ、どのポリシーが、どのドメインとして適用されているかまで確認する必要があります。
調査では Disabled ではなく Permissive を利用する
SELinux には、主に次の状態があります。
| 状態・モード | ポリシー適用 | 拒否ログ |
|---|---|---|
| Enforcing | 拒否する | 記録する |
| Permissive | 拒否しない | 記録する |
| Disabled | ポリシーを読み込まない | SELinux による拒否ログを取得できない |
問題調査では、恒久的に Disabled へ変更するのではなく、まず Permissive で挙動と AVC ログを確認します。 一時的に Permissive へ変更する場合は、次のように実行します。
setenforce 0Enforcing へ戻す場合は、次のように実行します。
setenforce 1現在の状態は、次のコマンドで確認できます。
getenforceSELinux 全体ではなく、特定のドメインだけを Permissive にすることもできます。
semanage permissive -a httpd_t全システムの強制を解除せず、対象アプリケーションだけを調査できるため、本番環境ではこちらが適する場合もあります。 AppArmor にも、SELinux の Enforcing と Permissive に近いモードがあります。
| AppArmor のモード | ポリシー適用 | 違反ログ |
|---|---|---|
| enforce | 拒否する | 記録する |
| complain | 拒否しない | 記録する |
例えば、次のコマンドでプロファイルのモードを変更できます。
aa-complain /usr/sbin/apache2
aa-enforce /usr/sbin/apache2どちらの仕組みでも、まず強制を一時的に緩めて違反を観察し、原因を修正してから再び強制する、という流れが基本です。
AVC ログを確認して原因を分類する
SELinux による拒否は、AVC メッセージとして audit ログなどへ記録されます。 最近の拒否を確認する場合は、例えば次のように実行します。
ausearch -m AVC,USER_AVC -ts recent特定のプロセス名で絞り込む場合は、次のように実行します。
ausearch -m AVC -c httpd -ts recentただし、AVC ログが存在しない場合は、SELinux が原因ではない可能性があります。 先に DAC で拒否されていれば、SELinux の判定まで進まないためです。 また、対象プロセスが想定した confined ドメインではなく、unconfined_t などで動作している場合、期待していた SELinux ポリシーそのものが適用されていない可能性もあります。 そのため、障害解析では次の順序で確認する必要があります。
- 所有者、グループ、パーミッション、ACL に問題がないか
- 対象プロセスがどの SELinux ドメインで動作しているか
- SELinux の AVC 拒否が記録されているか
- ファイルやディレクトリのラベルが正しいか
- 標準の配置先や標準ラベルを利用できないか
- SELinux Boolean で想定された機能を有効化できないか
- 本当に新しいポリシーが必要か
対象プロセスのドメインは、次のように確認できます。
ps -eZ特定のプロセスだけを確認する場合は、例えば次のように絞り込みます。
ps -eZ | grep httpdaudit2allow は自動修復コマンドではない
audit2allow は、SELinux の拒否ログを解析し、その拒否を許可する Type Enforcement ルールを生成するツールです。 例えば、ルール候補を確認する場合は次のように実行します。
ausearch -m AVC -ts recent | audit2allowローカルポリシーモジュールを生成することもできます。
ausearch -m AVC -ts recent |
audit2allow -M local-policyしかし、audit2allow は、アプリケーションに本当に必要な権限を設計してくれるわけではありません。 ログに記録された拒否を、許可ルールへ変換しているだけです。 例えば、ラベルが誤っているために拒否されたアクセスへ audit2allow を適用すると、本来は restorecon で直すべき問題に対して、誤ったラベルのままアクセスを許可するポリシーを追加することになります。
- 本来の問題
- ファイルのラベルが誤っている
- 誤った対応
- 誤ったラベルへのアクセスを許可する
- 結果
- 動作はするが、ポリシーの意味が崩れる
audit2allow を利用する場合は、少なくとも次の確認が必要です。
- 生成されたルールを読む
- なぜそのアクセスが必要なのか確認する
- ラベルや Boolean で解決できないか先に検討する
- 必要最小限の操作だけを許可する
- 正常系だけでなく異常系も含めて検証する
拒否ログが消えることと、適切なセキュリティポリシーが設計されたことは同じではありません。
コンテナでも SELinux と AppArmor は利用される
コンテナは仮想マシンではなく、Linux カーネル上で動作するプロセスです。 そのため、コンテナの分離は一つの仕組みだけで実現されているわけではありません。
| 機構 | 主な制御対象 |
|---|---|
| namespaces | プロセス、ネットワーク、マウントなどの可視範囲 |
| cgroups | CPU、メモリーなどのリソース |
| Linux Capabilities | root 権限に含まれる特権の細分化 |
| seccomp | 実行可能なシステムコール |
| SELinux/AppArmor | プロセスがアクセスできる対象や操作 |
例えば、コンテナ内のプロセスが root として動作していても、次の制限を重ねられます。
- Capabilities
- 特権操作を制限
- seccomp
- システムコールを制限
- SELinux / AppArmor
- ファイル、ソケット、デバイスなどへのアクセスを制限
コンテナを利用しているから SELinux や AppArmor が不要になるわけではありません。 複数のワークロードが同じカーネルを共有するため、プロセスが侵害された後の影響範囲を制限する仕組みとして、むしろ重要になります。 ただし、ここでも単に SELinux や AppArmor が有効であるだけでは不十分です。 どのプロファイルやラベルが実際にコンテナへ適用されているかを確認しなければ、期待した分離が成立しているとは限りません。
まとめ
SELinux と AppArmor は、どちらも Linux の MAC を実現し、DAC だけでは制限できないプロセスの振る舞いを制御します。 両者の違いを単純化すると、次のように整理できます。
- SELinux は、プロセスや対象へ付与されたラベルの関係を中心に判断する
- AppArmor は、プログラムへ適用されるプロファイルとアクセス先のパスを中心に判断する
SELinux の mv 問題は、この設計思想を理解する上で象徴的です。
- パスは変わった
- ラベルは変わっていない
この状態では、パスとパーミッションだけを確認しても拒否理由が分かりません。 Permissive にすると動作するため、SELinux そのものが問題であるように見えます。 しかし、本来行うべきことはアクセス制御を取り除くことではありません。
- DAC で拒否されていないか
- 対象プロセスはどのドメインで動作しているか
- MAC による拒否が記録されているか
- ラベルやプロファイルが正しいか
- 既存のポリシーで表現できるか
- 新しい許可が本当に必要か
という順序で確認することです。 また、SELinux が Enforcing であることと、システム上の全プロセスが厳密に閉じ込められていることは同じではありません。
- SELinux が有効
- 全プロセスが厳密に制約されている
- SELinux が有効
- +
- 対象プロセスが confined ドメインで動作
- +
- 適切なポリシーが適用されている
- Type Enforcement による閉じ込めが機能する
SELinux や AppArmor が扱いにくく見えるのは、Linux へ余計な制約を追加しているからではありません。 従来は暗黙だった「どのプロセスが、どの対象へ、何をしてよいか」という設計を、明示的なポリシーとして管理対象にしたためです。 その状態と適用範囲を運用設計へ取り込めなければ、MAC は障害の原因に見えます。 一方、ラベル、ドメイン、プロファイル、ログ、調査手順まで含めて管理できれば、アプリケーションが侵害された後の被害範囲を制限する、Linux の重要な防御層として機能します。
参考資料
- Red Hat Enterprise Linux 9 – Using SELinux
- Ubuntu Server documentation – AppArmor
- Ubuntu security documentation – AppArmor

