Pod Security Admission を動かして理解する
v1.37 時点(2026年10月26日 確認)
目次
この記事では Pod に求めるセキュリティの基準を Namespace ごとに守らせる Pod Security Admission の仕組みを、図で説明します。Pod やコンテナの権限を決める SecurityContext の主な項目、Pod Security Standards の 3 つの水準、Namespace のラベルで選ぶ 3 つのモードの違いがわかります。後半では kind のクラスタで、root で動く Pod が水準によって許されたり拒否されたりすることを確かめる手順を示します。
第38回で見た ServiceAccount と RBAC の内容を前提にしています。RBAC は誰にどの操作を許すかを決める仕組みでした。この回では、Pod の作成が許されたあとで、その Pod の中身が基準を満たしているかを確かめる仕組みを扱います。まだの方は先にこちらをどうぞ。
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 で作れるかどうかを、公式ドキュメントの決まりからまとめたものです。
上の 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 つ出てきます。違いを次の表にまとめます。
| 名前 | 何か | どこに書く・どこで働く |
|---|---|---|
| SecurityContext | Pod やコンテナの権限の設定 | Pod のマニフェストに書く |
| Pod Security Standards | Kubernetes の公式ドキュメントが、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 が見えるように一部を抜き出したものです(... は省略)。
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 リファレンスとタスクのページの説明に沿って次の表にまとめます。
| 項目 | 書ける場所 | 意味 |
|---|---|---|
runAsUser | Pod・コンテナ | コンテナのプロセスを動かす UID。書かなければ、イメージのメタデータで指定されたユーザー |
runAsGroup | Pod・コンテナ | プロセスを動かす GID(グループの番号)。書かなければ、コンテナランタイムの既定 |
runAsNonRoot | Pod・コンテナ | true なら、kubelet が実行時にイメージを確かめ、UID 0 で動くならコンテナを起動しない。書かないか false なら確かめない |
fsGroup | Pod だけ | Pod のすべてのコンテナに当てはまる特別な補助グループ。一部の種類のボリュームは kubelet がこのグループの持ち物に変え、書かなければ変えない |
allowPrivilegeEscalation | コンテナだけ | プロセスが親のプロセスより多くの権限を得られるか。書かなければ true で、特権か CAP_SYS_ADMIN を持つコンテナでは常に true |
privileged | コンテナだけ | 特権モードで動かし、プロセスはホストの root とほぼ同じになる。既定は false |
capabilities | コンテナだけ | 足す(add)・外す(drop)ケーパビリティ。書かなければ、コンテナランタイムが与える既定の組 |
readOnlyRootFilesystem | コンテナだけ | コンテナのルートのファイルシステムを読み取り専用にする。既定は false |
seccompProfile | Pod・コンテナ | 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)のベストプラクティスに従う |
権限の昇格は一般に、本来与えられた以上の権限を手に入れることです。公式ドキュメントによると、Privileged はシステムや基盤のワークロード、Baseline は一般的なコンテナのワークロード、Restricted はセキュリティが重要なアプリケーションや信頼の度合いの低い利用者を主に想定しています。
参考:Pod Security Standards | Kubernetes、Pod Security Standards | Kubernetes、Pod Security Standards | Kubernetes
Baseline と Restricted にはそれぞれ確かめる項目の表があります。Restricted は積み重ねなので、Baseline の項目も確かめます。この回の手順と警告の例に出てくる項目を、次の表にまとめます。
| 水準 | 項目 | 公式ドキュメントの制限 |
|---|---|---|
| Baseline | Privileged Containers | 特権の Pod はほとんどのセキュリティの仕組みを無効にするので、禁止しなければならない(must)。privileged は書かないか false |
| Baseline | Host Namespaces | ホストの namespace の共有は禁止しなければならない(must)。hostNetwork、hostPID、hostIPC は書かないか false |
| Baseline | HostPath Volumes | hostPath のボリュームは禁止しなければならない(must) |
| Baseline | Capabilities | 決まった一覧を超えるケーパビリティの追加は禁止しなければならない(must) |
| Restricted | Privilege Escalation | 権限の昇格を許すべきではない(should not)。allowPrivilegeEscalation は false だけ |
| Restricted | Running as Non-root | root 以外のユーザーで動くよう求めなければならない(must)。runAsNonRoot は true だけ(Pod に書けばコンテナでは省ける) |
| Restricted | Running as Non-root user | runAsUser を 0 にしてはならない(must not) |
| Restricted | Seccomp | RuntimeDefault か Localhost を明示しなければならない(must)。Unconfined も、書かないことも禁止 |
| Restricted | Capabilities | ALL(すべてのケーパビリティ)を外さなければならない(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 の水準に照らして許すか拒むかを決める流れを表しています。
戻る 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 つのラベルを使う例が載っています。
# 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><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 に固定しています。
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 だけに当てはめられます。
そのため enforce の水準に違反する Deployment も、作ること自体は成功すると考えられます(warn のラベルがあれば警告が出ます)。Pod を作るのはコントローラーなので、拒否されるのはコントローラーが Pod を作るときです。Deployment を作った利用者の手元には拒否のエラーが出ないはずなので、Deployment を作るときは warn の警告が手がかりになります。
Pod が増えないことには、Deployment の状態で気づけると考えられます。Deployment のページには、クォータが足りずに Pod を作れない例が載っています。その例では kubectl describe deployment の Conditions: に ReplicaFailure(True、理由は FailedCreate)が現れます。enforce で Pod が拒否されたときも、同じように現れると考えられます。
免除(exemptions)
決まった利用者、RuntimeClass(コンテナの動かし方を選ぶ仕組み)、Namespace を確かめる対象から外す、免除という仕組みもあります。免除はアドミッションコントローラーの設定ファイルで一つ一つ明示しなければなりません(must)。この回の手順では使いません。利用者を免除しても、enforce から外れるのはその利用者が Pod を直接作るときだけです。そのため公式ドキュメントは、コントローラーの ServiceAccount(system:serviceaccount:kube-system:replicaset-controller など)は一般に免除するべきではない(should not)としています。免除すると、対応するワークロードのリソースを作れるすべての利用者を、暗に免除することになるからです。
すでにある 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 はそのまま動き続けると考えられます。
ラベルを変える前には、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 を動かす仕組み)などを使ってください。
試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.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 でクラスタを作る手順は、以下の記事で解説しています。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。この回では端末を 2 つ使います。これまでの回と同じく、1 つ目の端末は変化の監視(この回では Pod)に取っておきます。監視を始めるのは「Pod を監視する」の段からです。クラスタの準備も含めて、操作は 2 つ目の端末で行います。2 つ目の端末を開いたら、cd でマニフェストを保存するディレクトリに移動しておきます。
シングルノードのクラスタを用意する
2 つ目の端末で、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
公式のチュートリアルには、新しいクラスタだけを対象にしているという注記があります。そのため、この回ではクラスタを新しく作ります。kind が表示されたら、残したいものが無いかを確かめてから kind delete cluster で消します。前の回までの multi-node が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name multi-node で消してから進めてください。続けて、クラスタを作って一覧します。
$ kind create cluster
$ kind get clusters
kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。続けて、ノードを一覧します。
$ 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回でも説明します。
$ 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
公式のチュートリアルの例では、出力は次のとおりです。
参考: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 でも試します。
$ kubectl label --dry-run=server --overwrite ns --all \
pod-security.kubernetes.io/enforce=restricted --context kind-kind
公式のチュートリアルの例では、出力は次のとおりです(kube-system の警告の行は一部を省略しました)。
参考: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 に替えました。
$ kubectl create ns psa-demo --context kind-kind
公式のチュートリアルの例では、出力は次の 1 行です。手元では example の部分が psa-demo になります。
参考:Apply Pod Security Standards at the Namespace Level | Kubernetes
続けて、チュートリアルと同じラベルを付けます。enforce は baseline、warn と audit は restricted で、版はどれも latest です。新しく付けるラベルなので --overwrite は無くてもかまいませんが、チュートリアルに合わせて付けています。付けておくと、すでに同じキーのラベルがあっても値を書き換えられます。
$ 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 が無いので、警告は出ないと考えられます。ラベルを確かめます。
$ 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 の結果は確かめません。
Pod を監視する
1 つ目の端末で、psa-demo の Pod を監視します。この監視は片付けの前まで止めずに続けます。
$ kubectl get pods -n psa-demo --watch --context kind-kind
まだ Pod が無いので、何も表示されないか、Pod が無いことを知らせる 1 行が出て、止まったように見えると考えられます。Pod が作られると、この端末に行が書き足されます。
root で動く Pod を作る
以下は root(UID 0)で動く Pod のマニフェストです。チュートリアルの nginx の Pod に namespace と securityContext.runAsUser: 0 を足し、名前を root-pod にしました。root-pod.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 を作ります。
$ kubectl apply -f root-pod.yaml --context kind-kind
Baseline には runAsUser の項目が無いので、Pod は作られるはずです。ただし warn は restricted なので、警告も出るはずです。公式のチュートリアルで、runAsUser を書かない nginx の Pod を作ったときの出力は次のとおりです。
参考: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回で使った、コンテナの中でコマンドを動かすコマンドです。
$ kubectl exec root-pod -n psa-demo --context kind-kind -- id
uid=0 で始まる 1 行が出るはずです。id の出力の形については後の「Restricted を満たす Pod を作る」で公式ドキュメントの例を引用します。この段では、警告付きで root-pod が作られて Running になり、uid=0 が表示されれば成功です。
特権の Pod を作る
以下は特権で動くコンテナを持つ Pod のマニフェストです。privileged-pod.yaml という名前で保存します。
apiVersion: v1
kind: Pod
metadata:
name: privileged-pod
namespace: psa-demo
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: trueこの段ではエラーが出るのが正しい結果です。Baseline の Privileged Containers に違反するので、enforce によって拒否されるはずです。2 つ目の端末で、Pod を作ります。
$ 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 つ目の端末で探します。
$ kubectl get pod privileged-pod -n psa-demo --context kind-kind
見つからないことを知らせる 1 行が出れば、Pod は作られていません。この段では、作成がエラーになり、privileged-pod が見つからなければ成功です。
enforce を restricted に上げる
まず --dry-run=server で、psa-demo の enforce を restricted にしたらどうなるかを試します。
$ 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 を外して本当にラベルを変えます。
$ 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 を消します。
$ kubectl delete pod root-pod -n psa-demo --context kind-kind
kubectl delete は既定では、リソースが消えるのを待ってから終わります。1 つ目の端末には STATUS 列が Terminating になった root-pod の行が出ると考えられます。コマンドが終わったら、同じマニフェストでもう一度作ります。
この段でもエラーが出るのが正しい結果です。enforce が restricted になったので、Running as Non-root と Running as Non-root user などの項目に違反して、拒否されるはずです。
$ kubectl apply -f root-pod.yaml --context kind-kind
作成を知らせる行が出ず、エラーになることを確かめます。エラーの形は「特権の Pod を作る」と同じく公式ドキュメントに例が無く、手がかりも同じです。今度は水準が restricted で、root-pod を最初に作ったときの警告と同じような項目(runAsNonRoot != true など)が並ぶと考えられます。
1 つ目の端末を見ます。削除のときに出た最後の行より下に、root-pod の行が増えなければ、新しい Pod は作られていません。念のため、2 つ目の端末で探します。
$ 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 という名前で保存します。
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 を作ります。
$ 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 になったら、どのユーザーで動いているかを見ます。
$ kubectl exec nonroot-pod -n psa-demo --context kind-kind -- id
タスクのページの例では、出力は次のとおりです。
参考: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 も一緒に消えます。
$ kubectl delete ns psa-demo --context kind-kind
kubectl delete は既定で、Namespace が消えるのを待ってから終わります。Namespace は中の Pod などを消してから消えると考えられるので、コマンドが終わるまでに少し時間がかかることがあります。コマンドが終わったら、残っていないかを確かめます。
$ kubectl get ns psa-demo --context kind-kind
見つからないことを知らせる 1 行が出れば、削除できています。STATUS 列が Terminating で表示されたら、少し待ってからもう一度実行します。
参考:kubectl delete | Kubernetes、Share a Cluster with Namespaces | Kubernetes
最後にクラスタを削除し、消えたことを確かめます。続けて次の回を試す場合は、クラスタを残しておいてもかまいません。
$ kind delete cluster
$ kind get clusters
kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。保存した 3 つのマニフェストのファイルも、要らなければ消してかまいません。
注意点
SecurityContext と Pod Security Admission でつまずきやすい点を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
| 書かなければイメージ次第 | runAsUser を書かないと、イメージのメタデータで指定されたユーザーで動く |
| ラベルの無い Namespace | 設定ファイルの例のコメントでは、モードのラベルが無いときの既定の水準は privileged |
| Deployment などに当てはめられるのは warn と audit だけ | enforce が当てはめられるのは、そこから作られる Pod。Deployment は作れても Pod が拒否されることがあると考えられる |
| 既存の Pod | enforce のラベルを変えると、違反は警告として返る。確かめた範囲の公式ドキュメントには、既存の 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 がリクエストを受けてから保存するまでの流れを扱います。認証、認可、アドミッションの各段階の細部は、次の回で解説します。
kube-apiserver がリクエストを処理する流れkube-apiserver が API のリクエストを受け取ってから etcd に保存するまでの、認証・認可・アドミッションの流れを図で説明します。各段階で誰が何を判断し、どの段階でどう断られるかを押さえ、リクエストが段階ごとに拒否される様子を kind のクラスタで確かめる手順を紹介します。Pod Security Admission のラベルを付ける Namespace と、Namespace の範囲で働くほかの決まりについては、以下の記事で解説しています。
Namespace で分けられるもの、分けられないもの1 つのクラスタの中でリソースをグループに分ける Namespace の仕組みを図で説明します。名前の範囲になること、Namespace に属さないクラスタ全体のリソース、既定では Namespace をまたいで届く通信、Namespace を消すと中身も消える流れを押さえ、kind のクラスタで確かめる手順を紹介します。第41回では起動しない Pod の原因を調べる方法を扱います。そこで使う kubectl debug の --profile には baseline と restricted のプロファイルがあり、公式ドキュメントはそれぞれを Pod Security Standards の Baseline と Restricted に合う設定と説明しています。
起動しない Pod の原因を調べる思いどおりに動かない Pod の原因を探す Pod のデバッグの進め方を図で説明します。kubectl describe の状態とイベント、kubectl logs、終了メッセージで見る場所を押さえ、kubectl debug でエフェメラルコンテナや Pod のコピーを使って調べる手順を kind のクラスタで紹介します。