- 后端
- 认证鉴权
- 云原生
【免费下载链接】opa
Open Policy Agent (OPA) is an open source, general-purpose policy engine.
本文以官方 Upgrading to v1.0 指南为骨架,结合 v0 向后兼容说明 与仓库源码(
cmd/flags.go、ast/ast.go、bundle/bundle.go等),系统梳理 OPA v1.0 的升级策略、九个生产/消费版本组合场景、Rego v1 语法变更细节,以及 Rego 项目与 Go 集成的迁移工具链。读完本文,你将能依据自身部署形态选出一条平滑的升级路径,并熟练使用opa check --v0-v1、opa fmt --write --v0-v1、--v0-compatible等工具完成迁移。
一、升级总体思路:先升级 Producer,再升级 Consumer
所有用户都应规划升级到 OPA v1.0。对自身部署内 Rego 有较强掌控力的团队可以更快完成升级;而对 Rego 掌控力较弱、或运行第三方 Rego 的团队,可以先升级到 v1.0 并使用 v0 兼容模式 逐步过渡。官方文档采用以下三个概念描述系统角色:
- Bundle Producer(Bundle 生产端):基于
opa build构建 Bundle,供消费端加载的系统。 - Bundle Consumer(Bundle 消费端):加载并评估系统中 Bundle 内策略的 OPA 实例。
- Authoring(策略编写):在 Bundle 被构建与消费之前编写 Rego 策略的过程。在托管系统中,这可能是用户与 OPA 的唯一接触点。
有些系统不使用 Bundle,此时不存在 Producer,升级过程会因此简化。官方建议尽可能早地升级到 OPA v1.0;在更新 Rego 之前,优先使用 v0 兼容功能,而不是推迟升级。除非你的 Rego 已经兼容 v1.0,否则应参照 升级 Rego 一节先完成策略改造。
为什么先升级 Producer?升级后的 Producer 能够在 Bundle manifest 中写入 Rego 版本(rego_version),消费端无需再以--v0-compatible运行;同时生产端数量通常远少于消费端,先升级生产端能让整体迁移更平滑。当然,具备充分测试与验证条件的团队也可以一次性整体迁移;由于并非所有步骤对所有人都必要,混合式(hybrid)升级同样可行。
二、九个生产/消费版本组合场景总览
官方文档将系统按 Producer 与 Consumer 的 OPA 版本划分为 3×3 共九种组合,下表直接取自 Upgrading to v1.0,供对照定位自己的起点(拿不准时,Scenario 1 是最常见起点):
| v0.x Consumer | Mix Consumer | v1.0 Consumer | |
|---|---|---|---|
| v0.x Producer | Scenario 1(全 v0.x) | Scenario 4 | Scenario 7 |
| Mix Producer | Scenario 2 | Scenario 5 | Scenario 8 |
| v1.0 Producer | Scenario 3 | Scenario 6 | Scenario 9(全 v1.0) |
官方文档同时给出了一张升级路径流程图(mermaid),描绘推荐走向与可跳过的路径(例如从 Scenario 1 可以直接单步升级到 Scenario 9,即一次性整体迁移):
Scenario 1: 全 v0.x(最普遍起点)
所有 OPA 运行时(消费端与生产端)均为 v0.x,这是从旧版本开始升级最常见的情形。
- 运行方式:策略按 v0.x 兼容的方式编写。
- 下一步:开始将 Producer 升级到 v1.0(Scenario 2),直至所有 Producer 均为 v1.0(Scenario 3)。
Scenario 2: Mix Producer,v0.x Consumer
部分 Producer 为 v1.0,其余仍为 v0.x;所有 Consumer 均为 v0.x。典型场景是多个租户/用户使用不同 OPA 版本构建 Bundle,而你无法控制它们使用的版本。
- 前置条件:必须统一 Producer 版本,或具备对新 Producer 使用
--v0-compatible的控制能力,否则无法继续。 - 运行方式:策略按 v0.x 兼容编写;v0.x Producer 原样运行;v1.0 Producer 以
--v0-compatible运行,或在模块中加入rego.v1import;v0.x Consumer 原样运行。 - 下一步:继续迁移 Producer 至 v1.0,直到全部升级(Scenario 3)。
Scenario 3: v1.0 Producer,v0.x Consumer
Bundle 由 v1.0 实例构建,Consumer 仍在 v0.x。这是"先升级生产端"策略下的常见中间态。
- 前置条件:必须能控制 Producer 设置
--v0-compatible或使用rego.v1import。 - 运行方式:策略按 v0.x 兼容编写;v0.x Consumer 原样运行;v1.0 Producer 以
--v0-compatible运行,或模块带rego.v1import;由于策略总是被 v0.x OPA 消费,所有策略必须保持 v0.x 兼容。 - 下一步:Producer 已全部 v1.0,接下来把 Consumer 全部升到 v1.0(Scenario 6)。
Scenario 4: v0.x Producer,Mix Consumer
Producer 为 v0.x,Consumer 混合 v0.x 与 v1.0。该情形发生在用户已部分升级 OPA 实例但尚未升级 Consumer 时。官方不推荐在可避免时采取此路径(应优先升级 Producer)。
- 前置条件:v1.0 Consumer 必须能以
--v0-compatible接受 v0.x Bundle,否则此路径无法继续。 - 运行方式:策略按 v0.x 兼容编写;v0.x Producer 与 v0.x Consumer 原样运行。
- 下一步:将 Producer 升级到 v1.0 后继续推进。一般建议先升 Producer;但根据现有 v1.0 Consumer 部署情况,你也可以选择把所有 Producer 升到 v1.0,而非回退 Consumer。
Scenario 5: Mix Producer,Mix Consumer
Bundle 生产与消费均使用混合版本。
- 前置条件:由于 Producer 版本混杂,必须能对 Producer 设置
--v0-compatible;同时必须能对 v1.0 Consumer 设置--v0-compatible标志。两个条件都必须满足才能继续。 - 运行方式:策略按 v0.x 兼容编写;v1.0 Consumer 以
--v0-compatible运行;v1.0 Producer 以--v0-compatible运行。 - 下一步:逐步将 Producer 升级到 v1.0,直到全部为 v1.0(Scenario 6)。
Scenario 6: v1.0 Producer,Mix Consumer
所有 Consumer 可以不带任何标志运行,因为 Bundle 中包含rego_version属性,可告知 v1.0 OPA 实例接受 v0.x 模块。
- 运行方式:策略按 v0.x 兼容编写;v0.x Consumer 原样运行(Bundle 内为 v0.x 策略);v1.0 Consumer 原样运行(Bundle 含
rego_version属性,v0.x 模块被接受)。 - 前置条件:若无法让 v1.0 Producer 使用
--v0-compatible以兼容 v0.x Consumer,此路径会被阻塞。 - 下一步:最终目标是全部运行 v1.0 Producer 与 Consumer(Scenario 9)。
Scenario 7: v0.x Producer,v1.0 Consumer
所有 Consumer 均为 v1.0,Producer 仍为 v0.x。该情形发生在评估用 OPA 实例先于 Bundle 打包系统升级时。
- 前置条件:若 v1.0 Consumer 不能以
--v0-compatible运行,则加载 v0.x Producer 生成的 Bundle 时,由于 Bundle 不含rego_version属性,路径会被阻塞,直到 Producer 能生成带 Rego 版本的 Bundle 或可使用--v0-compatible。 - 运行方式:策略按 v0.x 兼容编写;v1.0 Consumer 以
--v0-compatible运行。 - 下一步:将 Producer 升级到 v1.0(Scenario 8),直至全部 v1.0(Scenario 9)。
Scenario 8: Mix Producer,v1.0 Consumer
所有 Consumer 均为 v1.0,Producer 混合 v0.x 与 v1.0。
- 运行方式:策略按 v0.x 兼容编写;v0.x Producer 原样运行;v1.0 Consumer 以
--v0-compatible运行;v1.0 Producer 以--v0-compatible运行。 - 前置条件:若使用 v0.x Bundle,Producer 必须能设置
--v0-compatible才能被 v1.0 Consumer 接受。v1.0 Consumer 会接受 v1.0 Producer 构建的 Bundle(manifest 中带有 Rego 版本);但不会接受 v0.x Producer 的 Bundle,除非设置了--v0-compatible。 - 下一步:把 Producer 全部升到 v1.0(Scenario 9),完成升级。
Scenario 9: 全 v1.0(升级完成)
所有 Consumer 与 Producer 均运行 v1.0,即完成 OPA v1.0 升级。若使用了--v0-compatible功能,接下来应把 OPA 实例加载的 Rego 升级为 Rego v1。无论是否继续升级 Rego,都建议运行opa check、opa check --strict并对 Rego 项目做 lint,以提前发现问题。
三、Rego v1.0 的语言变更
将 OPA 实例升级到 v1.0(或一次性整体升级)后,需要把 Rego 策略升级为 Rego v1.0。官方文档明确了以下五个方面的语法变更。
1.future.keywords导入成为空操作
in、every、if、contains四个关键字是陆续引入的,Rego v0.x 要求显式future.keywords导入以避免破坏既有策略(opt-in 机制)。这些关键字提升了策略可读性,并为迭代、成员判断、多值规则等常见操作提供语法糖,在 OPA 文档与 Rego Playground 中已被广泛采用。
在 OPA v1.0 中,这四个关键字默认成为语言的一部分,再导入future.keywords.*将成为空操作(no-op)。在源码层面,ast/ast.go 定义了三种RegoVersion常量:
RegoV0:默认的原始 Rego 语法(当前 v0 包默认值);RegoV0CompatV1:同时满足 RegoV0 与 RegoV1 语法(如同模块导入了rego.v1的效果);RegoV1:OPA 1.0 强制执行的 Rego 语法,关键字集成为默认且无需导入。
而internal/future包(如 filter_imports.go)负责解析future.keywords与future.keywords.xyz形式的导入,将其转换为解析器选项(AllFutureKeywords/FutureKeywords)。一个使用了这些关键字但未导入的模块在 OPA v1.0 中合法,但在旧版本中不合法。
2. 强制在规则头部使用if与contains
Rego v0.x 中,a.b {true}与a.b.c {true}存在语义歧义:两者语法相似,但前者在data.a路径下生成包含条目b的集合,后者在data.a.b路径下生成属性"c": true的对象。if关键字不只是语法糖——规则头部使用if后,该规则不会贡献部分集合(partial set),除非同时使用contains。例如a.b if {true}在data.a下生成属性"b": true的对象。
OPA v1.0 要求声明规则时使用if和contains,意味着:
- 所有规则默认是单值的;头部省略值时默认值为
true; - 要使规则变为多值(即部分集合规则),使用
contains关键字把值转换为集合。
contains用于消除生成单值规则与多值规则的歧义;if用于确保规则语义在 v0.x 与 v1.0 之间不发生改变。下表(官方文档原文)展示了为何必须引入if:
| rule | output in v0.x | output in v1.0 |
|---|---|---|
p { true } | {"p": true} | compile error |
p.a { true } | {"p": {"a"}} | compile error |
p.a.b { true } | {"p": {"a": {"b": true}} | compile error |
p if { true } | {"p": true} | {"p": true} |
p.a if { true } | {"p":{"a": true}} | {"p":{"a": true}} |
p.a.b if { true } | {"p": {"a": {"b": true}} | {"p": {"a": {"b": true}} |
p contains "a" | {"p": {"a"}} | {"p": {"a"}} |
如果语言被改成"所有规则默认单值、除非用contains转为多值",那么p.a { true }这类规则在 v0.x 与 v1.0 之间的输出会静默改变而不报错。相比改变既有规则语义,产生编译错误是更可取的,因此 OPA v1.0 强制要求使用if关键字。
需要注意:if仅对带规则体的规则是必需的;常量(纯值赋值规则)不需要if。以下形式在 v1.0 中仍然合法:
| rule | output in v0.x | output in v1.0 |
|---|---|---|
p := 1 | {"p": 1} | {"p": 1} |
p.a := 1 | {"p": {"a": 1}} | {"p": {"a": 1}} |
p.a.b := 1 | {"p": {"a": {"b": 1}}} | {"p": {"a": {"b": 1}}} |
由于if只能置于规则体之前,没有规则体也没有值赋值的孤立引用(solitary reference)在 v1.0 语法中不被允许:
| rule | output in v0.x | output in v1.0 |
|---|---|---|
p | compile error | compile error |
p.a | {"p": {"a"}} | compile error |
p.a.b | {"p": {"a": {"b": true}}} | compile error |
下表汇总了 v0.x 合法但在 v1.0 非法的写法及其等价 v1.0 写法:
| invalid in v1.0 | v1.0 equivalent | Note |
|---|---|---|
p { true } | p if { true } | 单值规则 |
p.a | p contains "a" | 多值插入 |
p.a { true } | p contains "a" if { true } | 多值规则 |
p.a.b | p.a.b := true | 单值赋值 |
p.a.b { true } | p.a.b if { true } | 单值规则 |
生成集合的规则示例:
package play a contains b if { b := 1 }评估输出(JSON 中集合被序列化为数组):
{ "a": [1] }生成对象的规则示例:
package play a[b] if { b := 1 }评估输出:
{ "a": { "1": true } }if与contains的要求消除了单值/多值规则声明的歧义,使 Rego 更易编写、阅读与理解。
3. 禁止重复导入(shadowing)
作为 OPA 0.xstrict模式的一部分,编译器禁止一个导入遮蔽(shadow)另一个导入;OPA v1.0默认强制执行该检查。导入相互遮蔽极可能是无意的编写错误,默认检查有助于避免策略评估产生易错决策。
4.input与data成为保留关键字
Rego 编译器确保input和data是保留关键字,不得用作规则名或变量赋值名(同样源自 OPA 0.x 的strict模式)。原因在于:input文档存放用户提供的输入,而推入 OPA 的数据与规则评估结果嵌套在data文档下。若规则或变量遮蔽input/data,会在局部作用域内意外抹掉这些信息,导致错误的策略决策。v1.0 默认避免此类场景。
注意:使用with关键字向input或data文档插入/整体替换值(如my_func(x) with input as {...})不算遮蔽,在 v1.0 中合法。关于with的详细语义可参考 policy-language.md。
5. 禁止使用已弃用内置函数
作为 OPA 0.xstrict模式的一部分,编译器禁止使用已弃用的内置函数;在 OPA v1.0 中这些内置函数已被移除。官方列出的弃用内置函数为:any、all、re_match、net.cidr_overlap、set_diff、cast_array、cast_set、cast_string、cast_boolean、cast_null、cast_object。其中部分场景已由新内置函数提供至少相近的功能。仓库中 topdown/builtins/builtins.go 的包注释也明确标注该包为旧项目从 v0.x 迁移而保留,OPA v1.x 生命周期内可用但不建议使用。
四、v1.0 默认执行的编译约束与安全检查
以下约束与安全检查在 v1.0 编译期间默认强制执行。它们连同 v1.0strict模式(见 policy-language.md)中的检查,在 OPA 0.x 时期都属于编译器的strict模式:
| Name | Description |
|---|---|
| Duplicate imports | 禁止重复 imports,即一个 import 遮蔽另一个。 |
inputanddatareserved keywords | input与data是保留关键字,不得用作规则名与变量赋值名。 |
| Use of deprecated built-ins | 禁止使用已弃用内置函数,这些函数将在 OPA 1.0 中被移除。已弃用内置函数:any、all、re_match、net.cidr_overlap、set_diff、cast_array、cast_set、cast_string、cast_boolean、cast_null、cast_object |
五、升级 Rego 的四个步骤
v0.x Rego 项目建议按以下流程升级,以符合最佳实践并兼容 OPA v1.0。开始前请确保本地有1.0 或更高版本的 OPA 二进制。
opa check --v0-v1:捕获解析(parse)与编译(compile)错误。opa check --v0-v1 --strict:进一步指出可能导致与 OPA v1.0 不兼容的问题,如使用已弃用内置函数、重复导入等。opa fmt --write --v0-v1:自动将代码重排为 OPA v1.0 兼容格式。regal lint:Regal linter(仓库内文档见 docs/projects/regal)提供更多规则,用于排查可能导致错误、性能低下或意外行为的 Rego 代码问题。
升级过程中遇到问题时,可在 OPA Slack 的#help频道求助(官方文档建议)。
配套机制:rego.v1导入与--v0-v1标志
在正式切换前,v0 兼容模式下有三种渐进工具(详见 v0-compatibility.md):
rego.v1导入:让 OPA 应用 v1.0 默认强制执行的全部限制。模块导入rego.v1意味着隐含了适用的future.keywords导入;同一模块内同时导入rego.v1与future.keywords.in/every/if/contains是非法的。opa fmt --v0-v1:把现有模块中的future.keywords.*导入重写为rego.v1导入。opa check --v0-v1:检查模块中只要使用了in、every、if、contains任一关键字,就必须存在rego.v1导入或适用的future.keywords.*导入。
在命令行实现层面,cmd/flags.go 中可以看到这三个标志的真实定义:
--v0-v1:将模块视为同时兼容旧版 0.x 与当前 v1.0 的 Rego 语法(用于check/fmt);--v0-compatible:选择加入 OPA v1.0 之前的功能与行为(v1.0 中用于接受 v0.x 模块);--v1-compatible:选择加入 OPA v1.0 默认启用的功能与行为(v1.0 中已隐藏,主要用于 v1.0 之前版本产生/消费 Rego v1 Bundle)。
六、Rego-versioned Bundle(带 Rego 版本的 Bundle)
使用 OPAv0.64.0或更高版本构建的 Bundle,其 manifest 中包含rego_version属性;消费该 Bundle 的 OPA 会在处理其中模块时使用该版本。Bundle 内部的 rego 版本优先于--v1-compatible标志,因此无需事先了解所消费 Bundle 内的 Rego 语法。opa build命令上的--v1-compatible标志(以及 v1.0 中的--v0-compatible)允许用户控制所构建 Bundle 的 rego 版本。
这一机制正是 Scenario 6/8/9 中"v1.0 Consumer 可原样运行"的基础:Bundle 自带版本声明,消费端无需猜测。源码层面,bundle/bundle.go 的MergeWithRegoVersion在合并 Bundle 时显式接收ast.RegoVersion参数,印证了 Bundle 处理与 Rego 版本的深度绑定。
七、升级 OPA 实例:服务器监听地址的变化
OPA 1.0 之前,opa run --server/-s默认绑定所有网卡接口;OPA 1.0 默认改为绑定localhost。在受信任环境中这并非不安全,但如果 OPA 不打算对外暴露,默认绑定 localhost 是更稳妥的做法。如需复刻 v0.x 行为,可使用--addr标志绑定所有接口:
opa run --server --addr 0.0.0.0:8181提示:在容器中运行 OPA 时,若实例需要被宿主机或其他容器访问,则必须绑定所有接口。
更多细节参见 security.md。相应地,v0 兼容模式说明(v0-compatibility.md)中也指出:run命令在--v0-compatible下默认绑定所有接口而非 localhost。
八、升级 Go 集成:切换到 v1 包
官方建议使用 v0 SDK 与 v0 Rego 包的用户升级到新的 v1 包:
github.com/open-policy-agent/opa/v1/sdkgithub.com/open-policy-agent/opa/v1/rego
升级只需修改 import 路径。例如:
import ( "github.com/open-policy-agent/opa/rego" )改为:
import ( "github.com/open-policy-agent/opa/v1/rego" )这适用于应用依赖的所有OPA 包,而不只是rego和sdk,其他常用包还包括ast、bundle、compile、types、topdown。自 OPA 1.0 起,所有 v0 包均已弃用;它们将在 OPA 1.0 的整个生命周期内保留,但仍建议尽快升级。需要 v0 功能时仍可使用 v1 包,详见 Backwards Compatibility。
在 Go 集成中启用 v0.x 兼容
若仍需处理 v0.x Rego,官方推荐按粒度由粗到细的三种方式(详见 v0-compatibility.md):
- 在模块上设置 Rego 版本(首选,粒度最细):
m := ast.Module{ Package: regoCode, } m.SetRegoVersion(ast.RegoV0)- 在 Bundle manifest 上设置 Rego 版本:
b := Bundle{ // ... } b.SetRegoVersion(ast.RegoV0)前两种方式更受推荐,因为它们粒度更细,允许同一 OPA 实例内混合运行 v0.x 与 v1.0 兼容的 Rego,从而支持渐进式升级。
- 使用
SetRegoVersionRego 参数(仅当前两种不适用时):
r := rego.New( rego.Query("data.foo.bar"), rego.Module("policy.rego", regoCode), rego.SetRegoVersion(ast.RegoV1), // <--- )此外,也可以直接导入 v0 包(github.com/open-policy-agent/opa/rego,而非 v1 的.../opa/v1/rego)编写程序。警告:在同一程序中混用 v0 包与 v1 包被视为反模式,不受推荐也不受支持;两者间的互操作性没有保证。
对于必须支持 v0 Bundle 的 SDK 用户,应尽量按上文在 Bundle manifest 上设置 Rego 版本;无法做到的用户需使用 v0 SDK 导入(github.com/open-policy-agent/opa/sdk),并应尽快将 Bundle 版本化,以便切换到 v1 SDK。
九、快速自查清单
- 起点定位:按 九场景矩阵 找到当前组合,多数用户从 Scenario 1 起步;
- 顺序原则:先升级 Producer,再升级 Consumer;能整体迁移时也允许一次性切换;
- Rego 语法:规则体前加
if、多值规则用contains、移除孤立引用、检查重复导入、避免input/data遮蔽、替换已弃用内置函数; - 渐进工具:
rego.v1导入、opa check --v0-v1、opa fmt --write --v0-v1、regal lint; - Bundle 版本:优先用
v0.64.0+构建带rego_version的 Bundle,让消费端自动识别; - 服务器行为:v1.0 默认绑定 localhost,需要对外时用
--addr 0.0.0.0:8181; - Go 集成:把
rego/sdk/ast/bundle/compile/types/topdown等依赖切换到opa/v1/...路径。
遵循上述路径,即可在保留 v0.x 兼容性的前提下,分阶段、可回滚地完成 OPA v1.0 与 Rego v1 的迁移。
- 后端
- 认证鉴权
- 云原生
【免费下载链接】opa
Open Policy Agent (OPA) is an open source, general-purpose policy engine.
相关推荐
OPA 1.0 发布:Rego v1 默认语法与 Policy as Code 升级指南
OPA 1.0 发布:Rego v1 默认语法与 Policy as Code 升级指南 OPA(Open Policy Agent)在 2024 年 12 月
后端认证鉴权云原生Open Policy Agent 1.0 全面前瞻:Rego v1 语法变更、strict 默认化与迁移实践
Open Policy Agent 1.0 全面前瞻:Rego v1 语法变更、strict 默认化与迁移实践 OPA(Open Policy Agent)1.
后端认证鉴权云原生MDX Deck 迁移完全指南:从 v1 到 v4 的升级路径与代码改造实战
MDX Deck 迁移完全指南:从 v1 到 v4 的升级路径与代码改造实战 MDX Deck 是一个基于 React 与 MDX 的演示文稿框架,历经 v1→
开发工具前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考