Pod Security Admission を動かして理解する

v1.37 時点(2026年10月26日 確認)

Kubernetes 基本仕様第39回 / 全45回 ・ 第8部 セキュリティとアクセス制御
目次

この記事では Pod に求めるセキュリティの基準を Namespace ごとに守らせる Pod Security Admission の仕組みを、図で説明します。Pod やコンテナの権限を決める SecurityContext の主な項目、Pod Security Standards の 3 つの水準、Namespace のラベルで選ぶ 3 つのモードの違いがわかります。後半では kind のクラスタで、root で動く Pod が水準によって許されたり拒否されたりすることを確かめる手順を示します。

第38回で見た ServiceAccount と RBAC の内容を前提にしています。RBAC は誰にどの操作を許すかを決める仕組みでした。この回では、Pod の作成が許されたあとで、その Pod の中身が基準を満たしているかを確かめる仕組みを扱います。まだの方は先にこちらをどうぞ。

基本仕様 ・ 第38回 ServiceAccount と RBAC の仕組みPod の中のプログラムが API を使うときの身元になる ServiceAccount と、誰にどの操作を許すかを決める RBAC の仕組みを図で説明します。トークンが Pod に届く流れ、Role と RoleBinding の書き方、権限が足し算だけで決まることを押さえ、権限の無い ServiceAccount が断られる様子を kind のクラスタで確かめる手順を紹介します。

以下の図は、後半の手順で使う 3 つの Pod(特権で動く privileged-pod、root で動く root-pod、Restricted を満たす nonroot-pod)が、enforce を baseline にした Namespace と restricted にした Namespace で作れるかどうかを、公式ドキュメントの決まりからまとめたものです。

Namespace の enforce の水準と、作れる Pod

上の baseline より下の restricted のほうが、作れる Pod が少なくなります。図には無い privileged の水準は制限が無いので、3 つとも作れます。手順では Namespace を 2 つ作るのではなく、1 つの Namespace(psa-demo)の enforce を baseline から restricted に上げて試します。図のうち手順で試すのは、baseline の privileged-pod と root-pod、restricted の root-pod と nonroot-pod です。ラベルを付けていない Namespace の扱いは、「Namespace のラベルと 3 つのモード」で説明します。

参考:Pod Security Standards | Kubernetes、Pod Security Admission | Kubernetes

Pod Security Admission とは

この回には名前の似たものが 3 つ出てきます。違いを次の表にまとめます。

名前何かどこに書く・どこで働く
SecurityContextPod やコンテナの権限の設定Pod のマニフェストに書く
Pod Security StandardsKubernetes の公式ドキュメントが、Pod の隔離の度合いを 3 つの水準で定めた基準水準の名前を、Namespace のラベルの値に書く
Pod Security Admission基準を Pod に守らせる仕組みkube-apiserver の中で働く。実体は PodSecurity というアドミッションコントローラー

SecurityContext は Pod やコンテナの権限とアクセス制御を決める設定 です。公式ドキュメントの FAQ は SecurityContext と、Pod Security Standards のようなセキュリティのプロファイルとの違いを、次のように説明しています。SecurityContext は Pod とコンテナを実行時に設定するもので、コンテナランタイム(第4回)に渡すパラメータにあたります。一方、プロファイルは SecurityContext の中の設定や、その外の関連する設定を守らせる、コントロールプレーンの仕組みです。

参考:Configure a Security Context for a Pod or Container | Kubernetes、Pod Security Standards | Kubernetes

Pod Security Standards は Pod の隔離の度合いを 3 つの水準で定めたもので、確かめる中身の多くは SecurityContext です。公式ドキュメントによると、この基準を使うと、Pod の振る舞いをどう制限したいかを明確で一貫した形で決められます。公式ドキュメントは 3 つの水準をポリシー(policy)やプロファイル(profile)とも呼び、Pod Security Admission のページでは水準(level)と呼んでいます。この記事では水準と呼びます。文中では Baseline のように大文字で始め、ラベルの値では baseline のように小文字で書きますが、同じものです。

参考:Pod Security Standards | Kubernetes、Pod Security Admission | Kubernetes

Pod Security Admission は Pod Security Standards に照らして Pod を確かめる、Kubernetes に組み込まれたアドミッションコントローラー です。基準の制限を、Pod が作られるときに Namespace の単位で当てはめます。アドミッションコントローラーは第30回で見たとおり、kube-apiserver へのリクエストを、認証と認可のあと、保存する前に受け止めるコードです。詳しくは第40回で扱います。Pod Security Admission として働く PodSecurity の種類は、公式ドキュメントによると検証(Validating)です。PodSecurity は新しい Pod を受け付ける前に、求められたセキュリティの設定と、その Pod が入る Namespace に許された Pod Security Standards の制限をもとに、受け付けるかどうかを決めます。

参考:Pod Security Admission | Kubernetes、Admission Control in Kubernetes | Kubernetes、Admission Control in Kubernetes | Kubernetes

PodSecurity は v1.25 から Stable で、v1.37 の公式ドキュメントでは既定で有効なアドミッションコントローラーの一覧に入っています。PodSecurity は PodSecurityPolicy という古いアドミッションコントローラーを置き換えたもので、PodSecurityPolicy は v1.25 で Kubernetes から取り除かれました。

参考:Admission Control in Kubernetes | Kubernetes、Admission Control in Kubernetes | Kubernetes、Pod Security Policies | Kubernetes

なぜ Pod Security Admission が必要なのか

第38回で見た RBAC は Pod を作る操作を許すかどうかを決めます。Role の rules に書くのは API グループ、リソース、操作など、どの API の操作を許すかです。API リファレンスの PolicyRule にも、作られる Pod がどんな権限で動くかを決めるフィールドはありません。RBAC の推奨のページ(Role Based Access Control Good Practices)によると、特権(privileged)の Pod を動かせる利用者は、それを使ってノードへのアクセスを得られ、さらに権限を広げられる可能性もあります。第18回で見た、privileged: true で動くコンテナはそのノードで使われているすべての Secret にアクセスできる、という注意もその一例です。

参考:PolicyRule | Kubernetes API Reference、Role Based Access Control Good Practices | Kubernetes、Secrets | Kubernetes

同じページは、安全で隔離された Pod を作ることを完全には任せられない利用者がいるなら、Baseline か Restricted の水準を強制するべきだ(should)としています。その手段として挙げているのが、Pod Security Admission か、ほかの(サードパーティーの)仕組みです。第37回で見たとおり、Pod Security Admission は Namespace に付けるラベルで、Pod に求める基準を Namespace ごとに決めます。

参考:Role Based Access Control Good Practices | Kubernetes、Pod Security Admission | Kubernetes

Pod Security Admission の仕組み

SecurityContext を書く場所

SecurityContext は 2 か所に書けます。Pod 全体に当てはめる spec.securityContext と、コンテナごとの spec.containers[*].securityContext です。API リファレンスでは前者を PodSecurityContext、後者を SecurityContext と呼びますが、この記事では両方をまとめて SecurityContext と呼びます。一部の項目は両方にあり、両方に書いたときはコンテナの値が優先されます。タスクのページの例でも、Pod に runAsUser: 1000、コンテナに runAsUser: 2000 を書いたとき、コンテナのプロセスはユーザー 2000 で動いています。

参考:Pod | Kubernetes API Reference、Pod | Kubernetes API Reference、Configure a Security Context for a Pod or Container | Kubernetes

以下は公式ドキュメントの Pod の例から、2 か所の securityContext が見えるように一部を抜き出したものです(... は省略)。

yaml
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    ...
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    ...
    securityContext:
      allowPrivilegeEscalation: false

参考:Configure a Security Context for a Pod or Container | Kubernetes

SecurityContext の主な項目

項目の意味には Linux の用語が出てきます。UID は Linux のユーザーを表す番号で、GID はグループを表す番号です。管理者のユーザーである root は UID 0 です。この UID は第5回などで見たオブジェクトの UID(metadata.uid)とは別のものです。

参考:Configure a Security Context for a Pod or Container | Kubernetes、Pod | Kubernetes API Reference

ユーザーの namespace のページによると、コンテナの中で root で動くプロセスは、コンテナから抜け出された場合、ノードでも root の権限を持ちます。Restricted の水準が root 以外で動かすことを求めるのは、こうした被害を小さくするためだと考えられます。同じページが、この心配の無い Pod(ユーザーの namespace を使う Pod)では Restricted でも runAsNonRoot と runAsUser を確かめないとしていることからも、そう読めます。

参考:User Namespaces | Kubernetes、User Namespaces | Kubernetes

ケーパビリティ(capabilities)はプロセスに root のすべての権限ではなく、一部の権限だけを与える仕組みです。公式ドキュメントによると、Linux はケーパビリティで使える権限を種類に分けていて、プロセスはすべての権限をもらわなくても、決まった操作に要る権限だけを得られます。一般に、CAP_NET_BIND_SERVICE は 1024 より小さい番号のポートで待ち受ける権限です。公式ドキュメントは、特権コンテナの root が使えてしまう強いケーパビリティの例として、CAP_SYS_ADMIN と CAP_NET_ADMIN を挙げています。Kubernetes のマニフェストでは、CAP_ を除いた NET_BIND_SERVICE の形で書かなければなりません(must)。seccomp はプロセスのシステムコールを絞る仕組みです。システムコールは一般に、プログラムが OS の中心部(カーネル)に処理を頼む呼び出しのことです。

参考:Linux kernel security constraints for Pods and containers | Kubernetes、Linux kernel security constraints for Pods and containers | Kubernetes、Configure a Security Context for a Pod or Container | Kubernetes

主な項目を、API リファレンスとタスクのページの説明に沿って次の表にまとめます。

項目書ける場所意味
runAsUserPod・コンテナコンテナのプロセスを動かす UID。書かなければ、イメージのメタデータで指定されたユーザー
runAsGroupPod・コンテナプロセスを動かす GID(グループの番号)。書かなければ、コンテナランタイムの既定
runAsNonRootPod・コンテナtrue なら、kubelet が実行時にイメージを確かめ、UID 0 で動くならコンテナを起動しない。書かないか false なら確かめない
fsGroupPod だけPod のすべてのコンテナに当てはまる特別な補助グループ。一部の種類のボリュームは kubelet がこのグループの持ち物に変え、書かなければ変えない
allowPrivilegeEscalationコンテナだけプロセスが親のプロセスより多くの権限を得られるか。書かなければ true で、特権か CAP_SYS_ADMIN を持つコンテナでは常に true
privilegedコンテナだけ特権モードで動かし、プロセスはホストの root とほぼ同じになる。既定は false
capabilitiesコンテナだけ足す(add)・外す(drop)ケーパビリティ。書かなければ、コンテナランタイムが与える既定の組
readOnlyRootFilesystemコンテナだけコンテナのルートのファイルシステムを読み取り専用にする。既定は false
seccompProfilePod・コンテナseccomp のプロファイル。type は RuntimeDefault(ランタイムの既定)、Localhost(ノードのファイル)、Unconfined(使わない)

参考:Pod | Kubernetes API Reference、Pod | Kubernetes API Reference、Configure a Security Context for a Pod or Container | Kubernetes

表の補助グループは一般に、プロセスが主のグループ(GID)のほかに属するグループのことです。親のプロセスは一般に、そのプロセスを起動したプロセスのことです。

タスクのページによると、allowPrivilegeEscalation は、たとえば setuid のプログラム(一般に、実行するとファイルの持ち主の権限で動くプログラム)で権限を得られるかを決めます。allowPrivilegeEscalation: false は特権で動くコンテナや CAP_SYS_ADMIN を持つコンテナとは食い違うので、組み合わせられません。

参考:Configure a Security Context for a Pod or Container | Kubernetes

表のとおり、runAsUser を書かなかったとき、コンテナがどのユーザーで動くかはイメージ次第です。メタデータはイメージに付いている情報のことです。次に引く Docker の公式ドキュメントは Docker のコンテナについての説明なので、Kubernetes にとっては間接的な根拠です。このドキュメントはコンテナの既定のユーザーを root(UID 0)とし、Dockerfile(一般に、イメージの作り方を書いたファイル)の USER の命令で既定のユーザーを決められるとも説明しています。そのため、イメージがユーザーを指定していなければ、Kubernetes でも root で動くと考えられます。後半の手順では root で動くことをはっきりさせるため、runAsUser: 0 を書きます。

参考:Pod | Kubernetes API Reference、Running containers | Docker Docs

Pod Security Standards の 3 つの水準

公式ドキュメントによると、Pod Security Standards の 3 つのポリシーは積み重ね(cumulative)で、とても緩いものからとても厳しいものまで並びます。3 つの水準を、公式ドキュメントの表に沿って次の表にまとめます。

水準公式ドキュメントの説明
Privileged制限の無いポリシーで、できる限り広い権限を与える。知られている権限の昇格を許す
Baseline最小限の制限のポリシーで、知られている権限の昇格を防ぐ。既定の(最小限に指定した)Pod の設定を許す
Restricted強く制限したポリシーで、現在の Pod の堅牢化(hardening)のベストプラクティスに従う

参考:Pod Security Standards | Kubernetes

権限の昇格は一般に、本来与えられた以上の権限を手に入れることです。公式ドキュメントによると、Privileged はシステムや基盤のワークロード、Baseline は一般的なコンテナのワークロード、Restricted はセキュリティが重要なアプリケーションや信頼の度合いの低い利用者を主に想定しています。

参考:Pod Security Standards | Kubernetes、Pod Security Standards | Kubernetes、Pod Security Standards | Kubernetes

Baseline と Restricted にはそれぞれ確かめる項目の表があります。Restricted は積み重ねなので、Baseline の項目も確かめます。この回の手順と警告の例に出てくる項目を、次の表にまとめます。

水準項目公式ドキュメントの制限
BaselinePrivileged Containers特権の Pod はほとんどのセキュリティの仕組みを無効にするので、禁止しなければならない(must)。privileged は書かないか false
BaselineHost Namespacesホストの namespace の共有は禁止しなければならない(must)。hostNetwork、hostPID、hostIPC は書かないか false
BaselineHostPath VolumeshostPath のボリュームは禁止しなければならない(must)
BaselineCapabilities決まった一覧を超えるケーパビリティの追加は禁止しなければならない(must)
RestrictedPrivilege Escalation権限の昇格を許すべきではない(should not)。allowPrivilegeEscalation は false だけ
RestrictedRunning as Non-rootroot 以外のユーザーで動くよう求めなければならない(must)。runAsNonRoot は true だけ(Pod に書けばコンテナでは省ける)
RestrictedRunning as Non-root userrunAsUser を 0 にしてはならない(must not)
RestrictedSeccompRuntimeDefault か Localhost を明示しなければならない(must)。Unconfined も、書かないことも禁止
RestrictedCapabilitiesALL(すべてのケーパビリティ)を外さなければならない(must)。足し戻せるのは NET_BIND_SERVICE だけ

参考:Pod Security Standards | Kubernetes、Pod Security Standards | Kubernetes

Host Namespaces の namespace は Linux の仕組み(一般に、プロセスから見えるものを分ける仕組み)のことで、Kubernetes の Namespace とは別のものです。API リファレンスによると、hostNetwork、hostPID、hostIPC は、それぞれホストのネットワーク、プロセス番号(PID)、プロセス間通信(IPC)の namespace を使う指定です。この回の手順では使いません。第26回で見たとおり、hostPath は Baseline で禁止されています。Restricted の表のうち、Privilege Escalation、Seccomp、Capabilities の 3 つは、v1.25 以降は spec.os.name(Pod が動く OS を書くフィールド)が windows ではない Pod だけに求められます。

参考:Pod | Kubernetes API Reference、Pod Security Standards | Kubernetes

名前の似た Running as Non-root と Running as Non-root user は、別々の項目です。前者は runAsNonRoot: true を書いたか(書けば kubelet が起動時に確かめる)を見て、後者は runAsUser に 0 を書いていないかを見ます。後半の手順の root-pod は runAsNonRoot を書かずに runAsUser: 0 を書くので、両方に違反します。

公式ドキュメントの Baseline の表(全項目)にも、runAsUser や runAsNonRoot の項目はありません。そのため root で動く Pod でも、ほかの項目を満たしていれば Baseline の Namespace で作れると読めます。後半の手順で確かめるのは、この違いです。

Pod の作成が許されるか拒否されるか

以下の図は、利用者が kubectl で Pod を作るリクエストを送り、kube-apiserver が認証と認可のあと、Pod Security Admission で Namespace の水準に照らして許すか拒むかを決める流れを表しています。

Pod を作るリクエストが、Pod Security Admission で許されるか拒否されるかの流れ

戻る 2 本の線のうち、赤の実線は拒否の道で、灰色の破線は許可されて応答が返る道です。許可の線の「結果」は Pod を作ったことを知らせる応答で、kubectl では手順で見る pod/<名前> created の行にあたると考えられます。この許可の応答には、warn の水準に違反したときの警告が付くことがあります。認可で断られる道は第38回の図にあるので、この図では省いています。認証で断られる道も省いています。アドミッションの段階の細部(内容を変える段階と確かめる段階など)は、第40回で扱います。

PodSecurity は Pod が入る Namespace のラベルで決まった水準を、その Pod が満たすかを確かめます。公式ドキュメントによると、Pod Security Admission は Pod Security Standards の水準に従って、Pod の SecurityContext とほかの関連するフィールドに条件を課します。Pod Security Standards の表が挙げているのは、Pod の定義のフィールド(Restricted Fields)と、許される値です。runAsNonRoot の場合、イメージが実際に UID 0 で動くかを確かめるのは、コンテナを起動するときの kubelet です。

参考:Pod Security Admission | Kubernetes、Pod Security Standards | Kubernetes、Pod | Kubernetes API Reference

Namespace のラベルと 3 つのモード

水準は Namespace のラベルで選びます。公式ドキュメントには、モードごとに次の 2 つのラベルを使う例が載っています。

yaml
# The per-mode level label indicates which policy level to apply for the mode.
#
# MODE must be one of `enforce`, `audit`, or `warn`.
# LEVEL must be one of `privileged`, `baseline`, or `restricted`.
pod-security.kubernetes.io/<MODE>: <LEVEL>

# Optional: per-mode version label that can be used to pin the policy to the
# version that shipped with a given Kubernetes minor version (for example v1.37).
#
# MODE must be one of `enforce`, `audit`, or `warn`.
# VERSION must be a valid Kubernetes minor version, or `latest`.
pod-security.kubernetes.io/<MODE>-version: <VERSION>

参考:Pod Security Admission | Kubernetes

<MODE> に書くモードは、違反を見つけたときにコントロールプレーンが何をするかを決めます。3 つのモードを次の表にまとめます。2 列目は公式ドキュメントの表に沿い、3 列目はこの記事で補ったものです。

モード公式ドキュメントの説明違反の知らせが届く先
enforceポリシーに違反すると、Pod は拒否されるPod を作るリクエストを送った側へのエラー(kubectl で直接作れば利用者、Deployment からならコントローラー)
auditポリシーに違反すると、監査ログ(audit log)に記録されるイベントに監査の注釈が加わる。それ以外は許される監査ログ。あとから監査ログを見て気づく
warnポリシーに違反すると、利用者に向けた警告が出る。それ以外は許されるPod やワークロードのリソースを作った利用者への警告。kubectl ではその場で Warning: の行として出る

参考:Pod Security Admission | Kubernetes、Apply Pod Security Standards at the Namespace Level | Kubernetes

監査ログは kube-apiserver が受けたリクエストなど、クラスタで行われた操作を順に記録するものです。監査の注釈は監査ログのイベントに付く情報で、第42回で扱うオブジェクトの metadata のアノテーションとは付く場所が違います。手順の kind のクラスタでは監査ログが記録されないと考えられるので(手順で説明します)、audit は確かめません。

1 つの Namespace に、どのモードをいくつ設定してもよく、モードごとに違う水準にもできます。タスクのページの例では enforce を baseline に、warn と audit を restricted にしています。この Namespace では Baseline を満たさない Pod は拒否されます。Restricted を満たさない Pod は作られ、作った利用者に警告が返り、監査ログのイベントに注釈が付きます。

参考:Auditing | Kubernetes、Pod Security Admission | Kubernetes、Enforce Pod Security Standards with Namespace Labels | Kubernetes

水準の中身は版によって変わります。たとえば v1.25 では、Restricted が spec.os.name(Pod が動く OS を書くフィールド)を使うように変わりました。-version のラベルは水準の中身を、ある Kubernetes のマイナーバージョン(v1.37 など)のものに固定します。latest も書けます。手順では latest を使います。以下は先ほどのタスクのページにある、ラベルをマニフェストに書いた Namespace の例です。版を v1.37 に固定しています。

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-baseline-namespace
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.37

    # We are setting these to our _desired_ `enforce` level.
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.37
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.37

参考:Enforce Pod Security Standards with Namespace Labels | Kubernetes、Pod Security Standards | Kubernetes

ラベルの無い Namespace はどうなるのでしょうか。アドミッションコントローラーの設定ファイルの例のコメントによると、モードのラベルが無いときの既定の水準は privileged、既定の版は latest です。公式ドキュメントによると、このアドミッションコントローラーにはクラスタ全体の既定と免除を設定できます。そのため、設定ファイルで既定を変えていなければ、ラベルの無い Namespace の Pod は制限されないと読めます。

参考:Enforce Pod Security Standards by Configuring the Built-in Admission Controller | Kubernetes、Enforce Pod Security Standards by Configuring the Built-in Admission Controller | Kubernetes

Deployment などに当てはめられるのは warn と audit だけ

Pod は多くの場合、Deployment(第12回)や Job(第16回)のようなワークロードのリソースから間接的に作られます。公式ドキュメントによると、違反を早く見つけられるように、audit と warn のモードはワークロードのリソースにも当てはめられます。一方、enforce はワークロードのリソースには当てはめられず、そこから作られる Pod だけに当てはめられます。

参考:Pod Security Admission | Kubernetes

そのため enforce の水準に違反する Deployment も、作ること自体は成功すると考えられます(warn のラベルがあれば警告が出ます)。Pod を作るのはコントローラーなので、拒否されるのはコントローラーが Pod を作るときです。Deployment を作った利用者の手元には拒否のエラーが出ないはずなので、Deployment を作るときは warn の警告が手がかりになります。

Pod が増えないことには、Deployment の状態で気づけると考えられます。Deployment のページには、クォータが足りずに Pod を作れない例が載っています。その例では kubectl describe deployment の Conditions: に ReplicaFailure(True、理由は FailedCreate)が現れます。enforce で Pod が拒否されたときも、同じように現れると考えられます。

参考:Deployments | Kubernetes

免除(exemptions)

決まった利用者、RuntimeClass(コンテナの動かし方を選ぶ仕組み)、Namespace を確かめる対象から外す、免除という仕組みもあります。免除はアドミッションコントローラーの設定ファイルで一つ一つ明示しなければなりません(must)。この回の手順では使いません。利用者を免除しても、enforce から外れるのはその利用者が Pod を直接作るときだけです。そのため公式ドキュメントは、コントローラーの ServiceAccount(system:serviceaccount:kube-system:replicaset-controller など)は一般に免除するべきではない(should not)としています。免除すると、対応するワークロードのリソースを作れるすべての利用者を、暗に免除することになるからです。

参考:Pod Security Admission | Kubernetes

すでにある Pod とラベルを変えたときの警告

Pod Security Admission は Pod を受け付けるときに確かめる仕組みですが、公式ドキュメントによると、enforce の水準や版のラベルを足したり変えたりすると、PodSecurity はその Namespace の各 Pod を新しいポリシーで確かめます。違反は利用者に警告として返されます。この警告は warn のモードの警告とは別のものだと読めます。warn のラベルの無いクラスタでも出ていて(後半で引用する公式のチュートリアルの例)、文面も warn の警告と違うからです。warn の警告は Pod やワークロードのリソースを作るときなどに出るもので、こちらは enforce のラベルを変えたときに、すでにある Pod について出るものです。

参考:Enforce Pod Security Standards with Namespace Labels | Kubernetes、Apply Pod Security Standards at the Cluster Level | Kubernetes、Pod Security Admission | Kubernetes

このメディアで確かめた範囲の公式ドキュメントには、このとき既存の Pod を消したり止めたりするとは書かれていません。免除の節には、メタデータや tolerations など、決まったフィールドだけを変える更新は、その Pod が今の水準に違反していても拒否しない、という説明があります。違反した Pod が残っていることを前提にした説明なので、既存の Pod はそのまま動き続けると考えられます。

参考:Pod Security Admission | Kubernetes

ラベルを変える前には、kubectl label に --dry-run=server を付けて試せます。--dry-run=server を付けると、リクエストは kube-apiserver に送られますが、保存はされません。公式ドキュメントによると、このときも Pod Security Standards に照らして確かめる処理は動きます。ポリシーを実際には変えずに、新しいポリシーで既存の Pod がどう扱われるかがわかります。

参考:kubectl label | Kubernetes、Enforce Pod Security Standards with Namespace Labels | Kubernetes

手元で試す

ここまでの内容を手元の kind のクラスタで試す手順を示します。手順は公式のチュートリアル「Apply Pod Security Standards at the Namespace Level」をもとにしました。既存の Namespace を確かめる段は「Apply Pod Security Standards at the Cluster Level」から取り入れています。流れは次の 3 つです。

  • 既存の Namespace に水準を当てたらどうなるかを、--dry-run=server で試す
  • Namespace psa-demo に enforce=baseline を付け、root で動く Pod は作れて、特権の Pod は拒否されることを確かめる
  • enforce を restricted に上げ、root で動く Pod が拒否され、Restricted を満たす Pod だけが作れることを確かめる

この節で引用する出力の例は、公式ドキュメントに載っている、ほかのクラスタで取ったものです。名前や時間などの値は手元とは違います。各段に書く成功の目安は公式ドキュメントから予想される結果で、手元で実行して確かめたものではありません。

コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。

試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成シングルノード(クラスタ名 kind):コントロールプレーン 1
必要なものDocker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes

v1.37 では PodSecurity が既定で有効で、公式のチュートリアルも kind create cluster で作ったクラスタで試しているので、追加の設定は要らないと考えられます。

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes、Admission Control in Kubernetes | Kubernetes

この回では第37回・第38回と同じく、シングルノードのクラスタを使います。Pod Security Admission は kube-apiserver の中で働くので、ノードが 1 つでも確かめられるからです。kind でクラスタを作る手順は、以下の記事で解説しています。

基本仕様 ・ 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

この回では端末を 2 つ使います。これまでの回と同じく、1 つ目の端末は変化の監視(この回では Pod)に取っておきます。監視を始めるのは「Pod を監視する」の段からです。クラスタの準備も含めて、操作は 2 つ目の端末で行います。2 つ目の端末を開いたら、cd でマニフェストを保存するディレクトリに移動しておきます。

シングルノードのクラスタを用意する

2 つ目の端末で、前の回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

公式のチュートリアルには、新しいクラスタだけを対象にしているという注記があります。そのため、この回ではクラスタを新しく作ります。kind が表示されたら、残したいものが無いかを確かめてから kind delete cluster で消します。前の回までの multi-node が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name multi-node で消してから進めてください。続けて、クラスタを作って一覧します。

console
$ kind create cluster
$ kind get clusters

kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-kind

ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes、Quick Start | kind、kubectl get | Kubernetes

既存の Namespace に水準を当てて試す

この段では、kube-system などのシステムの Pod が Baseline や Restricted を満たしていないこと、つまり Namespace をまとめて厳しくすると困ることを見ます。2 つ目の端末で、すべての Namespace に enforce=baseline のラベルを付けたらどうなるかを試します。kubectl label は第11回で使った、ラベルを書き換えるコマンドです。ns は namespace の短い名前、--all はすべての Namespace を選ぶ指定です。--dry-run=server を付けているので、ラベルは保存されません。

--dry-run=server を付け忘れると、すべての Namespace に本当にラベルが付きます。付いてしまったら、第31回で見たとおりキーの後ろに - を付けて、kubectl label ns --all pod-security.kubernetes.io/enforce- --context kind-kind で外せます。この外し方はラベルとアノテーションを扱う第42回でも説明します。

console
$ kubectl label --dry-run=server --overwrite ns --all \
    pod-security.kubernetes.io/enforce=baseline --context kind-kind

参考:Apply Pod Security Standards at the Cluster Level | Kubernetes、kubectl label | Kubernetes、Command line tool (kubectl) | Kubernetes

公式のチュートリアルの例では、出力は次のとおりです。

namespace/default labeled
namespace/kube-node-lease labeled
namespace/kube-public labeled
Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "baseline:latest"
Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes
Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes
Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged
namespace/kube-system labeled
namespace/local-path-storage labeled

参考:Apply Pod Security Standards at the Cluster Level | Kubernetes

例で警告が出るのは kube-system の Namespace だけです。Warning: の行の後半は違反した項目で、host namespaces、hostPath volumes、privileged は、Baseline の表の Host Namespaces、HostPath Volumes、Privileged Containers にあたると読めます。kube-system には第4回で見たコントロールプレーンのコンポーネントなどが入っていて、Privileged の水準が想定するシステムのワークロードにあたると考えられます。

例のクラスタの名前は psa-wo-cluster-pss なので、手元では Pod の名前のその部分が kind になると考えられます(etcd-kind-control-plane など)。末尾のランダムな文字列も違い、指摘される Pod の数や項目が例と違うこともあると考えられます。

続けて、restricted でも試します。

console
$ kubectl label --dry-run=server --overwrite ns --all \
    pod-security.kubernetes.io/enforce=restricted --context kind-kind

公式のチュートリアルの例では、出力は次のとおりです(kube-system の警告の行は一部を省略しました)。

namespace/default labeled
namespace/kube-node-lease labeled
namespace/kube-public labeled
Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "restricted:latest"
Warning: coredns-7bb9c7b568-hsptc (and 1 other pod): unrestricted capabilities, runAsNonRoot != true, seccompProfile
(省略)
namespace/kube-system labeled
Warning: existing pods in namespace "local-path-storage" violate the new PodSecurity enforce level "restricted:latest"
Warning: local-path-provisioner-d6d9f7ffc-lw9lh: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile
namespace/local-path-storage labeled

参考:Apply Pod Security Standards at the Cluster Level | Kubernetes

restricted では local-path-storage にも警告が出ます。runAsNonRoot != true や seccompProfile は Restricted の表の Running as Non-root と Seccomp にあたると読めます。この段では、どちらのコマンドでも kube-system について Warning: の行が出て、どの Namespace にも labeled の行が出れば成功です。--dry-run=server なので、pod-security.kubernetes.io/enforce のラベルは、どの Namespace にも付いていないはずです。

Namespace を作ってラベルを付ける

2 つ目の端末で、この回で使う Namespace psa-demo を作ります。チュートリアルの example を psa-demo に替えました。

console
$ kubectl create ns psa-demo --context kind-kind

公式のチュートリアルの例では、出力は次の 1 行です。手元では example の部分が psa-demo になります。

namespace/example created

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes

続けて、チュートリアルと同じラベルを付けます。enforce は baseline、warn と audit は restricted で、版はどれも latest です。新しく付けるラベルなので --overwrite は無くてもかまいませんが、チュートリアルに合わせて付けています。付けておくと、すでに同じキーのラベルがあっても値を書き換えられます。

console
$ kubectl label --overwrite ns psa-demo \
    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 \
    --context kind-kind

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes

前の段の例の namespace/default labeled と同じ形で、namespace/psa-demo labeled の 1 行が出るはずです。まだ Pod が無いので、警告は出ないと考えられます。ラベルを確かめます。

console
$ kubectl get ns psa-demo --show-labels --context kind-kind

LABELS 列に、付けた 6 つのラベルと、第37回で見た kubernetes.io/metadata.name=psa-demo が並ぶはずです。並べば、この段は成功です。

audit の結果は監査ログに記録されます。公式ドキュメントによると、kube-apiserver に --audit-policy-file で監査のポリシーを渡さなければ、イベントは記録されません。kind には監査を有効にする手順が別に用意されているので、既定のクラスタでは監査ログは記録されていないと考えられます。この回では audit の結果は確かめません。

参考:Auditing | Kubernetes、Auditing | kind

Pod を監視する

1 つ目の端末で、psa-demo の Pod を監視します。この監視は片付けの前まで止めずに続けます。

console
$ kubectl get pods -n psa-demo --watch --context kind-kind

参考:kubectl get | Kubernetes

まだ Pod が無いので、何も表示されないか、Pod が無いことを知らせる 1 行が出て、止まったように見えると考えられます。Pod が作られると、この端末に行が書き足されます。

root で動く Pod を作る

以下は root(UID 0)で動く Pod のマニフェストです。チュートリアルの nginx の Pod に namespace と securityContext.runAsUser: 0 を足し、名前を root-pod にしました。root-pod.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: root-pod
  namespace: psa-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    securityContext:
      runAsUser: 0

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes、Pod | Kubernetes API Reference

2 つ目の端末で、Pod を作ります。

console
$ kubectl apply -f root-pod.yaml --context kind-kind

Baseline には runAsUser の項目が無いので、Pod は作られるはずです。ただし warn は restricted なので、警告も出るはずです。公式のチュートリアルで、runAsUser を書かない nginx の Pod を作ったときの出力は次のとおりです。

Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/nginx created

参考:Apply Pod Security Standards at the Namespace Level | Kubernetes

例の Pod は SecurityContext を何も書いていないので、Baseline が許す「既定の Pod の設定」にあたると読めます。それでも Restricted の 4 つの項目に違反しています。手元の出力と例の違いを次の表にまとめます。

違い手元の出力
最後の行pod/nginx created の代わりに pod/root-pod created
警告の項目例の 4 つに加えて、runAsUser を 0 にしたこと(Running as Non-root user)の指摘も入ると考えられる

1 つ目の端末に root-pod の行が現れ、STATUS 列が Running になるまで待ちます。初めて使うイメージは取得に時間がかかることがあり、そのあいだは ContainerCreating のような値が出ると考えられます(第9回)。かかる時間はネットワークによって違います。なかなか Running にならなければ、kubectl describe pod root-pod -n psa-demo --context kind-kind の Events: で理由を見ます。

Running になったら、コンテナの中で id を動かして、どのユーザーで動いているかを見ます。kubectl exec は第10回で使った、コンテナの中でコマンドを動かすコマンドです。

console
$ kubectl exec root-pod -n psa-demo --context kind-kind -- id

参考:kubectl exec | Kubernetes

uid=0 で始まる 1 行が出るはずです。id の出力の形については後の「Restricted を満たす Pod を作る」で公式ドキュメントの例を引用します。この段では、警告付きで root-pod が作られて Running になり、uid=0 が表示されれば成功です。

特権の Pod を作る

以下は特権で動くコンテナを持つ Pod のマニフェストです。privileged-pod.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
  namespace: psa-demo
spec:
  containers:
  - name: nginx
    image: nginx
    securityContext:
      privileged: true

参考:Pod | Kubernetes API Reference

この段ではエラーが出るのが正しい結果です。Baseline の Privileged Containers に違反するので、enforce によって拒否されるはずです。2 つ目の端末で、Pod を作ります。

console
$ kubectl apply -f privileged-pod.yaml --context kind-kind

作成が拒否されたことを知らせるエラーが出て、pod/privileged-pod created の行は出ないはずです。拒否されたときのメッセージの例は、このメディアで確かめた範囲の公式ドキュメントにはありません。エラーの形を考える手がかりは次の 2 つです。どちらも推測です。

  • 第30回で見た、LimitRange や ResourceQuota による拒否の例は Error from server (Forbidden) で始まっていた。同じアドミッションでの拒否なので、似た形で始まると考えられる
  • warn の警告は would violate PodSecurity "restricted:latest": ... の形で、水準と違反した項目を示していた。エラーにも、水準(baseline)と項目(privileged)を表す語が入ると考えられる

1 つ目の端末を見ます。root-pod の行より下に、privileged-pod の行は増えないはずです。念のため、2 つ目の端末で探します。

console
$ kubectl get pod privileged-pod -n psa-demo --context kind-kind

見つからないことを知らせる 1 行が出れば、Pod は作られていません。この段では、作成がエラーになり、privileged-pod が見つからなければ成功です。

enforce を restricted に上げる

まず --dry-run=server で、psa-demo の enforce を restricted にしたらどうなるかを試します。

console
$ kubectl label --dry-run=server --overwrite ns psa-demo \
    pod-security.kubernetes.io/enforce=restricted --context kind-kind

参考:Enforce Pod Security Standards with Namespace Labels | Kubernetes

root-pod は Restricted に違反しているので、警告が出るはずです。この警告は「すでにある Pod とラベルを変えたときの警告」で見た、enforce のラベルを変えたときに既存の Pod について出るもので、warn のモードの警告とは別のものだと読めます。「既存の Namespace に水準を当てて試す」の例の kube-system の行と同じ形で、Namespace が psa-demo、Pod が root-pod の Warning: の行が出ると考えられます。警告を確かめたら、--dry-run=server を外して本当にラベルを変えます。

console
$ kubectl label --overwrite ns psa-demo \
    pod-security.kubernetes.io/enforce=restricted --context kind-kind

参考:Enforce Pod Security Standards with Namespace Labels | Kubernetes

同じ警告と、namespace/psa-demo labeled の 1 行が出るはずです。このとき 1 つ目の端末を見ます。「すでにある Pod とラベルを変えたときの警告」で見たとおり、root-pod は消されずに Running のまま残ると考えられます。この段では、警告が出てラベルが変わり、root-pod が Running のままなら成功です。

restricted の Namespace で root の Pod を作り直す

2 つ目の端末で、root-pod を消します。

console
$ kubectl delete pod root-pod -n psa-demo --context kind-kind

kubectl delete は既定では、リソースが消えるのを待ってから終わります。1 つ目の端末には STATUS 列が Terminating になった root-pod の行が出ると考えられます。コマンドが終わったら、同じマニフェストでもう一度作ります。

参考:kubectl delete | Kubernetes

この段でもエラーが出るのが正しい結果です。enforce が restricted になったので、Running as Non-root と Running as Non-root user などの項目に違反して、拒否されるはずです。

console
$ kubectl apply -f root-pod.yaml --context kind-kind

作成を知らせる行が出ず、エラーになることを確かめます。エラーの形は「特権の Pod を作る」と同じく公式ドキュメントに例が無く、手がかりも同じです。今度は水準が restricted で、root-pod を最初に作ったときの警告と同じような項目(runAsNonRoot != true など)が並ぶと考えられます。

1 つ目の端末を見ます。削除のときに出た最後の行より下に、root-pod の行が増えなければ、新しい Pod は作られていません。念のため、2 つ目の端末で探します。

console
$ kubectl get pod root-pod -n psa-demo --context kind-kind

見つからないことを知らせる 1 行が出れば、Pod は作られていません。この段では、作成がエラーになり、root-pod が見つからなければ成功です。同じマニフェストが baseline では作れて restricted では拒否されたことになります。

Restricted を満たす Pod を作る

以下は Restricted の項目を満たす Pod のマニフェストです。イメージ、command、runAsUser、runAsGroup、allowPrivilegeEscalation: false はタスクのページの例のままです。そこに Restricted の表に沿って runAsNonRoot、seccompProfile、capabilities を足しています。nonroot-pod.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nonroot-pod
  namespace: psa-demo
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: ["sh", "-c", "sleep 1h"]
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]

参考:Configure a Security Context for a Pod or Container | Kubernetes、Pod Security Standards | Kubernetes、Pod | Kubernetes API Reference

runAsNonRoot と seccompProfile は Pod 全体に書いたので、コンテナでは省けます。allowPrivilegeEscalation と capabilities はコンテナにしか書けないので、コンテナに書きます。2 つ目の端末で、Pod を作ります。

console
$ kubectl apply -f nonroot-pod.yaml --context kind-kind

enforce も warn も restricted で、この Pod は Restricted を満たすので、警告の無い pod/nonroot-pod created の 1 行だけが出るはずです。1 つ目の端末に nonroot-pod の行が現れ、Running になるまで待ちます。root-pod と同じく、初めて使うイメージは取得に時間がかかることがあります。なかなか Running にならなければ、kubectl describe pod nonroot-pod -n psa-demo --context kind-kind の Events: で理由を見ます。Running になったら、どのユーザーで動いているかを見ます。

console
$ kubectl exec nonroot-pod -n psa-demo --context kind-kind -- id

タスクのページの例では、出力は次のとおりです。

uid=1000 gid=3000 groups=2000,3000,4000

参考:Configure a Security Context for a Pod or Container | Kubernetes

例の Pod は fsGroup: 2000 と supplementalGroups: [4000] も書いているので、groups に 2000 と 4000 が入っています。この 2 つを書いていない手元の Pod では、groups に 2000 と 4000 は入らないと考えられます。この段では、警告なしで nonroot-pod が作られて Running になり、uid=1000 と gid=3000 が表示されれば成功です。

片付け

1 つ目の端末で Ctrl+C を押して、Pod の監視を止めます。

2 つ目の端末で、psa-demo の Namespace を消します。第37回で見たとおり、中の Pod も一緒に消えます。

console
$ kubectl delete ns psa-demo --context kind-kind

kubectl delete は既定で、Namespace が消えるのを待ってから終わります。Namespace は中の Pod などを消してから消えると考えられるので、コマンドが終わるまでに少し時間がかかることがあります。コマンドが終わったら、残っていないかを確かめます。

console
$ kubectl get ns psa-demo --context kind-kind

見つからないことを知らせる 1 行が出れば、削除できています。STATUS 列が Terminating で表示されたら、少し待ってからもう一度実行します。

参考:kubectl delete | Kubernetes、Share a Cluster with Namespaces | Kubernetes

最後にクラスタを削除し、消えたことを確かめます。続けて次の回を試す場合は、クラスタを残しておいてもかまいません。

console
$ kind delete cluster
$ kind get clusters

kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。保存した 3 つのマニフェストのファイルも、要らなければ消してかまいません。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

SecurityContext と Pod Security Admission でつまずきやすい点を、次の表にまとめます。

項目内容
書かなければイメージ次第runAsUser を書かないと、イメージのメタデータで指定されたユーザーで動く
ラベルの無い Namespace設定ファイルの例のコメントでは、モードのラベルが無いときの既定の水準は privileged
Deployment などに当てはめられるのは warn と audit だけenforce が当てはめられるのは、そこから作られる Pod。Deployment は作れても Pod が拒否されることがあると考えられる
既存の Podenforce のラベルを変えると、違反は警告として返る。確かめた範囲の公式ドキュメントには、既存の Pod を消すとは書かれていない
audit の結果監査ログに記録される。kube-apiserver に監査のポリシーを渡していなければ、イベントは記録されない

参考:Pod | Kubernetes API Reference、Enforce Pod Security Standards by Configuring the Built-in Admission Controller | Kubernetes、Pod Security Admission | Kubernetes、Enforce Pod Security Standards with Namespace Labels | Kubernetes、Auditing | Kubernetes

関連記事

次の回では、kube-apiserver がリクエストを受けてから保存するまでの流れを扱います。認証、認可、アドミッションの各段階の細部は、次の回で解説します。

基本仕様 ・ 第40回 kube-apiserver がリクエストを処理する流れkube-apiserver が API のリクエストを受け取ってから etcd に保存するまでの、認証・認可・アドミッションの流れを図で説明します。各段階で誰が何を判断し、どの段階でどう断られるかを押さえ、リクエストが段階ごとに拒否される様子を kind のクラスタで確かめる手順を紹介します。

Pod Security Admission のラベルを付ける Namespace と、Namespace の範囲で働くほかの決まりについては、以下の記事で解説しています。

基本仕様 ・ 第37回 Namespace で分けられるもの、分けられないもの1 つのクラスタの中でリソースをグループに分ける Namespace の仕組みを図で説明します。名前の範囲になること、Namespace に属さないクラスタ全体のリソース、既定では Namespace をまたいで届く通信、Namespace を消すと中身も消える流れを押さえ、kind のクラスタで確かめる手順を紹介します。

第41回では起動しない Pod の原因を調べる方法を扱います。そこで使う kubectl debug の --profile には baseline と restricted のプロファイルがあり、公式ドキュメントはそれぞれを Pod Security Standards の Baseline と Restricted に合う設定と説明しています。

参考:Debug Running Pods | Kubernetes

基本仕様 ・ 第41回 起動しない Pod の原因を調べる思いどおりに動かない Pod の原因を探す Pod のデバッグの進め方を図で説明します。kubectl describe の状態とイベント、kubectl logs、終了メッセージで見る場所を押さえ、kubectl debug でエフェメラルコンテナや Pod のコピーを使って調べる手順を kind のクラスタで紹介します。

Kubernetes 基本仕様の目次へ