【実務・中級編】 KubernetesにおけるPod Security Admission (PSA) の実装 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

なぜ「デフォルトのKubernetes」はザルなのか?:Pod Security Admissionで封じ込める特権昇格の脅威

現場で数多のコンテナ環境を見てきたが、「とりあえず動けばいい」という設定でクラスタを運用しているエンジニアほど、インシデント発生時の被害が甚大になる。

KubernetesのPodは、何も設定しなければホストのカーネルに近い権限をいとも簡単に奪取できる。攻撃者は一度コンテナ内に侵入すると、hostPathマウントや特権モード(privileged: true)を悪用し、コンテナの壁を突き破ってホストOSのルート権限を掌握する。これが「コンテナ・エスケープ」の定石だ。

今回は、この悪夢を未然に防ぐための最強の盾、Pod Security Admission (PSA) について、実戦的な実装論を説く。

—

1. そもそもPSAで何を封じるのか?

PSAは、Podが起動する前に「こいつは危険な設定をしていないか?」をチェックする門番だ。以下の3つのプロファイルでPodを格付けする。

  • Privileged: 全制限なし。悪意あるPodや、カーネルモジュールを触るような特殊な用途以外では絶対禁止。
  • Baseline: 最小限の制限。既存アプリを動かすための妥協点。
  • Restricted: 極めて堅牢。読み取り専用ルートファイルシステム、非ルートユーザー実行などを強制する。本番環境のWebアプリはこれを目指すべきだ。

—

2. 現場で使える「セキュアな名前空間」の構築

まず、特定の名前空間(例: web-app-prod)に対して、強制的に restricted プロファイルを適用する。これだけで、不用意な特権昇格を伴うPodは起動すらできなくなる。

以下は、kubectl でラベルを付与してPSAを有効化するコマンドだ。

# 名前空間にラベルを付与し、警告(warn)と強制(enforce)の両方をrestrictedに設定
kubectl label namespace web-app-prod \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest \
  pod-security.kubernetes.io/warn=restricted

これだけで、privileged: true を含む Deployment をデプロイしようとすると、APIサーバーが即座に拒絶する。これが「デフォルト拒否(Default Deny)」の強みだ。

—

3. 【実務実装】PSAをパスする「正しい」Pod定義

restricted プロファイルでは、securityContext を厳密に制御する必要がある。以下は、セキュリティチーフが推奨する「合格点」の Deployment サンプルだ。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-web-app
  namespace: web-app-prod
spec:
  template:
    spec:
      securityContext:
        # 非ルートユーザー実行を強制
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: app
        image: my-secure-app:latest
        securityContext:
          # 特権昇格を禁止
          allowPrivilegeEscalation: false
          # ルートファイルシステムを読み取り専用に
          readOnlyRootFilesystem: true
          # 不要なLinuxケーパビリティを全て剥奪
          capabilities:
            drop:
              - ALL
        volumeMounts:
        - name: tmp-dir
          mountPath: /tmp
      volumes:
      - name: tmp-dir
        emptyDir: {}

なぜこの設定が重要なのか?

  • allowPrivilegeEscalation: false: これがないと、setuid ビットを持つバイナリ経由で攻撃者にルート権限を奪われる可能性がある。
  • readOnlyRootFilesystem: true: 攻撃者がシェルを送り込んでも、バイナリを書き換えたりバックドアを設置したりできなくなる。これが物理的な耐性だ。

—

4. 盲点:コードレベルのセキュリティとの二段構え

PSAでインフラを固めても、アプリケーション側の脆弱性(RCEなど)があれば意味がない。特に公開鍵暗号を用いた通信や、認証基盤の秘匿情報管理は別次元の話だ。

例えば、Pythonで外部APIと通信する場合、requests ライブラリの verify=True を外すような愚行は論外だ。以下のコードのように、暗号学的整合性を担保する。

import requests
import ssl

def secure_request(url):
    # TLSのバージョンを固定し、検証を強制する
    context = ssl.create_default_context()
    context.minimum_version = ssl.TLSVersion.TLSv1_2
    
    try:
        # verify=Trueは必須。中間者攻撃を防御する
        response = requests.get(url, verify=True, timeout=5)
        return response.json()
    except requests.exceptions.SSLError:
        # ログを吐いて即座に遮断する
        print("致命的: SSL検証失敗。通信を中断します。")
        raise

—

結びに:セキュリティは「自動化」と「諦め」のバランス

多くのエンジニアは「利便性」を優先してセキュリティ設定を緩めるが、それはインフラに対する冒涜だ。KubernetesのPSAは、開発者に「最初から正しい設定を書かせる」ための強力なナッジ(後押し)になる。

「privileged なPodが動かないと不便だ」という声が聞こえたら、それは設計が間違っているサインだ。コンテナの壁を破らなければ達成できない機能など、Webアプリケーションには存在しない。

今日からあなたのクラスタで pod-security.kubernetes.io/enforce=restricted を設定せよ。それが、システムを「守れるエンジニア」になるための第一歩だ。

コメント

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