实名反对 PodSecurity Admission
Kubernetes 1.22 中加入了一个新的功能叫 PodSecurity admission,据称是一个 PSP 的替代方案,于是我就“抱着试一试的态度”,第一时间体验了一下。
这个新功能的思路很直白,把 Pod/Container SecurityContext 的限制分为了三组,分别命名为 Privileged
、Baseline
以及 Restricted
,顾名思义,这三个级别代表着特权、普通以及严格管理三种对策。用法还是很简单的,只要给要控制的命名空间或者 Pod 打上标签即可。可用的标签列表如下:
pod-security.kubernetes.io/enforce: <policy level>
pod-security.kubernetes.io/enforce-version: <policy version>
pod-security.kubernetes.io/audit: <policy level>
pod-security.kubernetes.io/audit-version: <policy version>
pod-security.kubernetes.io/warn: <policy level>
pod-security.kubernetes.io/warn-version: <policy version>
其中的规定动作包括:
enforce
:仅允许创建符合该策略的 Pod 被创建,不合乎要求的 Pod 会被拒绝;audit
:可以创建违规 Pod,但是会出现在审计日志中;warn
:可以创建违规 Pod,但是会在客户端返回警告信息。
而版本是跟随 Kubernetes 的,例如 1.22
或者 latest
。
需要注意的是,多数情况下 Pod 都是用模板创建的,为了尽早发现问题,audit
和 warn
都是可以针对 Deployment 之类的控制器生效的,而 enforce
仅对 Pod 有效。
举个栗子
首先用 Kind 部署一个测试集群,使用如下的配置文件:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
"PodSecurity": true
nodes:
- role: control-plane
image: kindest/node:v1.22.0
- role: control-plane
image: kindest/node:v1.22.0
- role: control-plane
image: kindest/node:v1.22.0
- role: worker
image: kindest/node:v1.22.0
这里使用 "PodSecurity": true
启用该功能。创建集群并载入镜像:
$ kind create cluster --config 122.yaml
✓ Ensuring node image (kindest/node:v1.22.0) 🖼
✓ Preparing nodes 📦 📦 📦 📦
✓ Configuring the external load balancer ⚖️
✓ Writing configuration 📜
...
$ kind load docker-image dustise/sleep:v0.9.7
Image: "dustise/sleep:v0.9.7" with ID "sha256:cd6cdf0ece4664dcbc10cb98273799a0e4a0e0c2145bf36bb7031915c0ab04df" not yet present on node "kind-control-plane2", loading...
集群生成完毕之后,新建几个命名空间用来测试:
$ kubectl create ns dev
namespace/dev created
$ kubectl create ns stage
namespace/stage created
$ kubectl create ns prod
namespace/prod created
给三个命名空间分别打上标签:
$ kubectl label ns dev pod-security.kubernetes.io/warn=restricted
namespace/dev labeled
$ kubectl label ns stage pod-security.kubernetes.io/audit=restricted
namespace/stage labeled
$ kubectl label ns prod pod-security.kubernetes.io/enforce=restricted
namespace/prod labeled
接下来在每个命名空间创建 Deployment,看看会发生什么:
$ kubectl create deployment sleep --image=dustise/sleep:v0.9.7 -n prod
deployment.apps/sleep created
$ kubectl get pods -n prod
No resources found in prod namespace.
$ kubectl get events -n prod
...
Error creating: allowPrivilegeEscalation != false (container "sleep" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "sleep" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "sleep" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "sleep" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
...
可以看到,Deployment 成功创建,然而却没有 Pod 出现,查看事件会看到其创建过程被拒绝。
再去 Dev 命名空间看一下:
kubectl create deployment sleep --image=dustise/sleep:v0.9.7 -n dev
Warning: would violate "latest" version of "restricted" PodSecurity profile: allowPrivilegeEscalation != false (container "sleep" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "sleep" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "sleep" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "sleep" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
deployment.apps/sleep created
会看到直接返回告警信息,但是 Pod 还是建立起来了。
后记
这个新功能在我看来有些尴尬,每个类别的策略都是隐藏在预配置之中的,要想创建符合其要求的 Pod 可能会费点力气,用 CI 或者 Kyverno 辅助创建可能会更好。