3. Rego 语言入门(一):Rego 语法基础、关键字、注释、包声明与导入
Rego 是 Open Policy Agent (OPA) 的策略语言,专为在云原生环境中表达策略逻辑而设计。它并非通用编程语言,而是一种声明式查询语言,其核心思想是将策略定义为对结构化数据(如 JSON、YAML)的查询规则。本章将介绍 Rego 的语法骨架,包括基础结构、关键字、注释、包声明与导入机制,为后续编写准入控制策略打下坚实基础。
3.1 Rego 语法基础:声明式查询与规则
Rego 的语法核心是 规则 (Rule)。规则定义了一个逻辑条件,当条件满足时,规则被求值为 true,否则为 false 或 undefined。OPA 通过评估这些规则来做出策略决策。
- 声明式:你描述“策略应该是什么”,而不是“如何计算策略”。
- 基于文档:Rego 将输入数据视为嵌套的文档(Document),通过点号(
.)访问字段。 - 不可变性:变量一旦绑定,在同一个规则内不可重新赋值。
核心概念: 一个 Rego 文件通常包含多个规则,每个规则对应一个策略决策点。OPA 引擎会基于输入(
input)和数据(data)来评估这些规则。
3.2 关键字:Rego 的保留词汇
Rego 的关键字用于定义规则、控制流程和声明包。以下表格列出了最常用的关键字及其用途:
| 关键字 | 用途 | 示例 |
|---|---|---|
package |
声明当前文件的包名,用于组织规则和避免命名冲突。 | package kubernetes.admission |
import |
导入其他包中的规则或数据,简化引用路径。 | import data.kubernetes.pods |
default |
为规则定义一个默认值,当规则体未匹配时使用。 | default allow = false |
true / false |
布尔字面量,用于规则体中的条件判断。 | result := true |
null |
表示空值或缺失值。 | input.metadata.name != null |
with |
在测试或模拟中,临时替换输入或数据的一部分。 | allow with input as {"name": "test"} |
not |
逻辑非运算符,用于否定一个条件。 | not input.readOnly |
some |
声明局部变量,用于迭代集合或数组。 | some i; input.containers[i].image |
in |
成员关系运算符,检查元素是否存在于集合或数组中。 | "nginx" in input.images |
every |
全称量词,用于检查集合中的所有元素是否满足条件。 | every container in input.containers { container.image != "latest" } |
最佳实践: 在编写策略时,优先使用
every 和 some 关键字来处理集合,这比手动迭代更简洁且不易出错。
3.3 注释:为策略添加说明
Rego 支持两种注释风格,用于解释策略逻辑或临时禁用规则。
- 单行注释:以
#开头,直到行尾。 - 多行注释:以
/*开头,以*/结尾,可以跨越多行。
# 这是一个单行注释,用于解释下面规则的目的
allow if {
# 检查用户是否属于管理员组
input.user.role == "admin"
}
/*
* 这是一个多行注释块。
* 它可以用来详细描述一个复杂的策略逻辑,
* 或者暂时注释掉一组规则进行调试。
*/
# deny if {
# input.user.role == "guest"
# }
3.4 包声明:组织策略的命名空间
package 关键字用于声明当前 Rego 文件所属的包。包名决定了规则在 OPA 数据模型中的路径。例如,一个声明为 package kubernetes.admission 的规则,可以通过 data.kubernetes.admission 路径访问。
- 命名规范:包名通常使用小写字母和点号分隔,反映策略的层级结构。
- 文件与包的关系:一个文件通常只包含一个
package声明,且该声明必须位于文件顶部(注释之后)。 - 作用域:包内的规则默认是私有的,但可以通过
import被其他包引用。
package opa.example.network
# 此规则可以通过 data.opa.example.network.allow_external 访问
allow_external if {
input.destination == "external"
}
3.5 导入:引用外部规则与数据
import 语句允许你在当前包中引用其他包中的规则或数据,从而避免使用冗长的全路径。导入必须位于 package 声明之后,规则定义之前。
- 导入整个包:
import data.kubernetes.pods允许你直接使用pods来引用data.kubernetes.pods。 - 导入特定规则:
import data.kubernetes.pods.list允许你直接使用list。 - 导入 future 关键字:
import future.keywords.every用于启用every等较新的关键字,确保向后兼容。
package opa.example.policy
import data.kubernetes.pods
import future.keywords.every
# 现在可以直接使用 pods 变量,而不需要写 data.kubernetes.pods
deny_privileged_containers if {
every container in pods.containers {
container.securityContext.privileged == false
}
}
注意: 导入的路径是相对于 OPA 数据根目录
data 的。不要包含 data 前缀之外的路径。例如,import input.request 是错误的,因为 input 是特殊变量,不是数据包。
3.6 语法结构总览:一个完整的 Rego 文件
以下 SVG 图展示了一个标准 Rego 文件的语法结构,从包声明到规则定义,以及它们如何映射到 OPA 的数据模型。
3.7 关键字与语法对照表
为了帮助你快速区分 Rego 与其他策略语言(如 Sentinel、Styra DAS)的语法差异,下表列出了 Rego 关键字的典型使用场景:
| 关键字/语法 | 典型场景 | 示例 |
|---|---|---|
package |
定义策略模块的命名空间 | package networking.firewall |
import |
引入外部规则或数据 | import data.roles.admin |
default |
设置规则的默认值 | default allowed = false |
some |
声明局部变量用于迭代 | some i; input.names[i] |
every |
全称量词检查 | every x in list { x > 0 } |
not |
逻辑否定 | not input.privileged |
in |
成员关系检查 | "admin" in input.roles |
with |
测试时替换输入 | allow with input as {...} |
学习建议: 在 OPA Playground 中尝试修改上述代码示例,观察不同关键字对策略结果的影响。实践是掌握 Rego 语法的最佳途径。