4. Rego 语言入门:基本语法、规则定义、查询与数据模型

Rego 是 OPA 的声明式查询语言,专门用于定义策略规则。它脱胎于 Datalog,但扩展了对 JSON/YAML 等结构化数据的原生支持。理解 Rego 的核心概念——数据模型、规则与查询——是编写安全合规策略的基础。

4.1 数据模型:输入、数据与文档

Rego 将一切视为 文档(Document)。OPA 引擎在评估策略时,主要依赖两类文档:

文档类型 变量名 说明 示例(Serverless 流水线)
输入文档 input 每次查询时由外部传入的临时数据,代表当前请求或事件。 流水线构建请求的 JSON:{"method":"POST","path":"/deploy","user":"ci-bot"}
数据文档 data OPA 启动时加载的持久化数据,包含策略规则本身和外部数据(如黑名单、配置)。 允许部署的命名空间列表:data.allowed_namespaces
关键理解: 策略规则也是 data 文档的一部分。当你定义规则 allow { ... } 时,实际上是在 data 下创建了一个名为 allow 的文档。

4.2 基本语法与表达式

Rego 语法简洁,核心是 表达式(Expression) 的组合。表达式返回 true 或 false,多个表达式通过逻辑运算符连接。

  • 字面量:字符串、数字、布尔值、null。
  • 变量:以 _ 或字母开头,如 user、namespace。变量在规则中必须被赋值一次(单一赋值)。
  • 引用:通过点号或方括号访问嵌套数据,如 input.method、data.roles["admin"]。
  • 数组/对象复合值:["a","b"]、{"key":"value"}。
  • 比较运算符:==、!=、<、>、<=、>=。
  • 逻辑运算符:not、and(默认,用分号或换行连接)、or(用 else 或括号显式表达)。
# 基本表达式示例
input.method == "POST"
input.user != "anonymous"
count(data.allowed_namespaces) > 0

4.3 规则定义:完整规则与部分规则

规则是 Rego 的核心构建块。根据返回值类型,规则分为两类:

规则类型 返回值 定义方式 典型用途
完整规则 单个布尔值或标量 规则名 := 表达式 全局决策(允许/拒绝)
部分规则 一组值(集合或对象) 规则名[键] := 值 { 条件 } 生成可迭代的策略数据

4.3.1 完整规则(Complete Rules)

使用 := 定义,规则体中的所有表达式必须同时为真,规则才为 true。

# 完整规则:判断部署请求是否来自受信任的 CI 用户
allow_deploy := true {
    input.user == "ci-bot"
    input.method == "POST"
    input.path == "/deploy"
}

# 查询:data.allow_deploy
# 当 input 满足所有条件时返回 true,否则返回 undefined(视为 false)

4.3.2 部分规则(Partial Rules)

使用 [...] 定义,可以为集合或对象生成多个值。每个满足条件的表达式都会产生一个结果。

# 部分规则:生成所有被允许部署的命名空间集合
allowed_namespaces[ns] {
    ns := input.namespace
    ns != "production"
    ns != "critical"
}

# 查询:data.allowed_namespaces
# 返回:["staging", "dev"](假设 input.namespace 包含这些值)

4.4 查询:如何获取策略决策

查询是向 OPA 提问的方式。通过 REST API 或 CLI 执行查询,OPA 会评估所有相关规则并返回结果。

  • 简单查询:直接请求规则文档,如 data.allow_deploy。
  • 带变量的查询:使用变量绑定结果,如 data.allowed_namespaces[x] 会返回所有满足条件的 x 值。
  • 使用 with 关键字:在查询中模拟 input 或 data 的值,用于测试。
# 使用 opa eval 命令进行查询
# 查询完整规则
opa eval --data policy.rego --input input.json "data.allow_deploy"

# 查询部分规则的所有值
opa eval --data policy.rego --input input.json "data.allowed_namespaces"
常见误区: 完整规则如果没有任何条件满足,返回的是 undefined 而非 false。在策略中,通常将 undefined 视为拒绝。建议显式定义 default allow = false 来避免歧义。

4.5 数据模型与规则关系图

下图展示了 OPA 数据模型、规则定义与查询执行之间的核心关系:

Rego 数据模型与规则关系图 Rego 数据模型与规则执行流程 输入文档 input 数据文档 data Rego 规则引擎 评估规则体表达式 策略决策输出 allow / deny / 集合 查询请求 opa eval / REST API 说明: 1. 查询请求触发 OPA 评估,input 和 data 作为输入源。 2. 规则引擎执行 Rego 规则体,规则本身存储在 data 文档中。 3. 输出可以是布尔值(完整规则)或集合/对象(部分规则)。

4.6 常用内置函数与迭代

Rego 提供了丰富的内置函数,用于处理字符串、数组、集合和时间。在 Serverless 流水线策略中,以下函数高频使用:

函数 签名 用途 示例
count count(x) 返回数组/集合/对象的元素个数 count(input.images) > 0
split split(str, delimiter) 按分隔符拆分字符串为数组 split(input.image_ref, ":")[0]
glob.match glob.match(pattern, delimiters, match) 通配符匹配,常用于路径或镜像名匹配 glob.match("registry.example.com/*", [], input.image)
time.now_ns time.now_ns() 返回当前 Unix 纳秒时间戳 time.now_ns() > input.expiry

4.6.1 迭代与推导式

Rego 使用 推导式(Comprehension) 来迭代集合或数组,生成新的集合/对象。这是编写部分规则的核心技巧。

# 数组推导式:从 input.images 中提取所有包含 "vuln" 的镜像
vulnerable_images := [image |
    image := input.images[_]
    contains(image, "vuln")
]

# 集合推导式:生成所有被扫描出的高危 CVE 集合
high_cves := {cve |
    some cve in input.scan_results.cves
    cve.severity == "CRITICAL"
    cve.id
}

# 对象推导式:构建镜像名到标签的映射
image_labels := {image: label |
    some i in input.images
    image := i.name
    label := i.label
}
实践要点: 在 Serverless 流水线中,input 通常包含构建元数据、镜像列表、环境变量等。使用推导式可以高效地从这些数据中提取出违反策略的元素,然后通过 count() 或 not 组合成最终的允许/拒绝决策。

4.7 规则组合与默认值

实际策略很少只包含一条规则。Rego 允许通过 规则组合 和 默认值 构建层次化的决策逻辑。

# 默认拒绝
default allow := false

# 规则 1:允许来自 CI 的部署
allow := true {
    input.user == "ci-bot"
    input.action == "deploy"
}

# 规则 2:允许来自审计员的只读操作
allow := true {
    input.user == "auditor"
    input.action == "read"
}

# 规则 3:拒绝使用过时的镜像标签
deny[msg] {
    image := input.images[_]
    not glob.match("*:latest", [], image)
    msg := sprintf("镜像 %v 未使用 latest 标签,禁止部署", [image])
}

# 最终决策:allow 为 true 且没有 deny 消息
final_allow := true {
    allow == true
    count(deny) == 0
}

上述模式是 Serverless 流水线策略的典型结构:先定义默认拒绝,再列出允许条件,最后通过 deny 规则收集所有违规细节,确保策略的可审计性。