首页 / Kubernetes (k8s) 入门教程 / Pod 安全:PSS 与 PSA(替代 PSP)

Kubernetes (k8s) 入门教程

Pod 安全:PSS 与 PSA(替代 PSP)

本教程共 65 篇 · 第 46 篇 · 更新于 2026-08-14 · 约 20 分钟阅读

KubernetesPod 安全标准PSSPSA准入控制安全加固

本节目标:学会用命名空间标签给 Pod 上安全约束,搞清 privileged / baseline / restricted 三个级别的差别和 enforce / audit / warn 三种模式的用法。

先把一件事说清楚,免得你在网上翻到老文章走弯路:

Warning

PodSecurityPolicy(PSP)已在 Kubernetes v1.25 彻底移除。 你在 v1.36 集群上写 PSP 的 YAML,API 服务器根本不认识这个类型。网上大量 2021 年前的教程还在教 PSP,直接跳过。替代方案是本章讲的 Pod 安全标准(PSS)+ Pod 安全准入(PSA)。

PSP 为什么被弃了?简单说:它太难用。授权模型走 RBAC 但语义扭曲,多个 PSP 同时匹配时选哪个规则玄学,还会静默修改 Pod 定义。用对了很难,用错了不自知。社区试过修,最后决定重来。

46-1 PSS 和 PSA 是两个东西

新手最容易混淆的就是这两个缩写,先分清:

  • PSS(Pod Security Standards,Pod 安全标准)——一套标准定义。它规定了三个隔离级别各自允许什么、禁止什么。它只是文档和规范,本身不做任何事。
  • PSA(Pod Security Admission,Pod 安全准入)——Kubernetes 内置的准入控制器,负责把 PSS 真正执行起来。v1.25 起 stable。

打个比方:PSS 是国标里写的”三级防火等级各要满足什么条件”,PSA 是门口那个真会拦人的安检员。

PSA 是内置的,不用安装、不用装 Webhook,默认就在。你只要给命名空间打标签告诉它怎么管。

46-2 三个级别:privileged / baseline / restricted

PSS 定义三个策略级别,叠加式的——从宽松到严格:

级别说明
privileged完全不受限。允许已知的特权提升。
baseline限制最弱但禁止已知特权提升。默认的、最简 Pod 配置能直接通过。
restricted强限制,遵循当前 Pod 加固最佳实践。

privileged:等于没管

有意开放、完全无限制。这类策略针对由受信任用户管理的系统级、基础设施级负载。

用了它,Pod 能绕过典型的容器隔离机制——比如直接访问节点的主机网络。

kube-system 命名空间通常就是这个级别,因为 CNI 插件、kube-proxy 这些东西确实需要特权。

baseline:挡住已知提权

面向应用运维人员和非关键应用开发者。禁止的主要项:

  • 主机命名空间——spec.hostNetworkspec.hostPIDspec.hostIPC 必须为 false 或不设。
  • 特权容器——securityContext.privileged 不能为 true。
  • 额外权能(Capabilities)——只能添加一个很小的白名单内的权能,其他都不行。
  • hostPath 卷——spec.volumes[*].hostPath 被禁。
  • 主机端口——通常限制或禁止。
  • AppArmor / SELinux——不能覆盖或禁用默认配置,SELinux 只允许受限的 type,禁止自定义 user 和 role。
  • /proc 挂载类型——必须是默认值。
  • Seccomp——不能显式设为 Unconfined
  • Sysctls——只允许一个”安全”子集,比如 net.ipv4.ip_unprivileged_port_start

关键点:一个啥都没配的普通 Pod 可以直接通过 baseline。它只挡明显危险的操作。

restricted:真正的加固

在 baseline 之上继续收紧:

  • 卷类型白名单——只允许 configMapcsidownwardAPIemptyDirephemeralpersistentVolumeClaimprojectedsecret。别的一律不行。
  • 禁止权限提升——allowPrivilegeEscalation 必须显式设为 false
  • 必须非 root 运行——runAsNonRoot 必须为 true,且 runAsUser 不能是 0。
  • Seccomp 必须配——seccompProfile.type 要设为 RuntimeDefaultLocalhost
  • 权能必须丢弃 ALL——capabilities.drop 必须包含 ALL,之后只允许加回 NET_BIND_SERVICE 这一个(PSS 策略本身仅适用于 Linux Pod)。
Note

注意 restricted 有几项是必须显式声明的,不是”默认值刚好符合就行”。比如 allowPrivilegeEscalation 不写会被拒,得明确写 false。这个设计是有意的——强迫你确认过,而不是碰巧合规。

一个能通过 restricted 的最小 Pod 长这样:

apiVersion: v1
kind: Pod
metadata:
  name: restricted-ok
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: nginxinc/nginx-unprivileged:1.27
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]

注意镜像换成了 nginx-unprivileged。官方 nginx 镜像默认以 root 启动,配 runAsNonRoot: true 会直接起不来。这是从 baseline 升 restricted 时最常见的拦路虎——不是配置写错了,是镜像本身没准备好

Tip

官方文档提到过一个常见疑问:为什么 privileged 和 baseline 之间没有中间级别?因为那个区间的需求太零散,做成通用标准反而没人用得上。真有特殊需求,走豁免(Exemption)或者自己写准入 Webhook。

46-3 三种模式:enforce / audit / warn

级别定义了”什么算违规”,模式定义了”违规了怎么办”:

模式行为
enforce违规直接拒绝创建 Pod
audit违规照样创建,但在审计日志里加一条审计注解
warn违规照样创建,但给用户返回一条警告信息

三种模式可以同时配,而且各自可以用不同级别。这是 PSA 设计里最实用的一点。

46-4 用命名空间标签配起来

标签格式就两个:

# 模式的级别标签
# MODE 是 enforce / audit / warn 之一
# LEVEL 是 privileged / baseline / restricted 之一
pod-security.kubernetes.io/<MODE>: <LEVEL>

# 可选:把策略锁定到某个 Kubernetes 小版本
# VERSION 是合法小版本号或 latest
pod-security.kubernetes.io/<MODE>-version: <VERSION>

实际配置:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # 强制执行 baseline,违规就拒
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.36
    # 同时按 restricted 标准发警告和审计,但不拦
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

用命令打标签更快:

kubectl label namespace production \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/warn=restricted

# 改已有标签加 --overwrite
kubectl label --overwrite namespace production \
  pod-security.kubernetes.io/enforce=restricted

看当前配置:

kubectl get namespace production -o yaml | grep pod-security

推荐的推进路径

上面那个”enforce=baseline + warn=restricted”的组合就是标准渐进式做法:

  1. 先只开 warnaudit,级别设成你的目标(比如 restricted)。什么都不拦,先收集数据。
  2. 看警告和审计日志,统计有多少工作负载违规、违规在哪。
  3. 逐个修复应用,改 securityContext、换非 root 镜像。
  4. 违规清零后,把 enforce 提到目标级别。
Tip

直接上 enforce: restricted 是最常见的翻车方式。整个命名空间的 Deployment 全都建不出 Pod,而且报错信息藏在 ReplicaSet 事件里,第一次遇到能查半天。先 warn 再 enforce,多花两天,少熬一个通宵。

version 标签是干什么的

enforce-version 把策略锁到某个 Kubernetes 小版本。

为什么需要?因为 PSS 标准本身会随版本演进——新版本可能给某个级别加新的限制项。如果你不锁版本(等价于 latest),集群升级后可能突然有 Pod 建不出来了。

生产环境建议显式写版本号。想跟进新标准时再手动改。

46-5 一个坑:Deployment 建得出来,Pod 建不出来

这一点必须单独讲,因为它是 PSA 最反直觉的行为。

Pod 通常不是你直接建的,是通过 DeploymentJob 这类工作负载对象间接创建的——控制器读 Pod 模板,然后造 Pod。

PSA 的处理方式是:

  • auditwarn 会应用到工作负载资源本身。你 kubectl apply 一个违规的 Deployment,会立刻看到警告。
  • enforce 不应用到工作负载资源,只应用到最终生成的 Pod 对象

所以在 enforce 命名空间里 apply 一个违规的 Deployment:

kubectl apply -f bad-deployment.yaml
# deployment.apps/bad-deployment created     ← 成功了!
kubectl get pods
# 没有任何 Pod

Deployment 创建成功,Pod 一个都没有。真正的错误在 ReplicaSet 里:

kubectl describe rs -l app=bad-deployment
# Events:
#   Warning  FailedCreate  ...  Error creating: pods "bad-deployment-xxx" is forbidden:
#   violates PodSecurity "restricted:v1.36": allowPrivilegeEscalation != false ...

排查口诀:Deployment 有了但 Pod 没有,先去 describe rs 看事件。

好消息是 warn 模式会在 apply 工作负载时就提示你,所以同时开 warn 很有价值——它把问题提前到了你敲命令的那一刻。

46-6 豁免:确实需要特权怎么办

有些负载真的需要特权——日志采集要读 hostPath,网络插件要 hostNetwork

最简单的办法是把它们放到独立命名空间,那个命名空间设 privileged 这是首选方案,清晰、可审计。

apiVersion: v1
kind: Namespace
metadata:
  name: infra-agents
  labels:
    pod-security.kubernetes.io/enforce: privileged

另一条路是在准入控制器配置里静态声明豁免。豁免必须显式枚举,满足条件的请求会被 PSA 完全忽略enforceauditwarn 全部跳过)。三个维度:

  • Usernames——来自被豁免用户名(含伪装身份)的请求。
  • RuntimeClassNames——指定了被豁免运行时类的 Pod 和工作负载资源。
  • Namespaces——位于被豁免命名空间中的 Pod 和工作负载资源。
Warning

豁免用户名基本没用,而且危险。因为大多数 Pod 是控制器建的,豁免某个最终用户只在他直接建 Pod 时生效,建 Deployment 时不生效。更要命的是:绝对不要豁免控制器的服务账号,比如 system:serviceaccount:kube-system:replicaset-controller。豁免了它,等于豁免了所有能创建对应工作负载资源的用户——整个 PSA 相当于形同虚设。

另外,有些字段的更新操作天然豁免策略检查。如果一个 Pod 更新请求只改这些字段,即使 Pod 违反当前策略也不会被拒绝。大部分元数据更新属于这一类(seccomp 和 AppArmor 相关的那几个已废弃注解除外)。这是为了避免已存在的 Pod 因为策略变严而没法做无关的日常运维操作。

46-7 从 PSP 迁移过来的对照思路

如果你手上有老集群的 PSP 要迁移,思路是这样的:

官方提供了 PSP 参数到 PSS 的映射表。对每个 PSP 参数,看它的取值落在 baseline 还是 restricted 的允许范围里;超出这两个级别允许范围的,就归到 privileged。表里”无意见(No opinion)“表示所有 PSS 级别都允许该值。

实践上,大多数团队的 PSP 最终能归到这几类:

  • 明显宽松的 PSP → 对应命名空间设 privileged
  • 常规约束的 PSP(禁特权、禁 hostPath)→ 设 baseline
  • 严格加固的 PSP(强制非 root、drop ALL)→ 设 restricted

有些 PSP 能力 PSA 确实覆盖不了。比如 PSP 会修改 Pod(补默认值),PSA 只做校验不做修改。真需要修改行为,得上变更型准入 Webhook(第 57 章)。

46-8 排查违规

看具体哪一项不合规,最直接的办法是用 --dry-run=server 试一下:

# 服务端演练,会走完整的准入控制流程
kubectl apply -f my-pod.yaml --dry-run=server

或者在命名空间上临时开一个更严的 warn,看它警告什么:

kubectl label --overwrite namespace dev \
  pod-security.kubernetes.io/warn=restricted
kubectl apply -f my-pod.yaml
# Warning: would violate PodSecurity "restricted:latest": ...

违规信息读起来是这个格式:

violates PodSecurity "restricted:v1.36":
  allowPrivilegeEscalation != false (container "app" must set
  securityContext.allowPrivilegeEscalation=false),
  unrestricted capabilities (container "app" must set
  securityContext.capabilities.drop=["ALL"]),
  runAsNonRoot != true, seccompProfile

它会把所有违规项一次列全,不是报一个改一个。照着改就行。

46-9 一份实用的起手配置

给全集群命名空间的建议基线:

# 业务命名空间:强制 baseline,警告 restricted
apiVersion: v1
kind: Namespace
metadata:
  name: app-prod
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.36
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted
---
# 基础设施命名空间:放开
apiVersion: v1
kind: Namespace
metadata:
  name: infra-agents
  labels:
    pod-security.kubernetes.io/enforce: privileged
Note

default 命名空间默认没有任何 PSA 标签,等于 privileged。新集群第一件事就该给它打上标签,或者干脆约定不在 default 里跑业务。

小结

  • PSP 已在 v1.25 移除,别学别用。现在是 PSS(标准)+ PSA(内置准入控制器)。
  • 三个级别:privileged(不管)、baseline(挡已知提权,默认 Pod 能过)、restricted(加固最佳实践,需显式声明多个字段)。
  • 三种模式:enforce(拒绝)、audit(记审计)、warn(发警告),可同时配且各用不同级别。
  • 配置方式就是给命名空间打 pod-security.kubernetes.io/<MODE>: <LEVEL> 标签。
  • enforce 不作用于 Deployment,只作用于生成的 Pod——所以要 describe rs 看错误。
  • 实施时按”先 warn/audit,再 enforce”推进;生产环境锁 -version 标签。
  • 需要特权的负载放独立的 privileged 命名空间,别去豁免控制器服务账号。

下一章把安全上下文和网络策略串起来,做一次 Pod 加固要点的汇总。