Kubernetes策略即代码与准入治理实战
Kubernetes 的 RBAC 主要回答“谁可以调用什么 API”,却不能独立回答“提交的对象是否符合组织规则”。例如,开发者有权创建 Deployment,并不代表可以使用特权容器、无资源限制、来自未知仓库的镜像或永久有效的例外。策略即代码的目标,是把这些规则写成可审阅、可测试、可分阶段启用、可观测并能快速回退的版本化配置。
本文面向需要建立集群准入基线的平台、安全和运维团队。示例优先使用 Kubernetes 原生 ValidatingAdmissionPolicy 与 CEL,同时说明何时引入 Kyverno 等策略引擎。不同 Kubernetes 与策略引擎版本支持的字段并不相同,上线前应以当前集群和官方兼容矩阵为准。
一、准入控制在请求链路中的位置
一个写请求通常依次经过身份认证、授权和准入控制,最后才写入 etcd:
- 认证确认调用者身份;
- 授权判断该身份能否对资源执行动作;
- 变更型准入可补默认值或修改对象;
- 验证型准入决定对象是否允许写入;
- API Server 完成持久化。
策略不能替代镜像扫描、运行时检测、网络策略或主机加固。准入控制只看当前请求及允许访问的数据;已存在对象不会因为新策略上线而自动整改,节点上绕过 API Server 的行为也不在其保护范围内。
二、先做规则分层,而不是先选工具
| 层级 | 典型规则 | 建议实现 |
|---|---|---|
| Kubernetes内建保护 | Pod安全、资源配额、LimitRange | 内建准入控制器和原生资源 |
| 简单对象校验 | 标签、镜像仓库、字段组合 | ValidatingAdmissionPolicy与CEL |
| 复杂匹配和生成 | 例外、上下文查询、生成资源、镜像验证 | 成熟策略引擎 |
| 供应链校验 | 签名、来源证明、SBOM门禁 | 镜像验证策略与制品平台 |
| 运行时治理 | 进程、文件、网络和异常行为 | 运行时安全与可观测平台 |
原则是能用原生能力清楚表达的规则,不必先增加外部 webhook;确实需要生成资源、复杂上下文或供应链验证时,再引入策略引擎。每条规则都应有负责人、风险等级、作用范围、例外条件、测试用例和回退方式。
三、建立准入策略清单
不要一次写几十条拒绝规则。先从实际事故和审计要求整理清单:
- 禁止特权容器、宿主机 PID/IPC/Network 和高风险 hostPath;
- 默认禁止以 root 运行,要求只读根文件系统和必要的 seccomp;
- 禁止
latest、裸标签和未批准的镜像仓库; - 工作负载必须声明 CPU、内存 requests,关键服务同时声明 limits;
- 强制负责人、系统、环境、数据等级等标签;
- 禁止 NodePort、LoadBalancer 或跨命名空间引用被随意创建;
- 限制可使用的 StorageClass、Ingress/Gateway 域名和 TLS Secret;
- 生产命名空间禁止调试镜像、临时凭据和明文敏感配置;
- 例外必须有工单号、负责人、原因和到期时间。
把规则分为“必须阻断”“先告警后整改”“仅盘点”三类。对低风险环境和系统命名空间单独定义边界,避免把平台组件误拦截。
四、用Pod Security建立第一道基线
Pod Security Admission 提供 privileged、baseline 和 restricted 三档标准。生产命名空间可以先启用审计和警告,再逐步转为强制:
kubectl label namespace payments \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/enforce=baseline \
--overwrite
观察违规一段时间后,再将 enforce 提升到 restricted。建议显式固定策略版本,避免集群升级时规则语义在未评估的情况下变化。系统组件、CNI、存储插件等可能确实需要额外权限,应放在受控命名空间并记录例外,而不是全局关闭基线。
五、使用ValidatingAdmissionPolicy表达原生规则
ValidatingAdmissionPolicy 使用 CEL 表达验证逻辑,不依赖外部网络 webhook。下面禁止生产 Deployment 使用 latest 或无标签镜像:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: require-pinned-image
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: >-
object.spec.template.spec.containers.all(c,
c.image.contains(':') && !c.image.endsWith(':latest'))
message: "生产工作负载必须使用明确且非 latest 的镜像标签"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: require-pinned-image-prod
spec:
policyName: require-pinned-image
validationActions: [Deny, Audit]
matchResources:
namespaceSelector:
matchLabels:
policy-tier: production
创建规则前先确认 CRD、子资源、控制器生成对象和 UPDATE 行为。CEL 表达式要覆盖字段缺失、空数组、initContainers 和 ephemeralContainers 等情况;复杂逻辑应拆成多条 validation,让拒绝原因可读。
六、资源与标签规则要考虑真实工作负载
“所有容器都必须有 limits”看似严格,但 CPU limit 可能引起节流;“所有对象都有 owner 标签”也可能误伤第三方控制器。可采用以下分层:
- 所有容器必须有 CPU、内存 requests;
- 内存 limit 为必填,CPU limit 根据工作负载类别决定;
- 平台命名空间使用受控例外,不与业务命名空间混用;
- 控制器生成的 Pod 应在上层 Deployment、Job 或自定义资源校验;
- 负责人标签值必须来自组织目录,而不只是“字段存在”。
同时配置 ResourceQuota 和 LimitRange,防止策略只校验格式却没有命名空间总量约束。
七、何时使用Kyverno等策略引擎
当需要生成默认资源、校验镜像签名、访问其他对象上下文、自动变更或输出策略报告时,策略引擎更合适。示例规则只允许批准仓库:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
validationFailureAction: Audit
background: true
rules:
- name: approved-registries
match:
any:
- resources:
kinds: [Pod]
validate:
message: "镜像必须来自已批准的企业仓库"
pattern:
spec:
containers:
- image: "registry.example.com/*"
上线前核对策略引擎版本的字段语义。变更型规则可能让 Git 中的声明与集群实际对象不同,应明确哪些默认值由 API 默认、策略引擎或部署模板负责,避免多套系统同时修改同一字段。
八、采用“盘点—警告—阻断”三阶段上线
- 盘点:后台扫描存量资源,仅生成报告;
- 警告:新请求仍可通过,但返回警告并通知负责人;
- 阻断:只对已达到合规率的命名空间启用 Deny;
- 收口:清理临时例外并将策略纳入新集群基线。
每个阶段都应有通过率、违规对象数、误拦截数、例外数量和最长整改时间。阻断前至少回放真实清单,并覆盖部署、扩缩容、滚动更新、Job、备份恢复和故障处置流程。
九、例外不是白名单字符串
生产例外至少包含:策略编号、资源范围、业务负责人、批准人、原因、补偿措施、创建时间和到期时间。优先使用独立命名空间、精确对象选择器或专用 ServiceAccount;不要用宽泛正则排除整个团队。
可将例外放在独立仓库,通过 CI 检查到期时间。到期前提醒,到期后自动转为警告或拒绝。紧急处置的 break-glass 账号必须有审计、短时有效和事后复核,不能成为日常绕过通道。
十、在CI中测试策略
策略和应用变更都要在进入集群前测试:
kubectl apply --dry-run=server -f manifests/
kubectl diff -f manifests/
建议维护四组测试:
- 应允许的最小合规对象;
- 每条规则对应的应拒绝对象;
- 缺失字段、空值、多容器和更新操作等边界案例;
- 平台组件、控制器生成对象和紧急恢复清单。
测试必须固定 Kubernetes 和策略引擎版本。仅做 YAML 语法检查不足以发现准入链、CRD 默认值和版本转换后的真实行为。
十一、Webhook可用性与故障边界
外部 admission webhook 位于控制面写请求关键路径。应控制超时、并发、证书轮换和副本反亲和,避免规则引擎故障演变为全站无法发布。failurePolicy: Fail 更安全,但会在 webhook 不可用时拒绝请求;Ignore 提高可用性,却可能放行违规对象。二者必须按策略风险选择,而不是全局统一。
重点观察:
- 准入请求延迟的 P50、P95、P99;
- 超时、拒绝、错误和 fail-open 次数;
- API Server webhook 指标与审计日志;
- 策略引擎副本、队列、缓存和证书剩余时间;
- 各策略命中率及误拦截工单。
避免 webhook 调用自身依赖的受控服务形成循环依赖。不要把远程镜像扫描、工单查询等慢操作直接串在请求链路中,可用预计算结果或缓存降低风险。
十二、变更、回滚与紧急处置
策略变更应像应用发布一样分批:测试集群、非关键命名空间、少量生产、全量。回滚顺序通常是先将 Deny 降为 Warn/Audit,再修正规则,而不是立即删除所有策略组件。
当大量发布突然被拒绝时:
- 确认是授权、配额还是准入拒绝;
- 从 API 响应和审计日志定位具体策略;
- 检查最近策略、Kubernetes、CRD或控制器升级;
- 将单条故障策略降级,保留其他保护;
- 恢复关键发布并记录被放行对象;
- 修复后回放清单、恢复阻断并补做审计。
不要直接停掉整个 webhook 部署后继续操作;如果配置为 Fail,停服务可能让请求全部失败。
十三、生产验收清单
- [ ] 每条策略都有风险说明、负责人、测试与回滚方法;
- [ ] 规则先审计、后警告、再按命名空间阻断;
- [ ] 系统命名空间和控制器行为经过真实回放;
- [ ] 例外范围最小且有到期时间;
- [ ] webhook有多副本、证书监控、超时和容量保护;
- [ ] 策略延迟、错误、拒绝和fail-open行为可观测;
- [ ] GitOps、扩缩容、备份恢复和事故操作均经过验证;
- [ ] 集群升级前重新执行兼容性测试;
- [ ] 紧急降级不会同时移除全部保护;
- [ ] 存量违规有整改计划,而非永久忽略。
十四、官方参考
- Kubernetes Admission Controllers
- Validating Admission Policy
- Admission Webhook Good Practices
- Pod Security Standards
- Kyverno Policy Types
策略治理的成熟标志不是“拒绝规则很多”,而是规则与风险对应、例外可追踪、变更可验证、故障可降级。把准入策略当作控制面产品持续运营,才能在不阻塞交付的前提下提高默认安全性。