【テクニカル・上級編】 AppArmor/SELinuxによるコンテナの強制アクセス制御(MAC) – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

コンテナ脱獄(Escape)の現実と、MAC(強制アクセス制御)という最後の一線

KubernetesやDocker全盛の現在、インフラエンジニアの間で「コンテナはプロセス分離されているから安全だ」という神話はすでに過去のものとなった。日々、Linuxカーネルの脆弱性(例えば、Dirty COW (CVE-2016-5195) や runc の脆弱性である CVE-2019-5736 など)を突いたコンテナ脱獄(Escape)のエクスプロイトコードがダークウェブやGitHub上で公開され、攻撃者はいとも簡単にホストのルート権限を奪取している。

名前空間(Namespaces)やcgroupsによるリソース制限は、あくまで「見えている世界」を区切っているに過ぎない。ひとたびカーネル空間への不正なメモリアクセスや、特権コンテナの誤設定によるケーパビリティ(Capabilities)の濫用が発生すれば、ホストOSの要塞は崩壊する。

そこで重要になるのが、Discretionary Access Control(任意アクセス制御:DAC)の限界を補う、強制アクセス制御(MAC: Mandatory Access Control)である。本稿では、AppArmorおよびSELinuxを活用し、万が一のコンテナ脱獄時にも被害を最小限に抑え込むためのディープな要塞化アーキテクチャを解説する。

DACの限界と、AppArmor/SELinuxの本質

Linuxの標準的なアクセス制御であるDAC(ファイルパーミッションやUID/GID)は、プロセスを実行しているユーザーが主体となるため、root権限を持つプロセスは何でもできてしまうという致命的な欠陥を抱えている。コンテナ内のプロセスが root で動作している場合、実質的にホスト側へ影響を及ぼすファイルの改変やデバイスの操作が(名前空間の壁の向こう側で)可能になってしまうのだ。

これに対し、MACはシステム管理者(セキュリティポリシー)が定義した厳格なルールに基づき、root であろうともプロセスの振る舞いを強制的に制限する。

  • AppArmor: パスベースのアクセス制御。学習が比較的容易で、UbuntuやDebianエコシステムで標準的に使われる。
  • SELinux: ラベル(コンテキスト)ベースのアクセス制御。Red Hat系のディストリビューションでデフォルト有効となっており、よりきめ細やかで強固な制御が可能。

コンテナランタイム(runc や containerd)は、これらのMACフレームワークと深く統合されており、コンテナ起動時に特定のセキュリティプロファイル(またはドメイン)を強制適用することができる。

1. AppArmorによるコンテナプロファイルの設計と適用

デフォルトのDockerプロファイルは多くのシステムコールやファイルパスへのアクセスを制限しているが、高度なセキュリティを求める環境では、ワークロード固有のカスタムプロファイルを作成・適用すべきである。

以下の例は、特定のコンテナ内から /etc/ 以下の機密ファイルへのアクセスと、ネットワークソケットの新規作成を厳格に禁止するAppArmorプロファイルのサンプルである。

# ==============================================================================
# ターゲットコンテナ用 カスタムAppArmorプロファイル
# ファイルパス: /etc/apparmor.d/docker-custom-secure-app
# ==============================================================================

#include <tunables/global>

profile docker-custom-secure-app flags=(attach_disconnected,mediate_deleted) {
  # デフォルトの抽象リソース(基本的なライブラリやバイナリの読み込み)をインクルード
  #include <abstractions/base>

  # ネットワーク機能の制限(TCP/UDPの新規ソケット作成を拒否する場合)
  # 完全に遮断する場合は deny network を指定
  network inet tcp,
  network inet udp,

  # 許可するファイルシステムへのアクセス
  # ルートディレクトリ配下は基本的に読み取り専用(r)とし、特定のログ領域のみ書き込み(w)を許可
  / rix,
  /bin/{bash,sh} ix,
  /usr/bin/** r,
  /lib/** r,
  /lib64/** r,

  # 【重要】機密性の高いホスト由来のマウントや設定ファイルへのアクセスを完全にブロック
  deny /etc/shadow rwx,
  deny /etc/passwd rwx,
  deny /etc/hosts rwx,

  # アプリケーションが動作するために必要な一時ディレクトリのみ書き込みを許可
  /tmp/** rw,
  /var/tmp/** rw,

  # カーネルのprocやsysfsへの危険なアクセスを遮断
  deny /proc/sys/** rw,
  deny /sys/** rw,
}

プロファイルのロードとコンテナへの適用

作成したプロファイルは、ホストOS側でAppArmorのカーネルモジュールにロードさせる必要がある。

# プロファイルの構文チェックとカーネルへのロード
sudo apparmor_parser -r -W /etc/apparmor.d/docker-custom-secure-app

# Dockerコンテナ起動時にプロファイルを指定する
sudo docker run --rm -it \
  --security-opt apparmor=docker-custom-secure-app \
  ubuntu:22.04 /bin/bash

もし、このコンテナ内で攻撃者がシェルを奪取し、/etc/shadow を読み出そうとしても、AppArmorがカーネルレベルでシステムコールをインターセプトし、Permission denied を返してアクセスを完全にシャットアウトする。

2. SELinux (MCS/MLS) によるマルチテナント環境の分離

Red Hat Enterprise Linux (RHEL) や Rocky Linux などの環境では、SELinuxが強力な防衛線となる。特にコンテナセキュリティにおいて強力なのが、MCS(Multi-Category Security)である。

DockerやPodmanは、コンテナが起動するたびに動的に一意のSELinuxカテゴリ(例: s0:c100,c200)を割り当て、コンテナプロセスとボリューム(マウントされたホストディレクトリ)にラベル付けを行う。これにより、同じホスト上で動作する異なるコンテナ同士が、たとえ両方とも root 権限を持っていても、お互いのメモリ空間やファイルシステムにアクセスすることを防ぐ。

Kubernetes / CRI-O 環境でのSELinuxカテゴリの強制

Kubernetes(CRI-Oやcontainerd)を使用する場合、PodのセキュリティコンテキストでSELinuxOptionsを明示的に指定するか、クラスター全体で厳格なポリシーを強制する。

apiVersion: v1
kind: Pod
metadata:
  name: secure-isolated-pod
spec:
  securityContext:
    # Pod全体にセキュアなSELinuxコンテキストを適用
    seLinuxOptions:
      level: "s0:c123,c456"
      user: "system_u"
      role: "object_r"
      type: "container_t"
  containers:
  - name: app-container
    image: my-secure-app:latest
    securityContext:
      # 特権昇格の明示的な禁止
      allowPrivilegeEscalation: false
      # ルートファイルシステムを読み取り専用に
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL

この設定により、コンテナプロセスが万が一侵害され、ローカルのエクスプロイトによってカーネルの脆弱性を突いたとしても、SELinuxのラベル制約により、割り当てられたカテゴリ以外のリソース(他のテナントのデータやホストの重要システムファイル)にアクセスすることは数学的・構造的に不可能となる。

インシデントハンドリングと監査の現場から:MAC違反の検知

AppArmorやSELinuxを導入した際、最も現場で苦労するのが「正当なアプリケーションの挙動までブロックしてしまう(誤検知)」という問題だ。これを防ぐためには、監査ログを常時監視し、ポリシーをチューニングする泥臭いプロセスが不可欠である。

AppArmorは違反時に auditd またはカーネルログ(dmesg)へ以下のようなログを出力する。

[ 4321.123456] audit: type=1400 audit(1698765432.123:45): apparmor="DENIED" operation="open" profile="docker-custom-secure-app" name="/etc/secret.key" pid=12345 comm="nginx" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

このログを解析することで、「どのコンテナプロファイルが、どのリソースへのアクセスを拒否されたか」をリアルタイムに把握できる。セキュリティエンジニアは、単に「動かないから機能を緩める」のではなく、「なぜそのプロセスがそのファイルを開こうとしたのか」をコードレベルで精査し、サプライチェーン攻撃や意図しないコードの混入(バックドアなど)の兆候ではないかを常に疑うべきである。

まとめ:多層防御の要としてのMAC

コンテナの要塞化において、イメージの脆弱性スキャンや不要なパッケージの削除(ディストリビューションレス化)は当然の前提条件に過ぎない。最後の砦となるのは、OSカーネルレベルでプロセスの行動を縛り上げる AppArmor や SELinux による強制アクセス制御(MAC)である。

「動けばいい」という妥協を捨て、最小特権の原則をカーネル空間にまで拡張すること。それこそが、巧妙化するコンテナ脱獄の手口からインフラストラクチャを守り抜く、最高峰のセキュリティアーキテクチャである。

コメント

タイトルとURLをコピーしました