策略即代码:OPA、Conftest 与合规门禁

把合规与治理规则写成可测试、可版本化的代码:OPA 与 Rego 的核心模型、Conftest 在 CI 中做配置门禁、Kubernetes 准入策略、Terraform 计划阶段的 IaC 校验、策略的分发与版本管理、带有效期的例外豁免,以及用指标驱动的治理闭环。

合规要求写在 Word 文档里、靠人工评审兜底,几乎注定失效:规则说不清、执行靠自觉、审计靠回忆。策略即代码(Policy as Code)把治理规则变成可测试、可版本化、可自动执行的代码。本文从 OPA 与 Rego 的模型讲起,覆盖 CI 门禁、Kubernetes 准入、IaC 校验、策略分发与例外管理,给出一套能真正落地的合规治理方案。


目录


1. 策略即代码的理念

1.1 从文档到代码

传统的合规是「写一份规范 → 培训 → 人工评审」。策略即代码把规范转成机器可执行的规则,让「不合规」在流水线阶段就被拦截,而不是等到审计才发现。

维度文档式合规策略即代码
表达自然语言,有歧义代码,精确无歧义
执行人工评审,易漏自动执行,必检
时机事后审计事前拦截
版本文件替换,无历史版本控制,可追溯
测试无法测试单元测试覆盖

1.2 三种执行点

策略可以在三个位置执行:提交时(CI 静态校验配置与代码)、部署时(准入控制器拦截)、运行时(持续扫描已运行资源)。越靠前拦截,修复成本越低。

1.3 策略要能被测试

策略本身也是代码,也会有 bug。每条策略都应有正例(应通过)与反例(应拒绝)的测试用例,改策略时先跑测试,避免一条改动放行了本不该放行的资源。

2. OPA 与 Rego 基础

2.1 OPA 是什么

OPA(Open Policy Agent)是通用的策略引擎:它接收输入(JSON 数据),根据策略(Rego 语言)计算出决策(允许或拒绝)。它是通用的,不绑定任何具体系统。

OPA 决策模型
  input  : 待评估的数据(如 K8s 资源、Terraform plan)
  data   : 外部上下文(如允许的镜像仓库列表)
  policy : Rego 规则
  output : 决策结果(allow / deny + 原因)

2.2 Rego 语言要点

Rego 是声明式语言,核心是「规则求值」:规则为真则规则体成立。默认拒绝(default allow = false)是推荐写法,让策略在未覆盖的情况下倾向于安全。

package main

default allow = false

# 拒绝使用 latest 标签的镜像
deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("容器 %v 不得使用 latest 标签", [container.name])
}

allow {
  count(deny) == 0
}

2.3 常见写法陷阱

  • 忘记 default:未定义时 allow 为 undefined 而非 false,行为不直观。
  • 用 = 做比较:Rego 中比较应用 ==,= 是赋值与统一。
  • 迭代越界:[_] 在空数组上不产生结果,需注意语义。

3. Conftest 与 CI 门禁

3.1 Conftest 做什么

Conftest 是把 OPA 搬进 CI 的工具:它对 YAML、JSON、HCL、Dockerfile 等配置文件运行 Rego 策略,在提交阶段就拦截不合规配置,无需集群。

# 对 Kubernetes 清单跑策略
conftest test deployment.yaml --policy ./policy

# 对 Terraform 计划跑策略
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
conftest test plan.json --policy ./policy

# 对 Dockerfile 跑策略
conftest test Dockerfile --policy ./policy

3.2 在流水线里做门禁

把 conftest 作为流水线的一个必过步骤:策略检查失败则流水线失败,配置无法进入下一步。门禁的关键是「无例外通道」,否则形同虚设。

3.3 策略的单元测试

Conftest 支持用 --update 生成期望结果快照,也支持编写断言测试,保证策略在迭代中不引入回归。

4. Kubernetes 准入策略

4.1 准入控制的两个实现

在 Kubernetes 里做策略执行有两个主流方案:Gatekeeper(OPA 的 K8s 原生实现)与 Kyverno(K8s 原生策略语言,无需学 Rego)。

维度GatekeeperKyverno
策略语言RegoYAML 声明式
学习曲线陡平缓
校验与变更支持校验校验 + 变更 + 生成
复用性与 OPA 生态一致仅 K8s
适合团队已用 OPA 的团队纯 K8s 团队

4.2 校验与变更

准入策略分两类:校验(validate)拒绝不合规资源,变更(mutate)自动补全或修正资源。变更要谨慎使用,隐式修改可能掩盖问题。

4.3 从审计到强制

Gatekeeper 的约束有 dryrun 模式:先以审计模式跑,观察会拦截哪些现有资源,评估影响后再切到强制模式,避免上线即拦截大量存量资源。

# 约束示意:禁止使用 latest 标签
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowedTags
metadata:
  name: no-latest-tag
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    tags: ["latest"]

5. Terraform 与 IaC 策略

5.1 计划阶段校验

Terraform 的策略校验最好发生在计划(plan)阶段:terraform plan 产出的 JSON 描述「将要创建/修改什么」,对这份 JSON 跑策略,可以在资源真正创建前拦截。

5.2 典型 IaC 策略

  • 禁止创建公网可访问的存储桶。
  • 强制所有资源打上 owner 与 cost-center 标签。
  • 禁止使用过大的实例规格。
  • 禁止明文密钥出现在资源属性中。
策略类型校验对象拦截效果
标签合规plan 中所有资源强制成本归属
网络暴露安全组与存储桶防止数据泄露
规格限制实例类型控制成本
密钥检测属性值防止密钥入库

5.3 与 Terraform 原生校验配合

Terraform 1.5+ 提供原生 check 块与变量校验,能覆盖一部分简单规则;复杂跨资源规则仍推荐用 OPA,二者配合使用。

6. 策略的分发与版本

6.1 策略也是制品

策略应像应用代码一样管理:放进仓库、走评审、打版本、发布。OPA 支持把策略打包成 bundle(含策略与数据的压缩包),通过 HTTP 或 OCI 分发。

策略分发模型
  策略仓库 → CI 打包 bundle → 推送到 OCI registry
  OPA/Gatekeeper 定期拉取 bundle 并热更新
  版本回滚 = 切回上一个 bundle 版本

6.2 版本与灰度

策略更新也需要灰度:先在审计模式观察,再在小范围集群强制,最后全量。直接全量推一条新策略,可能一次性拦截大量合法资源。

6.3 数据与策略分离

把「允许的镜像仓库列表」「豁免名单」这类数据与策略逻辑分离,让运维改数据不必改策略代码,降低变更风险。

7. 例外与豁免管理

7.1 例外不可避免

总会有合理的例外:遗留系统、临时排障、监管特批。问题不在于有没有例外,而在于例外是否受控、是否有期限。

7.2 带有效期的豁免

豁免必须有明确的有效期与理由,到期自动失效并提醒续期。永久豁免等于策略失效。

# 豁免示意:带到期时间与审批人
exemptions:
  - resource: legacy-payment-service
    policy: no-latest-tag
    reason: "遗留系统升级中,工单 OPS-1234"
    approved_by: "security-team"
    expires_at: "2026-12-31"

7.3 豁免要可审计

每次豁免的申请、批准、到期都应留痕。定期回顾豁免清单,能发现「哪些策略与实际需求脱节」,反过来优化策略。

8. 策略度量与治理闭环

8.1 三个治理指标

  • 拦截率:策略拦截了多少次违规,反映规则是否有效。
  • 误报率:拦截中有多少是误判,反映规则是否过严。
  • 豁免数:当前生效的豁免数量,反映策略与现实的差距。

8.2 从指标到策略优化

如果某条策略误报率高,说明规则需要细化;如果某类违规反复出现,说明应该做「变更」而非「校验」(自动补全而非拒绝);如果豁免数持续增长,说明策略脱离实际。

8.3 治理是持续过程

策略不是一次写完就不变。随着架构演进与合规要求变化,策略要持续迭代。把策略迭代纳入常规工程节奏,治理才不会退化成摆设。

9. 案例与最佳实践

9.1 落地 Checklist

□ 每条策略配正例与反例测试,改前先跑测试
□ 新策略先以审计/dryrun 模式观察,再切强制
□ CI 阶段用 Conftest 拦截不合规配置
□ K8s 用 Gatekeeper 或 Kyverno 做准入校验
□ Terraform 在 plan 阶段用 OPA 校验
□ 策略打包成 bundle 走 OCI 分发,支持版本回滚
□ 策略逻辑与数据分离,改数据不改代码
□ 豁免必须带理由、审批人与到期时间
□ 用拦截率、误报率、豁免数驱动策略优化

9.2 常见坑与对策

坑现象对策
策略一上线就强制大量存量资源被拦先 dryrun 后强制
无策略测试改一条放行了不该放的正反例测试覆盖
永久豁免策略对部分资源失效豁免带到期时间
策略与数据混在一起改名单要改代码逻辑与数据分离
误报率高团队绕过策略细化规则或改校验为变更
门禁有例外通道形同虚设门禁无旁路
策略无版本出问题无法回滚bundle 版本化

小结

策略即代码把合规从「文档 + 人工评审」变成「代码 + 自动执行」。落地路径是:用 OPA 与 Rego 表达规则 → 在 CI 用 Conftest 拦截配置 → 在 K8s 用准入策略拦截资源 → 在 Terraform plan 阶段拦截 IaC → 把策略打包成 bundle 分发与版本化 → 用带有效期的豁免管理例外 → 用拦截率与误报率驱动策略优化。记住三条:新策略先审计后强制、每条策略都要有测试、豁免必须有期限。合规只有变成流水线里的一道门禁,才真正可靠。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「DevOps」更多文章

  1. 可观测性驱动的发布验证与自动回滚
  2. 制品管理与供应链溯源:从仓库到 Provenance
  3. 配置管理:Ansible 与不可变基础设施的取舍