Kubernetes Pod Security Admission 分阶段落地:从审计评估到 Restricted 强制与回滚

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
303
文章
0
粉丝
云原生安全1 67字数 2430阅读8分6秒阅读模式
AI智能摘要
在已有 Kubernetes 集群启用 PSA,最危险的做法不是选错层级,而是跳过评估直接打开 enforce;一次标签变更就可能让多个业务线的 Pod 被 API Server 拒绝,控制器随后反复重试。文章给出“先发现、再收敛、后强制”的路径:用 audit 和 warn 对齐 restricted 以暴露存量差距,再按工作负载类型收敛非 root 运行、能力丢弃和主机命名空间依赖,最后在测试命名空间强制并准备回滚。你的命名空间边界够清晰吗?
— AI 生成的文章内容摘要

在已有 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 等。

每个层级可以分别绑定到三种模式:

模式标签行为对发布流程的影响
enforcepod-security.kubernetes.io/enforce拒绝不合规 Pod直接阻断,控制器反复重试失败
auditpod-security.kubernetes.io/audit记录到审计日志,不阻断无影响,用于发现存量问题
warnpod-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 的输出就是主要观察窗口。

管理员在终端中查看命名空间标签与 Pod 安全审计告警

阶段二:按工作负载类型收敛配置

审计阶段收集到的告警会集中在几类典型配置上。理解它们为什么触发 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 安全基线变成命名空间级别的、可声明、可回滚的配置,而不是一次性的加固动作。分阶段推进的核心不是拖延,而是让每一步都有可观测的依据和可撤销的退路。

 
枫少@KillBoy
    • 河马小胖
      河马小胖 1

      先 audit 再 enforce,这顺序太重要了

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证