在已有 Kubernetes 集群里启用 Pod Security Admission(PSA)时,最常见的失误不是策略选错,而是跳过评估直接进入强制模式。PSA 通过命名空间标签在 Pod 创建或更新时做准入判断,一旦 enforce 生效,不满足要求的 Pod 会被 API Server 直接拒绝,Deployment、StatefulSet、CronJob 等控制器随后持续重试失败。对于运行中的存量工作负载,这意味着一次标签变更就可能同时影响多个业务线。因此更稳妥的路径是“先发现、再收敛、后强制”:先用 audit 和 warn 观察现有配置与目标的差距,再逐命名空间收敛工作负载,最后才把 enforce 打开,并事先准备好最小范围回滚手段。
先理解 PSA 的三个模式和三个层级
PSA 是 Kubernetes 内置的准入控制器,自 v1.25 起 GA,通过命名空间上的 pod-security.kubernetes.io/* 标签生效。它把策略分为三个层级,对应 Pod Security Standards 定义的隔离强度:
- privileged:完全不受限制,允许已知的权限提升,通常只用于集群关键组件或系统级工作负载。
- baseline:最小限制,阻止已知的权限提升方式,同时允许 Pod 规格字段的默认值,适合大多数常规业务。
- restricted:最严格,遵循 Pod 加固最佳实践,在 baseline 基础上进一步要求以非 root 运行、丢弃全部能力、使用 RuntimeDefault 或更严格的 seccomp 等。
每个层级可以分别绑定到三种模式:
| 模式 | 标签 | 行为 | 对发布流程的影响 |
|---|---|---|---|
| enforce | pod-security.kubernetes.io/enforce | 拒绝不合规 Pod | 直接阻断,控制器反复重试失败 |
| audit | pod-security.kubernetes.io/audit | 记录到审计日志,不阻断 | 无影响,用于发现存量问题 |
| warn | pod-security.kubernetes.io/warn | 向请求方返回告警信息 | 无阻断,但用户可见,适合提前通知 |
关键点是三个模式互相独立,可以给同一个命名空间同时设置不同层级的 warn、audit 和 enforce。这正是分阶段落地的基础:先让 warn 和 audit 指向 restricted,把 enforce 留在 baseline 甚至不设置,观察一段时间后再收紧。
阶段一:用 audit 和 warn 摸清现状
不要一上来就改生产命名空间的 enforce。先给目标命名空间加上审计和告警标签,让 PSA 把不合规项暴露出来,而不影响任何发布。
# 先只在 audit 和 warn 层面对齐 restricted,enforce 暂不设置
kubectl label namespace production
pod-security.kubernetes.io/audit=restricted
pod-security.kubernetes.io/audit-version=latest
pod-security.kubernetes.io/warn=restricted
pod-security.kubernetes.io/warn-version=latest
设置后,任何在该命名空间创建或更新的 Pod,如果违反 restricted 要求,都会在 kubectl apply 输出里看到告警,同时审计日志中留下记录。此时可以主动触发一轮重建来采集数据,而不是被动等待下一次发布:
# 查看某个命名空间当前的 PSA 标签
kubectl get namespace production -o jsonpath='{.metadata.labels}' | tr ',' 'n'
# 用 dry-run 快速检查一个清单是否会被 restricted 拒绝
kubectl apply --dry-run=server -f deployment.yaml -n production
--dry-run=server 会走完整的准入链路,因此能真实反映 enforce 生效后的结果,是评估阶段最值得养成的习惯。审计日志的查询方式取决于集群审计策略配置,如果集群未开启审计日志,warn 的输出就是主要观察窗口。

阶段二:按工作负载类型收敛配置
审计阶段收集到的告警会集中在几类典型配置上。理解它们为什么触发 restricted,比机械照抄修复模板更重要。
安全上下文与非 root 运行
restricted 要求 runAsNonRoot: true,并且不允许容器以 root 身份启动。实践中有两种写法:在 Pod 级 securityContext 设置,或在每个容器级设置。PSA 的字段校验允许 Pod 级与容器级互相补位,但要注意“未定义”和“显式设为允许”是两回事。
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.example.com/app:1.4.2
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
runAsNonRoot: true
如果镜像本身以 root 运行且进程需要写文件,单纯加 runAsNonRoot: true 会导致容器启动失败。需要同步调整镜像的 USER、文件属主或挂载卷权限,这属于工作负载改造,而不是标签配置能解决的问题。
特权能力与权限提升
restricted 要求丢弃全部 linux capabilities,并设置 allowPrivilegeEscalation: false。常见的不兼容来源是:为绑定低端口而添加 NET_BIND_SERVICE、为调试而保留 SYS_PTRACE、或直接使用 privileged: true。前两者可以通过改用高位端口或移除调试需求解决;privileged: true 通常意味着该工作负载本质上属于系统组件,应考虑放入单独的 privileged 命名空间,而不是强行改造。
主机命名空间与主机路径挂载
hostNetwork、hostPID、hostIPC 以及 hostPath 卷在 restricted 下都不被允许。日志采集、监控代理、CNI 插件等常依赖这些能力。合理的处理方式是把它们归入 baseline 或 privileged 命名空间,用命名空间边界区分信任级别,而不是放宽整个集群的策略。
例外处理
确实无法满足 restricted 的工作负载,建议用独立的命名空间承载,并为其设置明确的 enforce 层级,而不是在业务命名空间里开洞。命名空间是 PSA 唯一的策略粒度,把例外收敛到少数命名空间,能让策略边界保持清晰、可审计。
阶段三:在测试命名空间先强制
收敛完成后,先在测试或预发命名空间打开 enforce,验证策略不会误伤正常发布。
kubectl label namespace staging
pod-security.kubernetes.io/enforce=restricted
pod-security.kubernetes.io/enforce-version=latest
pod-security.kubernetes.io/warn=restricted
pod-security.kubernetes.io/audit=restricted
enforce-version 建议固定到具体版本(如 v1.30)而非 latest,避免集群升级后策略定义变化导致原本合规的 Pod 突然被拒。如果希望跟随集群默认,可以省略该标签,但要意识到这会引入升级带来的行为漂移。
强制生效后,验证方式包括:
# 确认标签已生效
kubectl get ns staging --show-labels
# 用不合规清单验证是否被拒绝(预期返回 Forbidden)
kubectl run psa-test --image=nginx --privileged -n staging
# 查看被拒绝的具体原因
kubectl describe rs <replicaset-name> -n staging
被拒绝时,API Server 返回的错误信息会明确指出违反了哪条 restricted 要求,例如 “violates PodSecurity restricted:latest: allowPrivilegeEscalation != false”。ReplicaSet 或 Deployment 的事件里也会记录同样的原因,这是定位问题最快的入口。
阶段四:推进到生产并持续观察
测试命名空间稳定运行一段时间、覆盖了典型发布场景后,再对生产命名空间执行同样的标签变更。建议按业务重要性分批,而不是一次性全量。
变更后需要重点观察的信号:
- 控制器事件:
kubectl get events -n <ns> --field-selector reason=FailedCreate能快速发现被拒绝的 Pod。 - 发布失败率:CI/CD 流水线中
kubectl apply的失败数量,是策略误伤最直接的指标。 - 审计日志:即使 enforce 已生效,保留 audit 标签仍能持续记录边缘情况。
- 回滚演练:确认团队知道如何快速撤销标签,而不是等到出事才翻文档。
误拦截时的最小范围回滚
PSA 的回滚成本很低,这是它相比旧版 PodSecurityPolicy 的优势之一。当生产命名空间出现误拦截且影响面在扩大时,优先做最小范围回滚,而不是全集群降级。
# 方案一:把 enforce 降回 baseline,保留 warn/audit 继续观察
kubectl label namespace production
pod-security.kubernetes.io/enforce=baseline --overwrite
# 方案二:完全移除 enforce 标签,回到不受强制约束的状态
kubectl label namespace production
pod-security.kubernetes.io/enforce-
# 方案三:仅对出问题的命名空间临时设为 privileged(谨慎使用)
kubectl label namespace production
pod-security.kubernetes.io/enforce=privileged --overwrite
回滚后,之前被拒绝的控制器会重新创建 Pod,通常无需手动干预。但要注意:已经处于 Running 状态的 Pod 不会因为标签变化而被驱逐,PSA 只在创建和更新时判断。因此回滚解决的是“新 Pod 创建失败”,而不是“存量 Pod 不合规”。
回滚只是止血。事后需要复盘的是:为什么评估阶段没有覆盖到这个工作负载?是审计窗口太短、测试命名空间与生产的配置漂移,还是某个团队绕过了标准模板直接提交清单。把根因补进评估流程,比反复回滚更有价值。
落地检查清单
- [ ] 所有目标命名空间已设置
audit和warn,并观察了足够长的业务周期 - [ ] 已用
--dry-run=server验证过典型工作负载清单 - [ ] 安全上下文、非 root 运行、能力丢弃、seccomp 配置已逐项确认
- [ ] 依赖主机命名空间或 hostPath 的工作负载已归入独立命名空间
- [ ]
enforce-version已固定,避免升级漂移 - [ ] 测试命名空间先于生产启用 enforce
- [ ] 监控覆盖 FailedCreate 事件与发布失败率
- [ ] 回滚命令已写入值班手册并演练过
PSA 的价值在于把 Pod 安全基线变成命名空间级别的、可声明、可回滚的配置,而不是一次性的加固动作。分阶段推进的核心不是拖延,而是让每一步都有可观测的依据和可撤销的退路。

上海市奉贤区 1F
先 audit 再 enforce,这顺序太重要了