引言
在微服务与 Kubernetes 规模化的今天,”谁能访问什么”这类策略决策往往散落在各服务的 if/else 里,改一条规则就要发版。Open Policy Agent(OPA)的出现就是为了解决这个痛点——它把策略决策从应用代码中彻底解耦,用一门专为策略设计的声明式语言 Rego 来表达”允许还是拒绝”。这种”策略即代码(Policy as Code)”的思路,让你可以用统一的方式治理 K8s 准入、API 授权与微服务鉴权。当团队规模扩大,硬编码的授权逻辑会迅速变成无人敢动的”技术债”:改一处要全链路回归,漏一处就是安全事件。本文从本地 CLI 上手,到 Kubernetes Gatekeeper,再到 Envoy 授权,带你把 OPA 真正用起来。
一、OPA 是什么:把决策从代码中解耦
OPA 是一个轻量、通用的开源策略引擎,也是 CNCF 毕业项目。它不关心你的业务逻辑,只负责回答一个问题:给定一组输入(input)和一组策略(policy),最终结果是 allow 还是 deny。策略用 Rego 编写,Rego 是一种专为查询和决策优化的语言,读起来像数据库查询。它通常被部署为 sidecar 或独立服务,业务系统把”请求上下文”作为 input 发过去,OPA 返回决策结果,整个过程毫秒级,且不依赖外部数据库——策略本身既可以是本地文件,也可以从 bundle 服务按需拉取并热更新。
- 策略与代码解耦:业务服务只发”请问这个请求允许吗”的查询,决策逻辑全在策略文件里。
- 统一语言:无论 K8s、API 网关还是微服务,都用同一套 Rego 表达策略。
- 可版本化、可测试:策略是纯文本,进 Git、走 CI,和代码一样评审。
二、快速上手:用 opa CLI 评估策略
先用单机 CLI 体会 Rego 的工作方式,这也是日常调试策略最快的路径。
2.1 安装 OPA
# macOS
brew install opa
# 其他平台直接下载静态二进制
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static
chmod +x opa
2.2 写一个最小 Rego 策略
假设我们要控制”只有带 admin 角色的用户才能删除资源”,把下面内容保存为 policy.rego:
package authz
default allow = false
allow {
input.method == "DELETE"
input.user.role == "admin"
}
allow {
input.method != "DELETE"
}
default allow = false 是”默认拒绝”的安全基线;下面的规则在满足条件时把 allow 置真。Rego 是声明式的,多条 allow 规则之间是”或”的关系——任意一条满足即为允许。
2.3 用 CLI 评估
# 把请求上下文放进 input.json
echo '{"method":"DELETE","user":{"role":"admin"}}' > input.json
# 评估策略
opa eval --data policy.rego --input input.json "data.authz.allow"
# 输出 data.authz.allow = true
把 role 改成 “guest” 再跑一次,结果会变成 false——策略生效,且无需改动任何业务代码。这就是”决策解耦”最直接的价值。
三、在 Kubernetes 中落地:OPA Gatekeeper
K8s 里最经典的用法是 OPA Gatekeeper:它以准入控制器(Admission Controller)的形式部署,在对象被持久化之前用 ConstraintTemplate 完成校验。这正好和本站已介绍的 Kyverno 策略即代码 形成互补——两者都能做策略即代码,区别在下文对比。
3.1 定义一个禁止 latest 镜像标签的约束
先用 ConstraintTemplate 声明规则(Rego),再用 Constraint 指定作用范围。
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowlatesttag
spec:
crd:
spec:
names:
kind: K8sDisallowLatestTag
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowlatesttag
violation[{"msg": "禁止使用 latest 标签"}] {
input.review.object.spec.containers[_].image endsWith "latest"
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowLatestTag
metadata:
name: disallow-latest-in-prod
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "prod"
应用后,任何在 prod 命名空间里使用 image:xxx:latest 的 Pod 都会被拒绝创建。策略变更只需改 YAML 重新 apply,不必动业务镜像。
3.2 Gatekeeper 与 Kyverno 怎么选
| 维度 | OPA Gatekeeper | Kyverno |
|---|---|---|
| 策略语言 | Rego(强大但需学习) | 类 K8s YAML(上手快) |
| 数据来源 | 任意(input 灵活) | 主要 K8s 对象 |
| 审计能力 | 内置 Constraint 报告 | PolicyReport |
| 适用人群 | 已有 Rego 经验 / 需复杂逻辑 | 纯 K8s 团队、不想学新语言 |
四、服务间授权:用 OPA 做 API 授权
OPA 不只管 K8s,也能作为独立授权服务运行。常见模式是 Envoy 通过 ext_authz 过滤器把请求转发给 OPA sidecar 做决策,这与本站 Istio/Envoy 流量治理 里提到的”扩展鉴权”思路一致。
# 在 sidecar 里启动 OPA,监听 8181 提供决策服务
opa run --server \
--set services.local.url=http://localhost:8181 \
--set bundles.authz.url=http://config-server/policy.tar.gz
业务服务收到请求时,向 OPA 发一个 /v1/data/authz/allow 的查询,带上请求上下文(用户、路径、方法),OPA 返回布尔值。这样授权逻辑集中在一处,所有服务共用同一套策略,变更零发版。
一个真实的决策请求长这样:把请求上下文 POST 给 OPA 的 Data API,OPA 在毫秒内返回 allow 的结果。注意 input 只传决策必需的字段,不要把整个请求体都塞进去。
# 向 OPA 决策服务查询
curl -s -X POST http://localhost:8181/v1/data/authz/allow \
-H 'Content-Type: application/json' \
-d '{"input":{"method":"DELETE","user":{"role":"guest"}}}'
# 返回 {"result":false}
五、OPA 与 Kubernetes 原生 ValidatingAdmissionPolicy
K8s 1.30+ 提供了原生的 ValidatingAdmissionPolicy(本站 准入控制实战 已介绍),它用 CEL 而非 Rego。三者关系如下:
| 方案 | 语言 | 依赖 | 适合场景 |
|---|---|---|---|
| OPA Gatekeeper | Rego | 需装组件 | 复杂策略、跨系统复用 |
| Kyverno | YAML | 需装组件 | 纯 K8s 策略 |
| ValidatingAdmissionPolicy | CEL | 原生无需组件 | 轻量校验、不想引第三方 |
建议:已有 OPA 投资或需复杂逻辑选 Gatekeeper;只想快速校验且不想引第三方,用原生 CEL。三者可以并存,按需选择。
六、生产落地清单
| 环节 | 建议 |
|---|---|
| 策略仓库 | 策略进 Git,PR 评审,CI 跑 opa test(可接 GitHub Actions) |
| 默认拒绝 | 所有 package 设 default allow = false |
| 性能 | 预热策略 bundle,避免每次远程拉取 |
| 可观测 | 暴露 OPA 指标到 Prometheus |
| 灰度 | 先 dry-run / audit 模式观察,再切 enforce |
七、常见误区与排障
- 误区一:把业务数据塞进策略。Rego 不适合做重计算,输入应尽量精简,只传决策必需的字段。
- 误区二:忘记默认拒绝。务必写
default allow = false,否则未覆盖的请求会被放行,留下安全漏洞。 - 排障方法:先用
opa eval本地复现,再比对生产 input;Gatekeeper 侧查看 Constraint 状态与 Pod event。
八、用 opa test 给策略写单元测试
策略也是代码,同样需要测试。OPA 自带 opa test,让你用 Rego 写断言,在 CI 里跑回归,避免一次策略修改误放行了不该放行的请求。把下面内容保存为 policy_test.rego,与 policy.rego 放在同一目录即可。
# policy_test.rego
package authz
test_admin_can_delete {
allow with input as {"method":"DELETE","user":{"role":"admin"}}
}
test_guest_cannot_delete {
not allow with input as {"method":"DELETE","user":{"role":"guest"}}
}
运行 opa test .,两个用例都应通过。把这条命令接进 GitHub Actions 的流水线,策略 PR 合并前自动跑测试,安全性和代码质量一起守住。
九、一个真实场景:多租户 API 网关授权
设想一个 SaaS 平台,API 网关需要根据”租户套餐 + 接口 + 当前用量”决定放行与否。如果把这个逻辑写进网关代码,每调一次套餐就要发版。改用 OPA 后,套餐规则写成 Rego,运营在后台改套餐,策略 bundle 自动下发,网关无需重启即可生效;限流计数这类动态数据通过 input 实时传入,OPA 在单次请求内完成”套餐是否包含该接口 + 是否超额”的组合判断。这正是 OPA 相比散落 if/else 的核心优势:决策逻辑与业务代码彻底分离,可独立迭代、独立测试、独立审计。
结语
OPA 把”策略决策”这件容易被硬编码、难以统一的事,变成了一门可版本化、可测试、可复用的代码。无论是 Kubernetes 准入、API 授权还是微服务鉴权,它都能用同一套 Rego 接住。配合本站已有的 Kyverno、Envoy、Prometheus 体系,你完全可以搭建一套贯穿集群与服务的统一策略治理层。下一步,不妨先把团队里散落的 if/else 授权逻辑,挑一处迁到 OPA 试试。




