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 はラベル変更では止まらず、再作成時に拒否される。ステージングで確かめてから本番へ