2026年10月8日

2026年10月8日

KubernetesのPod Security Standardsの設定方法:Pod Security Admissionで名前空間ごとに制限する

Kubernetes の Pod Security Standards(PSS)は、Namespace にラベルを付けるだけで「特権コンテナの禁止」「root 実行の禁止」などの基準を適用できる仕組みです。適用は組み込みの Pod Security Admission(PSA)が行います。この記事では、3つのレベルと3つのモードの違い、既存のワークロードを壊さずに段階的に有効にする手順、restricted に合わせた securityContext の書き方、違反したときの読み方と直し方を説明します。

PodSecurityPolicyからPod Security Admissionへ

以前は PodSecurityPolicy(PSP)という仕組みがありましたが、Kubernetes 1.25 で削除されました。代わりに同じ 1.25 で安定版(GA)になったのが Pod Security Admission です。PSA は API サーバーに組み込まれており、追加のインストールは不要です。古い記事で PSP の設定を見かけた場合、現在のクラスタでは使えないので注意してください。

PSA は PSP よりも単純で、細かいルールを自作する機能はありません。あらかじめ定義された基準(PSS)のどれを適用するかを、Namespace ごとに選ぶ仕組みです。

3つのレベル:privileged・baseline・restricted

PSS には3段階のレベルがあります。

  • privileged: 制限なし。CNI プラグインやストレージドライバーなど、ノードを直接操作する必要があるシステム系のワークロード向け
  • baseline: 既知の権限昇格を防ぐ最低限の基準。特権コンテナ、hostNetwork・hostPID・hostIPC、hostPath ボリューム、危険な capabilities の追加などを禁止する。多くの一般的なアプリはそのまま通る
  • restricted: 現時点のベストプラクティスに沿った厳しい基準。baseline の内容に加え、root 以外での実行(runAsNonRoot)、権限昇格の禁止(allowPrivilegeEscalation: false)、capabilities をすべて削除(追加できるのは NET_BIND_SERVICE のみ)、seccomp プロファイルの指定(RuntimeDefault または Localhost)、使えるボリューム種別の限定などを求める

目安として、アプリケーション用の Namespace は restricted を目標にし、どうしても通らないものだけ baseline にとどめる、という方針が扱いやすいです。

3つのモード:enforce・audit・warn

レベルとは別に、違反したときの扱いを決めるモードがあります。

  • enforce: 違反する Pod の作成を拒否する
  • audit: 作成は許可し、API サーバーの監査ログに記録する
  • warn: 作成は許可し、kubectl などの利用者に警告を表示する

モードは同時に複数使えます。たとえば「enforce は baseline、warn と audit は restricted」とすれば、現状は baseline で守りつつ、restricted に上げたときに何が引っかかるかを事前に把握できます。

注意点として、enforce が評価するのは Pod そのものです。Deployment を作っても拒否はされず、そこから作られる Pod が拒否されます。その結果、Deployment は作成できたのに Pod が1つも起動しない、という状況になります。一方、warn と audit は Deployment などのワークロードリソースのテンプレートも評価するため、適用時に警告で気付けます。

手順1:影響を事前に確認する

いきなり enforce を付ける前に、サーバー側のドライランで既存の Pod がどれだけ違反しているかを確認できます。

kubectl label --dry-run=server --overwrite ns my-app \
  pod-security.kubernetes.io/enforce=restricted

違反している Pod があれば、Pod 名と違反内容が警告として表示されます。ラベルは実際には付きません。クラスタ全体を一度に見たい場合は、ns my-app の代わりに --all を使う方法もあります。

手順2:warnとauditから始める

まず警告と監査だけを有効にして、一定期間様子を見ます。

apiVersion: v1
kind: Namespace
metadata:
  name: my-app
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

-version のラベルは、どのバージョンの基準で評価するかの指定です。latest にするとクラスタの更新に合わせて基準も新しくなり、更新後に急に違反扱いになるものが出る可能性があります。予期しない変化を避けたい場合は v1.30 のように固定し、クラスタ更新の際に見直します。

手順3:restrictedに合うようにマニフェストを直す

restricted を満たす securityContext の基本形です。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: web
        image: your-registry/web:1.0.0
        ports:
        - containerPort: 8080
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop: ["ALL"]
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir: {}

readOnlyRootFilesystem は restricted の必須項目ではありませんが、改ざん対策として有効なので併せて設定する例にしています。書き込みが必要なディレクトリは emptyDir でマウントします。

つまずきやすいのは、イメージ自体が root で動く前提で作られているケースです。runAsNonRoot を付けると、イメージの USER が root のままなら起動時にエラーになります。また、非 root ユーザーは原則として 1024 未満のポートを開けないため、80 番で待ち受けるイメージはそのままでは動きません。対処は次のいずれかです。

  • 非 root で動くよう作られたイメージを選ぶ
  • Dockerfile で専用ユーザーを作って USER を指定し、待ち受けポートを 8080 などに変える
  • ファイルの所有者が合わずに書き込めない場合は、fsGroup やイメージ側のパーミッションを調整する

たとえば WordPress の公式イメージ(Apache 版)は既定では root で起動し、80 番で待ち受ける作りなので、restricted の Namespace で動かすには設定変更が必要です。どこまで対応できるかはイメージのドキュメントを確認し、ステージング環境で動作を確かめてから本番に反映してください。

手順4:enforceをrestrictedに上げる

warn と audit で違反が出なくなったら、enforce を restricted に上げます。

kubectl label --overwrite ns my-app \
  pod-security.kubernetes.io/enforce=restricted

ラベルを変更しても、すでに動いている Pod が止まることはありません。影響が出るのは、次に Pod が作り直されるとき(デプロイ、ノードの drain、障害時の再作成など)です。そのため、ラベル変更の直後は問題がなくても、後日の再作成で初めて失敗が表面化することがあります。手順1のドライランで違反がないことを確認しておくのはこのためです。

違反したときの読み方

enforce で拒否されると、Pod は作られず、Deployment の ReplicaSet にイベントが残ります。

kubectl get rs -n my-app
kubectl describe rs <ReplicaSet名> -n my-app

イベントには violates PodSecurity "restricted:latest" に続いて、allowPrivilegeEscalation != false や runAsNonRoot != true のように、満たしていない項目が列挙されます。表示された項目を上の securityContext の例と見比べて、1つずつ直していきます。

クラスタ全体の既定値と適用除外

Namespace にラベルがない場合の既定のレベルは、API サーバーに渡す AdmissionConfiguration(PodSecurity 用の設定)で変更できます。特定のユーザーや Namespace、RuntimeClass を対象外にする exemptions もここで指定します。ただし、マネージド Kubernetes では API サーバーの起動設定を変更できないことが多く、その場合は Namespace のラベルで管理します。

kube-system などのシステム用 Namespace には特権が必要なコンポーネントが動いているため、むやみに restricted を付けないでください。

PSSだけでは足りない部分

PSS が見ているのは Pod の securityContext やボリュームなどの設定だけです。「信頼できるレジストリのイメージしか使わせない」「特定のラベルの付与を義務付ける」のような独自ルールは対象外です(2026年10月現在)。こうしたルールには、組み込みの ValidatingAdmissionPolicy や、Kyverno、OPA Gatekeeper といったポリシーエンジンを組み合わせます。また、Pod 間通信の制限は NetworkPolicy、API 操作の権限は RBAC の役割なので、PSS と併せて設計します。

まとめ

  • PSP は 1.25 で削除され、現在は Namespace のラベルで PSS を適用する PSA を使う
  • レベルは privileged・baseline・restricted、モードは enforce・audit・warn
  • まずドライランと warn/audit で影響を確認し、違反がなくなってから enforce を上げる
  • restricted では runAsNonRoot、allowPrivilegeEscalation: false、capabilities の全削除、seccomp の指定が要点
  • 既存 Pod はラベル変更では止まらず、再作成時に拒否される。ステージングで確かめてから本番へ

お気軽にご相談ください

お見積りへ お問い合わせへ
▲