Kubernetes 准入策略
一次发布把没有 owner 标签的 Deployment 带进了集群。告警平台找不到责任团队,回滚审批也无人接手。团队明明已经在 CI 中检查清单,却只检查了仓库里的基础 YAML;Helm 渲染后的对象覆盖了标签,值班人员直接执行的补丁也绕过了流水线。真正缺失的不是又一条 lint 规则,而是 API Server 在持久化对象前必须执行的最后一道一致性判断。
另一套集群把准入 webhook 改成了故障关闭,希望策略服务异常时宁可拒绝也不放行。控制器升级期间 Service 没有可用 Endpoint,连修复控制器本身的 Pod 都无法创建;应急人员随后把整个 webhook 配置删除,业务恢复了,所有约束也一起消失。事故说明“拒绝违规对象”和“策略执行器失联时拒绝请求”是两条不同分支,恢复命名空间、超时、可用副本和停用顺序必须与规则一起设计。
请求真正在哪里被挡住
客户端提交对象后,API Server 先完成认证与授权,再进入 admission chain。变更型准入先产生补丁,验证型准入看到接近最终形态的对象并决定接受或拒绝,成功对象才会写入 etcd。RBAC 回答“这个主体能否调用某个 API 动作”,准入策略回答“这次被允许的写请求所携带的对象是否满足组织约束”。两者缺一不可:通过认证和 RBAC 不代表对象安全,准入校验通过也不会给调用者新增 API 权限。
准入只观察创建、更新、删除、连接等 API 请求,不会因为一条新规则出现就自动回滚已经存在的对象。Kubernetes 原生 VAP 的 Audit 也只是给当前请求的 Kubernetes audit event 增加 annotation,并非周期扫描。Gatekeeper audit 与 Kyverno background report 才是存量发现路径;Kyverno 的 mutate-existing、generate 和 scheduled delete 又是会主动改变资源的协调路径。把这些能力统称为“审计”会直接混淆事故责任。
三条采用路径
目标集群至少为 Kubernetes 1.30、需求只是确定性的对象验证、规则可由 CEL 与参数资源表达时,优先考虑原生 ValidatingAdmissionPolicy。它使用稳定的 admissionregistration.k8s.io/v1 API,在 kube-apiserver 进程内求值,不需要安装 Deployment、Service、证书或外部 webhook。代价是计算消耗进入控制平面,而且它不能 mutation、读取任意 inventory、调用 HTTP 服务或扫描存量对象。
已有 Rego 与 ConstraintTemplate 资产,或者需要同一约束同时服务 admission、周期 audit 和本地 gator 测试时,Gatekeeper 更合适。它能扩展 referential data、mutation 与 External Data,但会引入 webhook 网络、证书、缓存、控制器和 CRD 的运行责任。采用 Gatekeeper 生成原生 VAP 时还要把生成对象视为 beta 管理面,明确 Gatekeeper 与 GitOps 谁拥有字段,不能让两个控制器持续争用同一个 VAP。
需要验证、变更、生成、删除、镜像策略、PolicyReport、PolicyException 与 Kubernetes 风格策略对象的一体化平台时,可以选择 Kyverno。新项目应从 policies.kyverno.io/v1 的离散 CEL API 评估,不再把旧 Policy/ClusterPolicy 当作永远不变的主线。Kyverno 的 admission、background、reports、cleanup 控制器有不同权限与伸缩语义;多副本首先提升故障接管能力,并不保证所有后台队列线性提速。
先确认集群提供什么
原生 VAP 没有独立安装包。对自建或托管集群,先在目标 context 上确认稳定 API,而不是根据本机 kubectl 版本推断服务端能力:
kubectl config current-context
kubectl version
kubectl api-resources --api-group=admissionregistration.k8s.io \
| grep -E 'validatingadmissionpolic(y|ies)|validatingadmissionpolicybinding'
kubectl explain validatingadmissionpolicy.spec
kubectl explain validatingadmissionpolicybinding.spec.validationActions预期能看到 ValidatingAdmissionPolicy 与 ValidatingAdmissionPolicyBinding,版本为 admissionregistration.k8s.io/v1。只有 Policy 没有 Binding 时规则不会执行;使用参数时还要安装对应原生资源或 CRD,并给 Binding 创建者读取参数对象的权限。CEL 库随 Kubernetes minor 演进,使用 IP/CIDR、format、双变量推导或 semver 等能力前,应按表达式实际使用的函数核对所有 API Server 节点的最低版本,不能只看 API 已是 v1。
安装 Gatekeeper 或 Kyverno 时,则需要把 Chart 版本、应用版本、Kubernetes 兼容矩阵、CRD、webhook、证书、PDB、拓扑分布与恢复命名空间一起纳入变更。Helm 的 kubeVersion 允许安装,只能说明模板的最低约束满足,不能替代产品发布线声明的测试矩阵与团队自己的故障验收。
同一规则要留四种证据
把“Deployment 必须有 owner 标签”接入项目时,仓库应同时保存合法 fixture、缺标签 fixture、策略对象与执行面配置。CI 对渲染结果做静态测试,测试集群先以 Warn/Audit 或 dry-run 运行,生产再按命名空间分批改成 Deny。一次 kubectl apply 成功只证明策略对象被 API Server 接受,不能证明业务对象命中规则。
有效证据至少包括:被提交资源的 GVK、namespace、name 与 UID;策略和 Binding 的 generation 或发布版本;客户端 warning 或拒绝 Status;Kubernetes audit ID;执行器日志与延迟指标。存量扫描还要保存扫描开始/结束、扫描对象集合、截断上限与报告状态。没有共同 policy ID 和资源身份时,准入拒绝、audit annotation、Gatekeeper violation 与 Kyverno PolicyReport 无法可靠关联。
反向实验必须分别制造正常违规和执行故障。正常违规是 CEL 返回 false、Rego 产生 violation,应该由 validationActions 或 enforcement action 决定;执行故障是表达式错误、参数 GVK 错误、webhook 超时、证书失败或没有 Endpoint,应该由 failurePolicy 决定。只测缺标签而不测执行器失联,无法证明 fail-open/fail-closed 的真实行为。
性能账单落在 API Server 前
原生 CEL 省去了 webhook 网络跳转,却没有消除成本。过宽的 matchConstraints 会让大量请求进入求值,重复遍历大型列表会消耗 API Server CPU;Kubernetes 的静态估算与运行时 cost budget 会拒绝或中止过于昂贵的表达式。应先缩小 GVK、操作和 namespace,再复用 variables,最后才优化表达式细节。
Webhook 的成本还包括 API Server 到 Service 的连接、TLS、序列化、控制器排队与外部依赖。超时不是免费缓冲:多个串行 webhook 会叠加写请求尾延迟,调用外部 registry 或 HTTP provider 会把第三方故障带进控制平面关键路径。容量验收要观察 admission latency 分位、拒绝率、调用错误、超时、Endpoint 可用数、控制器 CPU/内存和恢复时积压,而不是只看平均响应时间。
从观察切到阻断
上线顺序应保持一个随时可撤回的执行开关。先部署策略与参数,使用 Warn/Audit、dry-run 或报告模式收集真实命中;修复存量与误报后,只对测试 namespace 启用 Deny;再按租户或工作负载批次扩大。每一批都比较请求量、策略命中、错误、尾延迟和业务发布失败,达到停止条件就把 Binding 或 enforcement action 回到观察模式。
回滚时先解除执行,再删除定义。原生 VAP 先删除或收窄 Binding,确认请求恢复,再保留 Policy 与参数用于诊断;webhook 平台先把规则退到 warn/dry-run,必要时将 failure policy 临时改为经过审批的恢复状态,确认 API Server 不再发送危险调用后才卸载控制器、webhook 与 CRD。直接删除 CRD 可能连策略、报告和 finalizer 证据一起清掉。
从 webhook 迁移到原生 VAP 时,用同一组渲染 fixture 对比结果,并在集群中让新 VAP 先运行 Warn/Audit,旧执行器继续承担唯一 Deny。对齐 scope、参数、消息、缺失数据和错误语义后,才把 VAP 切为 Deny,再把旧规则降为观察,最后停止旧 webhook。任何阶段都只能有一个硬拒绝责任方,否则双重 warning、不同错误消息和重复延迟会让故障证据失真。
原生准入守不住什么
准入策略不能替代 RBAC、Pod Security、镜像签名与来源验证,也不会观察容器启动后的系统调用、网络连接或文件行为。它无法证明运行中的镜像仍安全,无法发现进程提权后的动作,也不负责备份、恢复和业务授权。规则需要任意外部数据、跨对象 inventory、主动修复或运行时检测时,应选择相应控制器或安全工具,同时保持每条控制只有一个明确 owner 和执行点。
团队长期维护的重点不是策略数量,而是每条规则是否有 fixture、参数 owner、发布版本、观察期、拒绝责任、故障策略、性能预算、恢复路径与退出条件。规则没有 Binding 等于未执行;报告为空不等于合规;控制器 Ready 不等于 webhook 有 Endpoint;请求成功也不等于所有策略都求值成功。把这些中间状态保留下来,准入控制才会从“集群里有个策略工具”变成可验证、可回滚的工程能力。
