【実務・中級編】 KubernetesにおけるPod Security Admissionの適用と特権コンテナの禁止 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

Kubernetesの「特権コンテナ」という時限爆弾:Pod Security Admissionで封じ込める現実解

現場でKubernetesを運用していると、開発チームから「このライブラリを動かすために特権モード(privileged: true)が必要です」というリクエストが飛んでくることがある。

正直に言おう。そのリクエストを承認した瞬間、あなたのクラスタのセキュリティは崩壊したも同然だ。

なぜか? 特権コンテナは、ホストOSのカーネル機能に直接アクセスできる。つまり、コンテナの脱獄(Breakout)は容易であり、ホスト上の全Podへのアクセス、さらにはノードそのものの乗っ取りまで許してしまうからだ。今日は、この「緩い設定」を物理的に許さないための、Pod Security Admission(PSA)による強制適用について、実務的な防衛術を解説する。

—

なぜ「特権」がそこまで危険なのか(攻撃の視点)

攻撃者は、アプリケーションの脆弱性(例えば、未パッチのWebアプリのRCE)を突いてコンテナ内に侵入する。もしそのコンテナが特権モードで動いていれば、攻撃者は以下のことができてしまう。

1. ホストファイルシステムへのアクセス: /host のようなマウントポイントを探索し、SSHの秘密鍵や、他のPodがマウントしている機密情報(Secret)を盗み出す。
2. カーネルモジュールのロード: 悪意のあるコードをホストのカーネルに注入し、セキュリティツール(falcoなど)を無効化する。
3. 特権昇格: ホストのプロセスID空間(PID Namespace)を共有することで、ホスト上の他のプロセスを操作する。

これは単なる「設定の不備」ではなく、「攻撃者にクラスタの鍵を渡している」のと同じことなのだ。

—

Pod Security Admission (PSA) で強制的に縛り上げる

Kubernetes 1.25以降、Pod Security Policies(PSP)が削除され、標準機能である Pod Security Admission が正義となった。これを使えば、ネームスペース単位で「この中では特権コンテナは禁止!」と強制できる。

まずは、特定のネームスペース(ここでは production)に対して、「特権禁止」を強制するラベルを付与する設定だ。

# productionネームスペースに対して、Pod Security Admissionのポリシーを強制適用する
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest \
  --overwrite

これにより、privileged: true を含むPodを作成しようとすると、APIサーバーが即座に拒否(Reject)するようになる。

—

実践:セキュアなPod設計のテンプレート

では、開発者がどう記述すべきか。以下は、特権を一切使わず、かつ最小権限(Least Privilege)で動かすためのベストプラクティスな deployment.yaml だ。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      securityContext:
        # コンテナ全体で非rootユーザー実行を強制
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 3000
      containers:
      - name: app
        image: my-secure-app:latest
        securityContext:
          # ここが最重要:特権昇格を一切許可しない
          allowPrivilegeEscalation: false
          # Linuxの機能を制限(不要なシステムコールを遮断)
          capabilities:
            drop:
              - ALL
          # 書き込み可能なルートファイルシステムを禁止(Read-Onlyにする)
          readOnlyRootFilesystem: true
        volumeMounts:
          # 書き込みが必要な場所だけ一時ディレクトリをマウント
          - mountPath: /tmp
            name: tmp-volume
      volumes:
        - name: tmp-volume
          emptyDir: {}

解説:この設定の狙い

  • allowPrivilegeEscalation: false: 子プロセスが親プロセスよりも高い権限を得ることを防ぐ。
  • capabilities: drop: ["ALL"]: Linuxカーネルの機能をすべて剥奪する。ほとんどのアプリはこれで動く。
  • readOnlyRootFilesystem: true: 万が一アプリが乗っ取られても、攻撃者がバックドアをファイルシステムに書き込むことを防ぐ。

—

運用現場での「泥臭い」インシデントハンドリング

「ポリシーを導入したらアプリが動かなくなった!」という叫びは、現場では日常茶飯事だ。その際、安易に privileged を戻してはいけない。

1. Auditログの確認: kubectl logs ではなく、APIサーバーのAuditログを見て、どの権限が拒否されたのか(例:CAP_NET_BIND_SERVICE が足りない等)を特定する。
2. ポリシーの段階的適用: いきなり enforce するのではなく、まずは warn モードで運用し、どのPodが違反しているかを洗い出すことから始める。

# 違反Podを検知するだけのモード(いきなり落とさない)
kubectl label namespace production \
  pod-security.kubernetes.io/warn=restricted

最後に:セキュリティは「諦め」から始まる

我々エンジニアが追求すべきなのは、「何でも動く最強のプラットフォーム」ではなく、「攻撃者が侵入した瞬間に、その場から一歩も動けなくなるプラットフォーム」だ。

特権コンテナの排除は、そのための最も強力な第一歩となる。便利さと引き換えにセキュリティを捨てるような設計は、今日で卒業しよう。もし開発チームから文句を言われたら、この記事を見せて「私の署名で許可は出せない。セキュアな実装を考えよう」と伝えてやってほしい。

それが、プロのエンジニアとしての仕事だ。

コメント

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