なぜ Pod Security Policy (PSP) は Kubernetes 1.21 から非推奨になったのか？

「Pod Security Policy (PSP) は Kubernetes 1.21 から非推奨 (deprecated) です！」という情報はよく目にするものの、肝心の「なぜ？」が整理された記事は見かけないと思い、PSP が抱えている課題や Kubernetes コミュニティでの議論の経緯などを調査してみました。

## 目次
<!-- タイトルとアンカー名を編集 -->
[Pod Security Policy (PSP) とは](#pod-security-policy-psp-とは)
[Pod Security Policy (PSP) が抱えている課題](#pod-security-policy-psp-が抱えている課題)
[今後の動向](#今後の動向)
[おわりに](#おわりに)
[参考文献](#参考文献)

<a id="#Chapter1"></a>
## Pod Security Policy (PSP) とは

この記事の本題である「なぜ PSP が非推奨になったのか？」という話を始める前に、PSP 自体について簡単にご紹介したいと思います。

 [Pod Security Policy (PSP)](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) とは、Pod のセキュリティに関わる設定を管理・制限するための機能であり、cluster-level のリソースとして定義して利用します。`kind: PodSecurityPolicy` の定義の中で、Pod のデプロイを許可するために必要な設定条件を記述したり、Pod にデフォルトで設定したいパラメータを記述することができます。

公式ドキュメントに記載されている [Create a policy and a pod](https://kubernetes.io/docs/concepts/policy/pod-security-policy/#create-a-policy-and-a-pod) の例を見てみましょう。`spec.privileged` を `false` に設定することで、privileged (特権) コンテナのデプロイを禁止しています。

``` yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: example
spec:
  privileged: false  # Don't allow privileged pods!
  # The rest fills in some required fields.
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'
```
`seLinux` 以降の行は必須フィールドのため記述していますが、`RunAsAny` を指定しているため特に制限される設定はありません。

上記の PSP を適用した環境で privileged (特権) コンテナのデプロイを実際に試してみると、`Invalid value: true: Privileged containers are not allowed` エラーによって Pod のデプロイが拒否されることが分かります。

```shell
$ cat pod.yaml 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    securityContext:
      privileged: true
```
```shell
$ kubectl apply -f pod.yaml 
Error from server (Forbidden): error when creating "pod.yaml": pods "myapp-pod" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
```
このように、デプロイを制限するための Pod のセキュリティポリシーを定義し、kubernetes 環境をセキュアに維持するというのが PSP のひとつの役割です。

本題から逸れるためこの記事では説明を割愛していますが、PodSecurityPolicy Admission Controller の有効化方法や、PSP で定義可能なフィールド一覧については [公式ドキュメント](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) をご参照ください。

<a id="#Chapter2"></a>
## Pod Security Policy (PSP) が抱えている課題

それでは、なぜこの便利そうな機能が非推奨になってしまうのでしょうか。[Kubernetes Blog](https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/) やコミュニティでの議論内容などからその理由を調査し、整理してみました。

### 1. Authorizing Model

PSP が抱えている1つ目の課題として、PSP のポリシーチェックをリクエストするために「`user` または `service account` に、`--verb=use --resource=podsecuritypolicies` の権限を与えるモデルになっている」という点が挙げられます。

もし `user` または `service account` がこの権限を持っていないと、以下のようなエラーが出力されて Pod を起動することができません。

```bash
Error creating: pods "nginx-6799fc88d8-" is forbidden: PodSecurityPolicy: unable to admit pod: []
```

ここで、`user` に権限を与えるというのは一見直感的に感じるのですが、たとえば権限を持った `user` が deployment リソースを作成したとしても、実際に Pod を作成するのは replicaset-controller であるため、replicaset-controller が権限を持っていない限り Pod を起動することはできません。

また、実運用上 kubernetes クラスタに Pod をデプロイする際に `user` が直接 Pod を起動することは殆どなく、「そもそも `user` への権限付与って何なんだっけ？」という疑問も生じます。

PSP は、OpenShift の Security Context Constraints (SCC) をベースに kubernetes 1.3 から beta 版として導入されましたが (beta-quality のレベルに到達したのは 1.8 から)、早い段階で導入されたということもあり、まだ実運用に適したモデルを十分に検討できるほどの状況ではなかったという背景もあります。

このような実態から、`service account` を用いた権限付与が推奨されていたわけではありますが、コミュニティとしては「PSP の Authorizing Model は紛らわしくて、実運用に見合ったモデルではない」という課題感を持っていたようです。

### 2. 運用開始が困難

`PodSecurityPolicy` リソースとしてデプロイしたポリシーに基づいて Pod のデプロイを許可・拒否してくれる PSP ですが、PodSecurityPolicy Admission Controller の有効化後 ポリシーが設定されていない状態だと、すべての Pod の起動が拒否されます。

```bash
$ kubectl run nginx --image=nginx
Error from server (Forbidden): pods "nginx" is forbidden: PodSecurityPolicy: no providers available to validate pod request
```

この仕様により、PodSecurityPolicy Admission Controller を有効化する前には、必要なポリシーをすべて完璧に設定しておく必要があり、PSP の運用を開始するハードルを高めてしまっています。

せめて、実際に拒否せずにポリシーの監査ログをファイルに書き出す Audit mode があればよいのですが、残念ながら PSP に Audit mode のような機能はありません。

このような状況から、PSP をデフォルトで有効化することは困難であり、現時点では PSP が一般提供 (GA) になる未来は想像できないという声もあるくらいでした。

### 3. ポリシーの優先付けが不可

複数のポリシーが存在した場合、優先付けを行うことはできず、アルファベット順で一番先頭のポリシーが適用されるそうです。「本当だろうか？」と疑問に思ったので、実際に試してみました。

まず、以下2つのポリシーを作成します。

-  a-example： `spec.privileged: true` を設定したポリシー
-  b-example： `spec.privileged: false` を設定したポリシー（特権コンテナの起動は拒否）

この状態で、[Pod Security Policy (PSP) とは](#pod-security-policy-psp-とは) の章で紹介した `privileged: true` を設定した Pod をデプロイしてみたところ、たしかにアルファベット順で先頭になる a-example のみが適用されて、特権を持つ Pod の起動が許可されてしまう挙動になりました。

これは、仕様を知らないと想定していないポリシー設定がされてしまう可能性がありそうですね・・。

```bash
$ kubectl get psp
NAME        PRIV    CAPS   SELINUX    RUNASUSER   FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
a-example   true           RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            *
b-example   false          RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            *

$ kubectl apply -f pod.yaml 
pod/myapp-pod created

$ kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
myapp-pod   1/1     Running   0          5s
```

### その他

Kubernetes コミュニティ (SIG-Auth) での議論や、Kubecon のセッションなどでよく取り上げられていた課題を3つご紹介してきましたが、他にも以下のような課題が挙げられていました。

- PSP は Pod に関するポリシーを定義するものであり、他のリソースのポリシーを定義することができない
- Main コンテナと Sidecar コンテナが異なる権限を持っていたときの取り扱い
- Windows Pod や、Sandbox Pod (gVisor や Kata Containers) のサポート　など


<a id="#Chapter3"></a>
## 非推奨化が決まったきっかけ

ここまでご紹介したように、PSP に複数の課題があるのは理解しましたが、なぜ Kubernetes 1.21 から非推奨になると決まったのでしょうか。

そのきっかけとなったのは、kubernetes 1.19 でリリースされた、[Moving Forward From Beta](https://kubernetes.io/blog/2020/08/21/moving-forward-from-beta/) という beta 機能に関するライフサイクル追加です。ここでポイントとなるのは [Avoiding permanent beta](https://kubernetes.io/blog/2020/08/21/moving-forward-from-beta/#avoiding-permanent-beta) という取り決めで、ある機能が beta 版に到達した場合、その機能は「3リリース以内」に以下のどちらかのレベルに到達する必要があります。

- 1.　一般公開 (GA) して、beta 版の API を非推奨化する
- 2.　新たな beta 版 API を公開して、旧 beta 版の API を非推奨化する

もし「3リリース以内」に 1 と 2 どちらのレベルにも到達出来なかった場合は、その次のリリースバージョンから該当の機能は非推奨になり、さらにその3リリース後に機能を削除する必要があります。

これを現在 v1beta1 の PSP に当てはめてみると、kubernetes 1.19・1.20・1.21 の 3リリース以内に上記のレベルに到達できないと、「kubernetes 1.22 から非推奨、1.25 で機能削除」になる計算です。
![Screen Shot 2021-04-23 at 23.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240692/7cb45885-fe02-5bbb-5466-3fcb24ad22f7.png)
ここで「あれ、PSP は kubernetes 1.21 から非推奨になったのでは？」という疑問が生じると思います。

この点については、個人的にはコミュニティ ([SIG Auth](https://github.com/kubernetes/community/tree/master/sig-auth)) の英断だと思うのですが、前述の通り現在の PSP は多数の課題を抱えており、kubernetes 1.22 までに 1 と 2 のレベルに到達することは難しい (そもそも GA に到達する未来を想像できないという声もある) という状況から、`kubernetes 1.21 から非推奨、1.25 で機能削除` のように、1リリース分 早めに非推奨にすることを [アナウンス](https://github.com/kubernetes/kubernetes/pull/97171) しました。
![Screen Shot 2021-04-24 at 15.16.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240692/a6ac75f4-b6fb-34ae-7b0c-511f2c0d5188.png)
[Moving Forward From Beta](https://kubernetes.io/blog/2020/08/21/moving-forward-from-beta/) のライフサイクルに則って「kubernetes 1.25 で機能削除」というマイルストーンはそのまま設定した上で、いち早くユーザに非推奨になることを伝えることで、以下の効果があるためです。

- ユーザが kubernetes 1.21 から PSP を利用し始めてしまわないようにする
- すでに PSP を利用しているユーザが、いち早く PSP の非推奨化について情報をキャッチアップし、今後の動向について注視してもらうようにする

これで、ようやく PSP が非推奨になる理由と、kubernetes 1.21 から非推奨化されるに至った経緯を理解することができました。

<a id="#Chapter4"></a>
## 今後の動向

それでは、PSP が kubernetes 1.21 で非推奨になって 1.25 で削除された後はどうなるのでしょうか。現在、[KEP#2582](https://github.com/kubernetes/enhancements/pull/2582) で [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) に沿った PSP の Replacement について検討されていて、`kubernetes 1.22 での Alpha release` を目標に開発が進められています。

[PodSecurityPolicy Deprecation: Past, Present, and Future](https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/) によると、今のところ PSP の Replacement (Built-in の Admission Controller) としてはシンプルさ・採用しやすさを重点におき、複雑な条件による制限をかけたい場合においては OPA Gatekeeper や Kyverno、K-Rail など他の Admission Controller を推奨するという方針のようです。

<a id="#Chapter5"></a>
## おわりに

以上、Pod Security Policy (PSP) が Kubernetes 1.21 から非推奨になった理由とその経緯の紹介でした。今後の動向についても引き続きチェックしていきますので、PSP の Replacement やその他 Admission Controller については、また別の機会に整理したいと思います。

本投稿に関して、ご指摘やアドバイス・質問等がありましたらぜひコメント欄に記入いただけたらと思います。

<a id="#Chapter6"></a>
## 参考文献

- [PodSecurityPolicy Deprecation: Past, Present, and Future](https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/) 
- [SIG Auth Update and Deep Dive - Mo Khan, Red Hat; Mike Danese, Google; & Tim Allclair, Google](https://www.youtube.com/watch?v=SFtHRmPuhEw)
- [Kubernetes Sig-auth Meeting Notes 2020](https://github.com/kubernetes/community/blob/master/sig-auth/archive/meeting-notes-2020.md)
- [Pod Security Policies Are Being Deprecated in Kubernetes](https://www.antitree.com/2020/11/pod-security-policies-are-being-deprecated-in-kubernetes/)