ランタイム強制アクセス制御の設計 — SELinux・AppArmorをどこで有効にし、何を制約するか

ランタイムレベルの強制アクセス制御(MAC)を導入する際に、多くのエンジニアが最初に直面するのは「コマンドの打ち方」ではなく「どの環境で・どのサービスに・いつ有効にするか」という設計上の判断です。SELinuxとAppArmorは思想もポリシー管理の粒度も異なり、ディストリビューション・コンテナ基盤・運用体制によって最適な選択肢は変わります。誤った組み合わせでEnforcingモードに突入すると、深夜の本番停止を招きます。本記事では設計思想の差を比較したうえで、適用タイミング・ポリシー選定・コンテナへの展開・特権昇格の封じ込めという各判断軸を解説します。

SELinuxとAppArmorの設計思想と適用範囲の比較

ラベルベースとプロファイルベース — 根本的なモデルの違い

SELinuxはシステム上のすべてのオブジェクト(ファイル・プロセス・ソケット)にセキュリティラベルを付与し、ラベルの組み合わせでアクセスを決定します。ポリシーはシステム全体に一枚で適用されるため設定の一貫性が高い反面、ラベルの伝播ミスが予期しない拒否を引き起こしやすくなります。AppArmorはプロセス単位でプロファイルを記述するパスベースのモデルで、可読性が高く特定アプリケーションだけを素早く保護したい場面に向いています。二つのモデルは優劣ではなく「全体を網羅して強固に守るか、アプリ単位で素早く保護するか」というトレードオフで選ぶものです。

ディストリビューション選定との連動

RHELおよびその派生(CentOS Stream・Rocky Linux・AlmaLinux)はSELinuxがデフォルトで有効化されており、サポートされるポリシーセットも充実しています。一方、Debian/UbuntuはAppArmorを標準で採用し、パッケージが同梱するプロファイルも多く存在します。新規構築でディストリビューションの選択肢があるなら、標準ツールに逆らわない選択が最もリスクを低くします。10台以下の小規模環境ではどちらへ切り替えるコストも吸収できますが、100台規模の既存フリートを移行する場合、ラベル付けの再設計だけで数百時間の工数がかかる可能性を念頭に置く必要があります。

SELinuxとAppArmorの設計比較

比較軸 SELinux AppArmor
アクセス制御モデル ラベルベース(全オブジェクトにコンテキスト付与) プロファイルベース(パス指定)
標準採用ディストリ RHEL系(Rocky・Alma・Stream) Debian系(Ubuntu等)
ポリシーの可読性 低い(専門知識が必要) 高い(パスで直感的)
適用の粒度 システム全体・非常に細かい アプリ単位・中程度
既製ポリシーの充実度 高(RHELエコシステム) 中(Ubuntu同梱が多い)
カスタムポリシーの難易度 高 中
コンテナとの連携 k8sのseLinuxOptionsで指定 docker-default・k8sで指定可

EnforcingとPermissiveをどのタイミングで切り替えるかの判断軸

Permissiveモードの正しい位置づけ

Permissiveモードは「制御しない観察フェーズ」ではなく「本番適用前のポリシー検証フェーズ」として位置づけるのが正しい使い方です。拒否ではなくログが残るため、アプリケーションが実際に必要とするアクセスパターンを洗い出せます。ただし、Permissiveのままステージングを長期間運用していると、Enforcingに切り替えた際に拒否が一気に噴き出し、原因追跡に時間を取られます。新規サービスのデプロイ後、少なくとも1〜2週間はPermissiveで動作ログを収集してからEnforcingへ移行する手順が、現場では安定した実績を持ちます。

Enforcingへの移行タイミングと移行条件

Enforcingへの移行条件として「ポリシー違反ログがゼロになった状態が数日間継続すること」を基準に置くチームが多くあります。しかしPermissiveログが静かに見えていても、バッチ処理・障害時フェイルオーバー・バックアップ取得などの非常パスが未実行のまま移行すると、初回実行時にサービスが止まります。主要な運用シナリオをすべて一度は実行してからEnforcingに切り替えることが前提です。以下はEnforcing移行前の確認リストとして使えます。

  • Permissiveモードで主要運用シナリオ(通常フロー)をすべて実行済みか
  • バッチ処理・バックアップ・フェイルオーバーパスでの動作確認済みか
  • ポリシー違反ログが連続した数日間ゼロになっているか
  • auditログが中央ログ基盤に転送されているか
  • ロールバック手順が文書化・実地確認済みか
  • 本番でPermissiveに戻す条件と時間上限が変更管理に明記されているか

本番環境での設定変更ポリシーの設計

本番で一時的にPermissiveに戻す行為はMACを無効化するに等しい操作です。この操作を許容する条件を変更管理プロセスに明記しておかないと、深夜の障害対応で担当者が判断できず、変更を躊躇して障害が長期化するか、あるいは無条件にPermissiveに戻してセキュリティログに穴を開けるかのどちらかになります。「本番でPermissiveに変更してよい条件」と「Enforcingに戻すまでの時間上限」を事前に定義しておくことが、設計段階でやるべき判断の一つです。

ポリシーを既製品で済ますか自前で書くかの選定基準

既製ポリシーが通用する条件

Nginx・sshd・MySQLなどの主要OSSには、ディストリビューションのパッケージやコミュニティが管理するポリシーが存在します。これらが機能する前提は「ソフトウェアをカスタムビルドせず、標準的な設定ディレクトリ構成を維持している」ことです。設定ファイルを標準パスから変更した途端にポリシーのラベルやプロファイルが合わなくなり、最終的にカスタム修正が必要になります。既製ポリシーで完結できるのは「OSSを素のまま動かす」場合に限られると考えておくと、選定の判断がしやすくなります。

カスタムポリシーが必要になる閾値

自社開発のアプリケーション・スクリプト・独自デーモンを動かす場合は、ほぼ確実にカスタムポリシーが必要になります。「既製ポリシーにunconfinedを追加して逃げる」選択肢は機能はしますが、特定プロセスをMACの管理外に置くことを意味し、そのプロセスが侵害された際の横移動リスクを受け入れることになります。自前ポリシーの初期構築には既製品の倍以上の時間がかかりますが、アプリケーション固有のアクセスパターンを正確に反映できるため誤検知率が下がり、運用が安定します。

ハイブリッド選定の判断フロー

実際の構築では「OSS標準コンポーネントは既製ポリシー、自社アプリは自前ポリシー」という組み合わせが最も現実的です。選定の分岐点は「そのプロセスに既存コミュニティポリシーが存在するか」「設定パスが標準構成と一致するか」「ポリシーをメンテナンスできる担当者がチームにいるか」の三点で判断します。担当者が不在の状態でカスタムポリシーを書き始めると、構築後に誰も触れない「秘伝のポリシー」になって運用負荷の根本原因になります。ポリシーの選定は、その後の保守体制込みで決める判断です。

コンテナランタイムへの適用をどこから始めるかの設計

コンテナとMACの役割関係の整理

コンテナはcgroupsとnamespaceで隔離されますが、同一カーネルを共有するプロセス間の強制制御にはなりません。SELinux/AppArmorをコンテナに適用することで、コンテナブレイクアウトが起きた際のホストへのアクセスを制限できます。Dockerはデフォルトでdocker-defaultという制限的なAppArmorプロファイルをコンテナに適用しますが、特定の機能には不十分なケースが多く、本番ではコンテナの役割ごとに個別設計が必要です。namespaceやcgroupsとMACは置き換えではなく、積み重ねとして設計するものです。

Kubernetesクラスターへの段階的展開

KubernetesではPodのsecurityContextにappArmorProfileやseLinuxOptionsを指定してポリシーを適用します。すべてのNamespaceに一度に適用するのではなく、リスクの高い公開サービスが乗るNamespaceから始め、検証済みのポリシーを順次展開するのが安全な進め方です。20ノード規模のクラスターでも、最初の適用スコープを5〜10 Pod程度に絞り、2〜4週間の観測期間を置いてから拡大する手順を踏む現場が多くあります。段階展開の判断基準を事前にチケットで管理することで、拡大タイミングの属人化を防げます。

seccompとの役割分担の明確化

seccompはプロセスが呼び出せるシステムコールを制限し、SELinux/AppArmorはファイル・ネットワーク・プロセスへのアクセスを制御します。この二層は補完関係にあり、どちらか一方で他方を不要にはできません。設計上の判断基準は「プロセスが呼べるシステムコールを絞りたいならseccomp、ファイルやソケットへのアクセスパターンを制御したいならMAC」と役割を分けることで整理しやすくなります。コンテナ環境ではこの二層にネットワークポリシーを組み合わせた三層設計が標準的な構成です。

MACを回避するsudo・privileged実行をどこで封じるか

privilegedコンテナがMACを無効化する仕組み

Dockerのprivilegedオプションを付けたコンテナはすべてのLinux capabilityを取得し、AppArmorプロファイルおよびSELinuxラベルの適用がバイパスされます。デバッグ用途での一時利用は許容範囲内でも、本番ワークロードに残ったまま運用されるケースが少なくありません。CIパイプラインのビルドコンテナに–privilegedが残ったまま本番環境に流用されており、定期的なセキュリティレビューで初めて発覚したという話を現場でよく聞きます。privilegedコンテナの存在は、MACを有効にしているという前提自体を崩します。

sudoによるポリシー迂回の設計的対処

sudoでrootに昇格したプロセスはSELinuxのコンテキストを引き継ぎますが、sudoを使って別ドメインに遷移するポリシーが設計されていなければ実質的に制御外になります。「sudo su -」でrootシェルを取った後の操作はMACの管理から外れることが多く、侵害経路として悪用されうる部分です。sudo設定とMACのロールトランジションを連動して設計し、rootになる操作を特定ドメインに閉じ込めることが根本対策です。この設計なしにMACを導入しても、sudoという抜け穴が残り続けます。

特権昇格の封じ込めアーキテクチャ

MACで特権昇格を封じるには、プロセスが遷移できるドメインを明示的にポリシーで制限する必要があります。Kubernetes環境では、PodのsecurityContextでallowPrivilegeEscalationをfalseに設定し、MACポリシーと組み合わせることで二重の防護線を設ける設計が有効です。100台以上のノードに統一したポリシーを適用するには、OPA/GatekeeperやKyvernoなどのポリシーエンジンでadmission controlとして自動強制する構成が現実的な規模感です。手作業での確認は管理対象が数十Pod規模を超えた時点で破綻します。

ポリシー変更の検証をどの環境で行うかの設計

「本番同等環境」の定義と現実的な維持コスト

ポリシーの検証には「本番と同等のソフトウェアスタック・設定ファイル・OSバージョンが揃った環境」が必要です。しかし現実には、ステージングのOS/カーネルバージョンが本番より古いままになっているケースが多く、そこで検証済みのポリシーが本番のカーネルバージョン差でふるまいを変えることがあります。SELinuxのポリシーモジュールはカーネルバージョンに依存する部分があるため、少なくともカーネルのマイナーバージョンを本番と揃えた検証環境を1セット維持することが最低条件です。この環境が存在しない状態でポリシーを変更するのは、本番を直接テストしているに等しい判断です。

自動化されたポリシー検証の組み込み

ポリシー変更をCIパイプラインで自動検証する仕組みとして、Permissiveモードのコンテナを短時間起動して拒否ログが発生しないことを確認するテストを組み込む方法が有効です。すべての運用シナリオをテストケース化する初期コストはかかりますが、一度整備すれば以降のポリシー変更ごとにリグレッションを自動で検知できます。3〜5分の自動テストで主要パスのポリシー違反を検出できる仕組みを持つチームでは、本番障害に至る前に変更を差し戻せた事例が複数報告されています。

ロールバック手順の設計とドリル

ポリシー変更のロールバック手順を事前に定義し、年に1〜2回は実際に実行する訓練を行うチームは、本番障害時の復旧時間が明らかに短くなります。手順書があっても「本番で実行したことがない操作」は必ず何かで行き詰まります。ポリシーのロールバックは影響範囲が広く、誤った差し戻しがさらに別のサービスに波及することもあるため、変更管理の訓練対象として組み込んでおくことが設計の一部です。手順書の存在確認ではなく「実行済み確認」をチェックリストに入れておきます。

落とし穴 — ポリシーの誤設定で深夜に本番が止まるパターン

ラベル伝播の見落とし(SELinux特有の罠)

SELinuxではファイルを別ディレクトリからコピー・シンボリックリンクで参照した場合、元のラベルが引き継がれるか変わるかが操作方法によって異なります。デプロイスクリプトがcpではなくrsyncを使っている場合にラベルが変化しないケースがあり、デプロイ直後にサービスが起動できなくなる落とし穴があります。この問題はPermissiveモードで見落とされやすく、Enforcingに切り替えた本番で初めて顕在化するケースが報告されています。デプロイツールごとのラベル伝播の動作を事前に検証しておくことが、この落とし穴を避ける唯一の対策です。

パッケージアップデートでポリシーが上書きされるパターン

OSのパッケージアップデートによって、ディストリビューション提供のポリシーモジュールが上書きされ、カスタム追加したルールが消えることがあります。特にSELinuxでカスタムモジュールがパッケージ付属のモジュールに依存している場合、モジュールが上書きされるとカスタムルールの前提が崩れます。定期アップデートとポリシー検証を組み合わせるCI/CDが整備されていないチームが、四半期に一度のまとめてアップデートで深夜に止まる原因になっています。アップデートと検証をセットにする手順を確立しておくことが防止策です。

ログ不足で原因特定に数時間かかるパターン

MACの拒否ログはauditdを通じて記録されますが、auditdのログローテーション設定が不十分で古いログが消えていた場合、障害発生から数時間後に調査を始めても証拠が残っていません。また、auditログを中央ログ基盤に転送していないチームでは、リモートのSIEM側に何も残らず、障害後の原因解析で行き詰まることになります。ポリシーを有効にする前に、auditログが中央ログ基盤に確実に転送される仕組みを確認してから進めることが大前提です。ログ設計なしのMAC導入は、障害時に自分で手掛かりを消すに等しい行為です。

▶ 手を動かして本番まで到達する

その設計判断を、RHEL10の本番環境で手を動かして固めませんか

ここまでの設計判断は、読めば腹に落ちます。ですが「自分の手で本番まで通しきる自信」は、それとは別の話になります。

最大6名の少人数で、構築実務者の到達点まで引き上げます。座学で終わらせず、本番相当の環境で判断を検証する2日間です。

SELinux・AppArmorの設計を実装で確かめる上級Linuxハンズオンへ →

Linuxセミナー上級編(RHEL10)|最大6名の少人数ハンズオン