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 的数据模型。

Rego 文件语法结构与数据模型映射 Rego 文件 (policy.rego) package kubernetes.admission 声明包路径 import data.kubernetes.pods 导入外部数据 default allow = false 默认规则 allow if { ... } 策略逻辑体 (条件判断、迭代) OPA 数据模型 (data) data.kubernetes.admission 包路径 data.kubernetes.pods 导入的数据 data.kubernetes.admission.allow 规则决策结果 映射关系 文件中的规则被编译为 data 下的 JSON 对象

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 语法的最佳途径。