- 后端
- 认证鉴权
- 云原生
【免费下载链接】opa
Open Policy Agent (OPA) is an open source, general-purpose policy engine.
在 Open Policy Agent(OPA)的 Rego 策略语言中,访问控制系统通常遵循"除非显式允许,否则拒绝"(default deny)的安全原则。本指南围绕 OPA 官方策略参考中default关键字的 deny 示例,讲解如何利用default allow := false保证策略结果永远是一个布尔值而非 undefined,并深入剖析 Rego 中default关键字的完整语法、适用场景与函数用法。读完本文,你将掌握编写健壮访问控制策略的核心技法,理解为什么"结果恒有定义"是生产级策略的关键设计目标。
背景:undefined 是访问控制的隐患
在 Rego 中,规则在没有匹配任何条件时会保持未定义(undefined),而不是返回false或空值。这一点在访问控制场景中非常危险:调用方(如 API 网关、Kubernetes admission webhook)通常期望策略返回一个明确的布尔结果,undefined 往往会被下游误处理为"允许"或触发异常。
官方文档在 keywords/default 关键字参考 中明确指出:
The
defaultkeyword is used to provide a default value for rules and functions. If in other cases, a rule or function is not defined, the default value will be used.
也就是说,default关键字的价值在于:让策略作者和调用方都确信某个文档(document)"总是有定义",从而无需在策略侧或调用侧再额外处理 undefined 的边界情况。
示例全景:Denying by default
本文的核心示例位于仓库docs/docs/policy-reference/keywords/_examples/default/deny/目录下,由标题文件 title.txt 可以得知该示例的主题正是"Denying by default"。示例配套了完整的 policy.rego、input.json、output.json、data.json 与 config.json,可直接在 OPA Playground 或本地opa eval中复现。
示例的 intro.md 说明其设计意图:当需要默认拒绝行为时,知道一个值永远不会是 undefined 是很有帮助的。这在访问控制系统中很常见——除非显式允许,否则一律拒绝访问。示例策略中的allow规则依赖input中的字段,如果任何字段缺失,allow应返回false而不是 undefined,这一目标正是通过default关键字实现的;策略同时还能处理意外的数据格式,确保结果始终是布尔值。
逐步拆解示例策略
示例策略完整代码如下:
package play default allow := false allow if input.admin == true allow if { input.path[0] == "users" input.path[1] == input.user }逐行解读:
package play:声明策略包名,便于在查询时以data.play.allow引用该文档。default allow := false:为allow文档定义默认值false。这是整个默认拒绝模式的基石——当下面两条规则都不满足时,allow不再是 undefined,而是确定地返回false。allow if input.admin == true:第一条允许规则。只要input.admin为true,访问即被允许。- 第二条规则:当
input.path的第一个元素是"users",且第二个元素与input.user相等时,允许访问。注意它使用了input.path[0]、input.path[1]这类下标访问——如果input.path缺失、不是数组或长度不足,这条规则在旧式 Rego 求值中会产生 undefined(而非直接 panic),此时第一条规则若也不满足,最终allow便会落到default值false上。
示例配套的 input.json 是:
{ "roles": [ "admin" ], "path": "/" }注意这里的输入故意"不友好":input.admin字段缺失、input.user字段缺失、input.path是字符串"/"而非数组。也就是说,两条allow规则都无法满足,查询结果(见 output.json)为:
{ "allow": false }这正是该示例要传达的核心行为:在输入数据格式完全不符合预期(字段缺失、类型错误)的情况下,策略依然稳定返回布尔值false,而不是 undefined 或求值错误。这与示例 config.json 中showInput: true的展示设置相呼应——用户可以在 Playground 中同时看到畸形输入与干净输出,直观理解 default deny 的价值。
default关键字的完整语法约束
在 policy-language.md 的 Default Keyword 章节中,OPA 对default的语法做了严格限定。当使用default关键字时,规则语法被限制为:
default <name> := <term>关于<term>(取值项)的约束:
- 取值可以是任何标量(scalar)、复合值(composite)或推导式(comprehension),但不能是变量或引用(reference);
- 如果取值是复合值(如数组、对象),则其中不能包含变量或引用;
- 推导式除外——推导式的结果永远不会是 undefined,因此可以作为
default的取值。
例如以下两种都是合法的:
default role := "guest" # 标量字符串 default location := "unknown" # 标量字符串从源码结构看,这些约束保证了default值本身在任何输入下都能被确定性地构造出来,不会反过来引入 undefined 的传播。
default关键字仅对**完整定义(complete definitions)**的规则产生默认文档值:当所有共享同一名称的规则都 undefined 时,使用默认值(见 policy-language.md#L2069-L2071)。可以对比理解:在示例中,如果没有default allow := false,对于上面的畸形输入,data.play.allow将直接是 undefined——调用方将得不到任何布尔判定。
进阶:default关键字同样适用于函数
default不仅能用于规则,还能用于函数,官方文档给出的典型示例是"正数截断":
default clamp_positive(_) := 0 clamp_positive(x) := x if { x > 0 }当查询clamp_positive时,返回值要么是传入的参数(当参数大于 0 时),要么是0——函数永远有定义,调用方无需判空。
default函数除了遵循与default规则相同的取值约束外,还必须满足以下性质(见 policy-language.md#L2138-L2143):
- 与同名其他函数保持相同的元数(arity);
- 参数只能是普通变量,不能是复合值;
- 参数名不得重复。
这里有一个值得注意的边界情况(policy-language.md#L2145-L2149):如果函数调用中的任一参数是 undefined,default函数依然会失败(即连默认值都不会返回)。原因在于:参数在函数调用之前就已经被求值,一旦某个参数 undefined,求值在该点就会中止。因此在为函数编写default时,仍需确保调用方传入的参数本身有定义。
在本地运行与验证
示例目录中的每个_examples/default/deny资源都可脱离 Playground 在本地验证。假设你已安装 OPA 二进制,可以按如下方式复现:
# 将上述 policy.rego 保存到本地后,用示例 input.json 求值 opa eval --format pretty --input docs/docs/policy-reference/keywords/_examples/default/deny/input.json \ 'data.play.allow'预期输出为:
false也可以去掉default allow := false这行再运行一次,此时结果会变为<undefined>——通过这个对照实验,你能最直观地体会到default关键字对"结果恒有定义"的保障作用。该示例在同目录下还有姊妹篇_examples/default/overrides(默认值被覆盖的场景),官方在 keywords/default.md 与 policy-language.md#L2151-L2153 中都引导读者结合这些示例深入学习。
总结:默认拒绝模式的最佳实践要点
回到本文的核心示例,编写健壮的默认拒绝(deny by default)策略时应把握以下要点:
- 用
default <name> := false为判定文档兜底,保证结果永远是布尔值,杜绝 undefined 泄漏给调用方; - 允许规则只写"显式放行"条件,任何不满足放行条件的输入自动落入默认拒绝;
- 默认值必须是常量式的标量/复合值/推导式,不能依赖变量或引用,确保默认值本身稳定可构造;
- 对函数同样可设默认值,但需注意参数 undefined 仍会阻止函数求值这一边界;
- 在本地用
opa eval对照"有无 default 关键字"两种结果,验证策略在不同输入(包括畸形输入)下的确定性。
默认拒绝是访问控制策略的黄金法则,而 Rego 的default关键字正是让这一法则落地为"类型安全、永远有结果"的工程保障。
- 后端
- 认证鉴权
- 云原生
【免费下载链接】opa
Open Policy Agent (OPA) is an open source, general-purpose policy engine.
相关推荐
Salt Resources 拒绝默认(Deny-by-Default)执行加载器:修复 69881 的 per-resource-type 模块隔离机制
Salt Resources 拒绝默认(Deny by Default)执行加载器:修复 69881 的 per resource type 模块隔离机制 导读
运维配置管理后端OPA/Rego 的 default 关键字实战:用默认函数值构建可覆盖的基准策略
OPA/Rego 的 default 关键字实战:用默认函数值构建可覆盖的基准策略 本篇文章围绕 Open Policy Agent(OPA)策略语言 Rego
后端认证鉴权云原生IronClaw 内核授权裁决:ironclaw_authorization 的默认拒绝(default-deny)授权与能力租约(Capability Lease)状态机解析
IronClaw 内核授权裁决:ironclaw_authorization 的默认拒绝(default deny)授权与能力租约(Capability Lea
人工智能AI 应用交互助手AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考