合规要求写在 Word 文档里、靠人工评审兜底,几乎注定失效:规则说不清、执行靠自觉、审计靠回忆。策略即代码(Policy as Code)把治理规则变成可测试、可版本化、可自动执行的代码。本文从 OPA 与 Rego 的模型讲起,覆盖 CI 门禁、Kubernetes 准入、IaC 校验、策略分发与例外管理,给出一套能真正落地的合规治理方案。
目录
- 1. 策略即代码的理念
- 2. OPA 与 Rego 基础
- 3. Conftest 与 CI 门禁
- 4. Kubernetes 准入策略
- 5. Terraform 与 IaC 策略
- 6. 策略的分发与版本
- 7. 例外与豁免管理
- 8. 策略度量与治理闭环
- 9. 案例与最佳实践
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)。
| 维度 | Gatekeeper | Kyverno |
|---|---|---|
| 策略语言 | Rego | YAML 声明式 |
| 学习曲线 | 陡 | 平缓 |
| 校验与变更 | 支持校验 | 校验 + 变更 + 生成 |
| 复用性 | 与 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 分发与版本化 → 用带有效期的豁免管理例外 → 用拦截率与误报率驱动策略优化。记住三条:新策略先审计后强制、每条策略都要有测试、豁免必须有期限。合规只有变成流水线里的一道门禁,才真正可靠。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。