【テクニカル・上級編】 KubernetesにおけるPod Security Admissionの適用 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

コンテナ技術は、OSカーネルの「名前空間(Namespaces)」と「コントロールグループ(cgroups)」による論理的な分離を前提に成り立っています。しかし、この分離はハイパーバイザー型の仮想化(VM)ほど強固ではありません。共有された同一のホストカーネル上で動作する以上、コンテナという境界線は、設定一つで容易に崩壊するハリボテの防壁になり得ます。

かつてKubernetesのセキュリティを担っていた Pod Security Policies (PSP) は廃止され、現在は Pod Security Admission (PSA) がその標準的な座を引き継ぎました。しかし、PSAを単なる「準拠すべきルール」や「監査用チェックリスト」として捉えているうちは、インフラの真の要塞化(ハーデニング)は不可能です。

本稿では、最高峰の防衛技術と攻撃者の思考プロセスを交え、PSAが防ぐべき「コンテナ脱出(Container Escape)」の低レイヤにおける動作原理と、クラスター全体を保護するための実用的な防御アーキテクチャを徹底的に解説します。

—

1. 攻撃者が狙う「コンテナ脱出」の低レイヤメカニズム

PSAの必要性を理解するには、攻撃者がどのようにしてコンテナの境界線を突破し、ホストOS(ひいてはクラスタ全体)の支配権を奪取するのか、そのメカニズムを理解する必要があります。代表的な攻撃ベクトルは以下の3つに集約されます。

1-1. privileged: true がもたらす「全能の権限」と cgroups 悪用

Pod定義で securityContext.privileged: true を設定することは、コンテナ内の root ユーザーに対して、ホスト上の root と実質的に同等の権限を与えることを意味します。

これによって、コンテナ内のプロセスにはすべての Linux Capabilities(CAP_SYS_ADMIN や CAP_SYS_RAWIO など)が付与され、ホストの物理デバイス(/dev 配下)への直接アクセスが可能になります。

例えば、cgroups v1 の脆弱性を突いたコンテナ脱出手法(CVE-2022-0492 など)では、CAP_SYS_ADMIN を持つ攻撃者が cgroups の release_agent を書き換えることで、ホストカーネル空間で任意のペイロードを実行させ、一瞬にしてホストのシェルを奪取します。

1-2. ホスト名前空間(hostPID, hostNetwork, hostIPC)の共有

コンテナの隔離を司る「名前空間」を意図的に無効化する設定は、攻撃者にとって格好の侵入経路です。

  • hostPID: true: コンテナ内のプロセスからホスト上のすべてのプロセス(PID 1 を含む)が可視化され、ptrace 等を用いてホスト上の他プロセスにコードをインジェクションすることが可能になります。
  • hostNetwork: true: コンテナがホストのネットワークスタックに直接乗り入れます。これにより、コンテナはループバックインターフェース(127.0.0.1)で待ち受けているホスト上の未認証サービス(kubeletの読み取り専用ポートやローカルのデータベースなど)に直接パケットを送り込めるようになります。

1-3. writeable なホストパスのマウント(hostPath)

設定ミスや利便性のために、ホストのファイルシステム(/ や /var/run/docker.sock、/run/containerd/containerd.sock など)をコンテナ内にマウントすることがあります。

コンテナランタイムのソケットファイルをマウントされた場合、コンテナ内の攻撃者は、コンテナランタイムのAPI(Unixドメインソケット)を叩くだけで、新しく「ホストのルートディレクトリをマウントした特権コンテナ」を起動し、コンテナ外へ脱出することができます。これはもはや脆弱性ではなく、仕様に則った正当なエクスプロイトです。

—

2. Pod Security Admission (PSA) のアーキテクチャと3つの防御層

Pod Security Admission は、Kubernetesの Admission Controller(アドミッションコントローラー)のフェーズにおいて、Podの作成・更新リクエストをインターセプトし、定義されたセキュリティ基準に適合しているかを検証する内蔵機能です。

PSAは、以下の3つの ポリシーレベル(Standards)を、3つの モード(Modes)でNamespaceごとに適用します。

[ Pod Creation Request ]
          │
          ▼
┌─────────────────────────────────────────┐
│       Kubernetes API Server             │
│  ┌───────────────────────────────────┐  │
│  │   Pod Security Admission (PSA)    │  │
│  │                                   │  │
│  │  1. ENFORCE: 基準未満なら即座に拒否 │  │
│  │  2. WARN:    警告をクライアントに返送│  │
│  │  3. AUDIT:   監査ログに違反を記録    │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
          │
          ▼ (If allowed)
[ Pod Scheduled on Node ]

2-1. 3つのポリシーレベル(Standards)

| ポリシーレベル | 防御の対象と深度 | 主な制限事項 |
| :— | :— | :— |
| Privileged | 制限なし。システムやインフラレベルのPod(CNIプラグイン、ストレージドライバーなど)にのみ許容される。 | なし(実質的なワイルドカード)。 |
| Baseline | デフォルトの最小限の制限。既知の特権昇格や一般的なコンテナ脱出経路を塞ぐ。 | hostNetwork, hostPID, hostIPC の禁止。hostPath マウントの禁止。危険なLinux Capabilities(CAP_SYS_ADMIN等)の追加禁止。 |
| Restricted | 徹底的な要塞化。 hardening(硬化)のベストプラクティスを強制。 | Baseline の全制限に加え、runAsNonRoot: true(UID 0での実行禁止)、allowPrivilegeEscalation: false の強制、ルートファイルシステムの読み取り専用化(推奨)、seccomp プロファイルの強制。 |

2-2. 3つの動作モード(Modes)

  • enforce: ポリシーに違反するPodの作成・更新を即座に拒否(Reject)します。
  • warn: Podの作成自体は許可しますが、適用しようとしているマニフェストがポリシーに違反している場合、ユーザー(kubectl 実行者やCI/CDパイプライン)に対して警告(Warning)を返します。
  • audit: ポリシー違反を検知した際、Kubernetesの監査ログ(Audit Log)にイベントを記録します(Podの作成自体は許可されます)。

—

3. 実践:PSAによるインフラ要塞化の実装パターン

ここからは、実際にプロダクション環境でPSAを導入し、クラスタをセキュアに保つための具体的なマニフェスト設計について解説します。

3-1. クラスタ全体のデフォルトポリシー設計(AdmissionConfiguration)

クラスタ全体に対してグローバルにデフォルトのPSA挙動を設定するために、APIサーバー起動時に --admission-control-config-file オプションで読み込ませる設定ファイルを定義します。

新規に作成されるすべてのNamespaceに対して、デフォルトで Restricted を warn / audit レベルで適用し、予期せぬシャドーIT(シャドーPod)の発生を防ぎます。

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    defaults:
      # クラスタ全体のデフォルトポリシー
      enforce: "baseline"
      enforce-version: "latest"
      warn: "restricted"
      warn-version: "latest"
      audit: "restricted"
      audit-version: "latest"
    exemptions:
      # システムコンポーネントが動作する特定のNamespaceをPSAの監査・制限対象から除外する
      usernames: []
      runtimeClasses: []
      namespaces:
        - kube-system
        - calico-system
        - local-path-storage

3-2. Namespaceレベルでの厳格な適用(マニフェスト例)

本番環境のアプリケーションを実行するNamespace(例: production-apps)には、最も厳しい Restricted ポリシーを enforce モードで適用します。これにより、インフラ要塞化(ハーデニング)の最高基準を強制します。

apiVersion: v1
kind: Namespace
metadata:
  name: production-apps
  labels:
    # このNamespace内のすべてのPodに Restricted ポリシーを強制適用する
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    
    # 開発者に違反を認識させるため、Restricted違反時は警告を返す
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    
    # SIEMや監査ログ分析基盤(Elasticsearch, Splunk等)に流すため、違反を記録する
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

3-3. Restrictedポリシーをクリアする「堅牢なPodマニフェスト」の実装

Restricted ポリシーが適用されたNamespaceでは、一般的な「ただ動けばいい」コンテナ定義はことごとく拒否されます。以下は、低レイヤの要塞化要件(システムコール制限、非特権実行、書き込み権限排除)をすべてクリアした、極めて堅牢なPod定義のテンプレートです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hardened-web-app
  namespace: production-apps
  labels:
    app: hardened-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hardened-web
  template:
    metadata:
      labels:
        app: hardened-web
    spec:
      # ホストの名前空間を徹底的に隔離する(PSA Restrictedの基本要件)
      hostNetwork: false
      hostPID: false
      hostIPC: false
      
      securityContext:
        # コンテナ内のプロセスがUID 0(root)で動作することをカーネルレベルで禁止
        runAsNonRoot: true
        # 実行ユーザーを一般ユーザー(例: UID 10001)に明示的に固定
        runAsUser: 10001
        runAsGroup: 10001
        # コンテナ内のすべてのファイルシステムに対するデフォルトのGID
        fsGroup: 10001
        # Seccompプロファイルを適用し、不要なシステムコールをカーネルレベルで遮断する。
        # RuntimeDefaultは、コンテナランタイム(containerd等)が持つデフォルトのフィルタを適用。
        seccompProfile:
          type: RuntimeDefault

      containers:
      - name: web-server
        image: nginx:alpine-slim # 軽量かつ既知の脆弱性が少ないスリムイメージを採用
        
        securityContext:
          # コンテナ起動後の特権昇格(setuidバイナリの実行など)を完全にブロックする
          allowPrivilegeEscalation: false
          
          # Linux ケーパビリティ(特権の一部を細分化したもの)をすべて剥奪(Drop)する。
          # ネットワークのBindやRawソケットの使用すらデフォルトでは禁止。
          capabilities:
            drop:
            - ALL
            
          # ルートファイルシステムを読み取り専用(Read-Only)に設定。
          # これにより、攻撃者が侵入後にWebシェル(.phpや.sh)を配置したり、
          # 設定ファイルを書き換えて永続化を図る行為を根本的に防止する。
          readOnlyRootFilesystem: true

        # ルートファイルシステムを Read-Only にしたため、
        # アプリケーションが動作に必要とする一時的な書き込み領域(キャッシュやログ等)は、
        # メモリ上に確保される emptyDir ボリュームをマウントして対応する。
        volumeMounts:
        - mountPath: /tmp
          name: tmp-volume
        - mountPath: /var/cache/nginx
          name: nginx-cache

        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
          requests:
            cpu: "250m"
            memory: "256Mi"

      volumes:
      - name: tmp-volume
        emptyDir:
          medium: Memory # メモリ(tmpfs)上に配置し、ディスクへの書き込みを発生させない
      - name: nginx-cache
        emptyDir:
          medium: Memory

—

4. 監査と継続的防御:PSAの先にある「次世代ガードレイル」の設計

Pod Security Admission は、Kubernetesクラスタにおける静的なマニフェスト検証の「第一の砦」に過ぎません。これだけでインフラを完璧に防御できると過信してはなりません。

4-1. 静的検証(PSA)の限界

PSAは、Podが 作成される瞬間 の設定値をチェックする仕組みです。そのため、以下のような「動的な脅威」や「カーネルレベルのゼロデイ脆弱性」には対応できません。

1. コンテナイメージの汚染: 許可された設定(非特権、Read-Only等)で起動したコンテナであっても、その内部のライブラリ(Log4jやOpenSSLなど)に脆弱性があり、リモートコード実行(RCE)を許してしまった場合。
2. Linuxカーネルの未知の脆弱性(ゼロデイ): コンテナが非特権であっても、システムコールを通じて直接ホストの Linux カーネルの脆弱性(例: io_uring や ebpf サブシステムの不具合)を突き、特権に昇格して脱出するケース。

4-2. eBPFを用いたリアルタイム・システムコール監視(動的防御)

PSAによるハーデニングを完了した後に実装すべきは、eBPF (Extended Berkeley Packet Filter) を用いたカーネルレベルのリアルタイム防御です。

Cilium Tetragon や Falco といったツールを導入し、コンテナ内から発行されるシステムコールを直接カーネル空間でフック・分析します。

[ Application in Container ]
            │
            ▼ (System Call: execve, socket, connect)
┌──────────────────────────────────────────────┐
│             Linux Kernel                     │
│  ┌────────────────────────────────────────┐  │
│  │   eBPF Probe (Tetragon / Falco)        │  │
│  │  ┌──────────────────────────────────┐  │  │
│  │  │  Real-time Behavioral Analysis   │  │  │
│  │  │  - Detects unexpected binaries   │  │  │
│  │  │  - Blocks anomalous syscalls     │  │  │
│  │  └──────────────────────────────────┘  │  │
│  └────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘

例えば、堅牢化されたはずのWebコンテナ(通常は nginx プロセスしか走らないはず)から、突如として execve システムコールによって /bin/sh や curl が呼び出された瞬間、eBPFプローブがそれを検知し、瞬時にそのコンテナプロセスをカーネルレベルで強制終了(SIGKILL)させます。

4-3. 生成AI時代の開発パイプラインとセキュリティガードレイル

昨今、GitHub Copilot などの生成AIが生成したマニフェストコードをそのままプロダクションにデプロイするケースが増加しています。AIは時に、動作の容易さを優先して privileged: true や不要な Linux Capabilities を付加した「動くが脆弱なマニフェスト」を出力します。

この文脈において、PSA(特に enforce モード)は、AIが生成したセキュリティ的に欠陥のあるコードが本番環境へ侵入するのを自動的にブロックする、CI/CDパイプラインの「最後の物理的ガードレイル」として機能します。

—

5. まとめ

Kubernetesのセキュリティとは、突き詰めれば「カーネルという単一の共有資源を、悪意あるプロセスからいかに隔離し続けるか」という低レイヤの戦いです。

Pod Security Admission(PSA)の導入は、この戦いにおける必須のスタートラインです。
Baseline で大半の既知の脱出経路を塞ぎ、Restricted でプロセスの特権を極限まで剥奪する。そして、eBPFによる動的監視を組み合わせることで、初めてモダンなクラウドネイティブインフラの「要塞化(ハーデニング)」が完成します。

設定ファイルの「一行の真偽値」が、数百万台のコンテナを、そして企業の信頼を揺るがす境界線であることを、我々セキュリティアーキテクトは片時も忘れてはなりません。

コメント

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