实名反对 PodSecurity Admission

Kubernetes 1.22 中加入了一个新的功能叫 PodSecurity admission,据称是一个 PSP 的替代方案,于是我就“抱着试一试的态度”,第一时间体验了一下。

这个新功能的思路很直白,把 Pod/Container SecurityContext 的限制分为了三组,分别命名为 PrivilegedBaseline 以及 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 都是用模板创建的,为了尽早发现问题,auditwarn 都是可以针对 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 辅助创建可能会更好。

Avatar
崔秀龙

简单,是大师的责任;我们凡夫俗子,能做到清楚就很不容易了。

comments powered by Disqus
下一页
上一页