Skip to main content

Command Palette

Search for a command to run...

Kubernetes 中的 Pod 安全策略

Updated
4 min read

很多人分不清 SecurityContextPodSecurityPolicy 这两个关键字的差别,其实很简单:

  • SecurityContext 是 Pod 中的一个字段,而 PSP 是一个独立的资源类型。

  • SecurityContext 是 Pod 自身对安全上下文的声明;而 PSP 则是强制实施的——不合规矩的 Pod 无法创建。

PSP 的用法和 RBAC 是紧密相关的,换句话说,应用 PSP 的基础要求是:

  • 不同运维人员的操作账号需要互相隔离并进行单独授权。

  • 不同命名空间,不同 ServiceAccount 也同样要纳入管理流程。

PSP 环境下,运维人员或者新应用要接入集群,除了 RBAC 设置之外,还需要声明其工作范围所需的安全策略,并进行绑定,才能完成工作。

PSP 的官方文档中提到,PSP 是通过 Admission Controller 启用的,并且注明了:启用 PSP 是一个有风险的工作,未经合理授权,可能导致 Pod 无法创建。

开始之前,首先设置一个别名,在 default 命名空间新建 ServiceAccount 来模拟一个有权创建 Pod 的用户:

$ kubectl create sa common
serviceaccount/common created

$ kubectl create rolebinding common --clusterrole=edit --serviceaccount=default:common
rolebinding.rbac.authorization.k8s.io/common created

$ alias kube-common='kubectl --as=system:serviceaccount:default:common'

第一个 PSP

我们首先创建一个不允许创建特权 Pod 的策略:

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: noprivileged
spec:
  privileged: false
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

保存为 psp.noprivileged.yaml 并提交给集群。

接下来创建两个 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: noprivileged
spec:
  containers:
  - name: pause
    image: k8s.gcr.io/pause
---
apiVersion: v1
kind: Pod
metadata:
  name: privileged
spec:
  containers:
  - name: pause
    image: k8s.gcr.io/pause
    securityContext:
      privileged: true

用普通用户创建这个 Pod:

$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml
pod/noprivileged created
pod/privileged created
pod "noprivileged" deleted
pod "privileged" deleted

可以看到,在不允许创建特权容器的规则之中,我们的用户还是能够创建特权容器,这是因为还没启用 PSP,接下来在集群设置中启动 PSP,各种环境的启用方式不同,例如在 GKE 环境:

$ gcloud beta container clusters update gcp-k8s --enable-pod-security-policy --zone=asia-east1-a
Updating gcp-vlab-k8s...done.

删除重建 Pod:

$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml
Error from server (Forbidden): error when creating "pod.yaml": pods "noprivileged" is forbidden: unable to validate against any pod security policy: []
Error from server (Forbidden): error when creating "pod.yaml": pods "privileged" is forbidden: unable to validate against any pod security policy: []

可以看到,Pod 的新建请求被拒绝了——然而使用集群管理员身份还是能成功创建的

$ kubectl apply -f pod.yaml && kubectl delete -f pod.yaml
pod/noprivileged created
pod/privileged created
pod "noprivileged" deleted
pod "privileged" deleted

全员 admin 是万恶之源。

用 RBAC 进行授权:

$ kubectl create role psp:noprivileged \
    --verb=use \
    --resource=podsecuritypolicy \
    --resource-name=noprivileged
role.rbac.authorization.k8s.io/psp:noprivileged created

$ kubectl create rolebinding common:psp:noprivileged \
    --role=psp:noprivileged \
    --serviceaccount=default:common
rolebinding.rbac.authorization.k8s.io/common:psp:noprivileged created

再试试普通用户的能力:

$ kube-common apply -f pod.yaml ; kube-common delete -f pod.yaml
pod/noprivileged created
Error from server (Forbidden): error when creating "pod.yaml": pods "privileged" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
pod "noprivileged" deleted
Error from server (NotFound): error when deleting "pod.yaml": pods "privileged" not found

非特权 Pod 才能够成功创建,这符合我们的预期。

副作用

Pod 成功创建了之后,顺理成章,做个 Deployment 看看:

kind: Deployment
metadata:
  name: privileged
spec:
  replicas: 1  
  template:
    metadata:
      labels:
        app: pause
        version: v1
    spec:
      containers:
      - name: sleep
        image: k8s.gcr.io/pause
        securityContext:
          privileged: true
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: noprivileged
spec:
  replicas: 1
  template:
    serviceAccount: common
    metadata:
      labels:
        app: pause
        version: v1
    spec:
      containers:
      - name: sleep
        image: k8s.gcr.io/pause

我们会发现,Deployment 无法正常工作:

$ kubectl get pods
kuNo resources found in default namespace.
$ kubectl get deployment
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
noprivileged   0/1     0            0           15m
privileged     0/1     0            0           15m

查看一下事件:

$ kubectl get events | grep policy
8m38s       Warning   FailedCreate        replicaset/noprivileged-6f94f9c9b8                 Error creating: pods "noprivileged-6f94f9c9b8-" is forbidden: unable to validate against any pod security policy: []
8m38s       Warning   FailedCreate        replicaset/privileged-6d78d5458                    Error creating: pods "privileged-6d78d5458-" is forbidden: unable to validate against any pod security policy: []

这次的 Pod 不是由我们授权的 common 用户创建的,而是由 RS Controller 启动的,因此会失败,加入一个 Service Account:

...
    spec:
      serviceAccount: common
      containers:
...
    spec:
      serviceAccount: common
      containers:
...

提交变更,会发现非特权 Pod 开始创建:

$ kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
noprivileged-6cf595c5bd-rc8cx   1/1     Running   0          4s

系统 Pod 怎么办

这时候我想到个问题,其它 Pod 会不会受到影响?我删除了 kube-system 下面的一个 kube-proxy 的 Pod,发现这个 Pod 自动重建了,没有受到 PSP 的影响,查看一下 RBAC 相关配置,会发现 GCP 在更新集群的过程中已经为系统服务进行了预设:

$ kubectl get rolebinding
...
gce:podsecuritypolicy:kube-proxy                    80m
gce:podsecuritypolicy:metadata-agent                80m
gce:podsecuritypolicy:metadata-proxy                80m
gce:podsecuritypolicy:nodes                         80m
...

追查下去:

$ kubectl get rolebinding gce:podsecuritypolicy:metadata-proxy -o yaml
...
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: gce:podsecuritypolicy:privileged
subjects:
- kind: ServiceAccount
  name: metadata-proxy
  namespace: kube-system

如果追查其中涉及到的 ClusterRole,会发现它指向一个 PSP:

$ kubectl get clusterrole gce:podsecuritypolicy:privileged -o yaml
...
rules:
- apiGroups:
  - policy
  resourceNames:
  - gce.privileged
  resources:
  - podsecuritypolicies
  verbs:
  - use

看看这个 PSP 的内容:

$ kubectl get psp  gce.privileged -o yaml
...
    privileged: true
...

的确包含了特权 Pod 的内容。

最后看看负责创建这个特权 Pod 的 Daemonset:

$  kubectl get daemonset  metadata-proxy-v0.1 -o yaml
...
      serviceAccount: metadata-proxy
      serviceAccountName: metadata-proxy
...

PSP 的限制能力

分为以下几个大方面:

  • 特权容器

  • 主机命名空间:例如 HostPID、HostNetwork 等。

  • 卷和文件系统:例如 PVC、configMap、emptyDir 等卷类型,以及 fsGroup、AllowedHostPaths 等加载能力。

  • 用户和组:运行身份

  • 提权:是否允许

  • Capability 和 sysctl

  • SeLinux、AppArmor 等。

马后炮

kubectl 的 advise-psp 插件,能够根据当前运行的 Pod,提取出所需的 PSP 信息。

参考链接

  • https://kubernetes.io/docs/concepts/policy/pod-security-policy/

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts