3. Rego 语言基础:语法入门、规则定义、数据查询与内置函数

Rego 是 OPA 的策略语言,专为在复杂云原生环境中表达可读、可测试的合规规则而设计。本章将系统讲解 Rego 的核心语法、规则定义模式、数据查询方法以及关键内置函数,为后续编写镜像拉取安全扫描与灾备合规策略奠定基础。

3.1 Rego 语法入门

Rego 是一种声明式查询语言,其语法受 Datalog 启发,但扩展了对 JSON 数据的原生支持。策略由 规则(Rules) 和 查询(Queries) 组成。

3.1.1 基本结构与注释

  • 包声明(Package):每个 Rego 文件以 package 关键字开头,用于组织策略命名空间。
  • 导入(Import):使用 import 引入其他包或数据源。
  • 注释:使用 # 进行单行注释。
package compliance.image_scan

import data.registry.images

# 检查镜像是否包含高危漏洞
allow := true {
    images.vulnerabilities[_].severity == "CRITICAL"
}

3.1.2 标量与复合值

类型 示例 说明
字符串 "alpine:3.18" 双引号包裹
数字 2024, 3.14 整数与浮点数
布尔值 true, false 小写
数组 [1, 2, 3] 有序集合,索引从 0 开始
对象 {"name": "nginx", "tag": "latest"} 键值对,键可为字符串或变量
集合 {"critical", "high"} 无序唯一值,使用大括号

3.2 规则定义

规则是 Rego 策略的核心单元,用于定义策略决策。规则可分为 完整规则 和 部分规则。

3.2.1 完整规则

完整规则返回单个布尔值或标量,常用于定义 allow、deny 等最终决策。

package compliance.pull

# 禁止拉取 latest 标签的镜像
deny := "镜像禁止使用 latest 标签" {
    input.image.tag == "latest"
}

3.2.2 部分规则

部分规则返回一个集合或对象,常用于生成报告或中间数据。

package compliance.scan

# 收集所有高危漏洞的 CVE 编号
critical_cves[cve] {
    vuln := input.scan_results[_]
    vuln.severity == "CRITICAL"
    cve := vuln.cve_id
}

3.2.3 默认值与否定

  • 默认值:使用 default 关键字为规则设置初始值,避免未定义错误。
  • 否定:使用 not 关键字表达条件不成立。
default allow := false

allow := true {
    not input.image.vulnerabilities
}
最佳实践:在灾备合规策略中,始终为 allow 或 deny 规则设置 default 值,确保策略在数据缺失时仍能返回明确结果。

3.3 数据查询

Rego 通过 迭代(Iteration) 和 变量绑定 实现强大的数据查询能力。查询通常使用 [_] 遍历数组或集合。

3.3.1 数组与对象遍历

package query.example

# 遍历镜像层,查找包含敏感文件的层
sensitive_layers[digest] {
    layer := input.image.layers[_]
    layer.files[_] == ".env"
    digest := layer.digest
}

3.3.2 多条件组合查询

# 查询所有未通过安全扫描且来自非可信仓库的镜像
blocked_images[image_ref] {
    image := input.images[_]
    image.scan_status == "FAILED"
    image.registry != "harbor.internal.com"
    image_ref := image.reference
}

3.3.3 使用 every 关键字

Rego 1.0 引入 every 关键字,用于表达“所有元素必须满足条件”的语义,提升可读性。

# 所有镜像层必须来自可信构建器
allow := true {
    every layer in input.image.layers {
        layer.builder == "trusted-ci"
    }
}

3.4 内置函数

Rego 提供丰富的内置函数,用于处理字符串、时间、JWT、加密等操作。在镜像拉取安全扫描与灾备合规场景中,以下函数尤为关键。

3.4.1 JWT 解码:io.jwt.decode_sign

该函数用于解码并验证 JWT 签名,常用于校验镜像拉取凭证或灾备令牌的有效性。

package jwt.check

# 解码 JWT 并验证签名
_payload := payload {
    [header, payload, sig] := io.jwt.decode_sign(input., input.cert)
    # 可进一步检查 payload 中的 claims
    payload.iss == "dr-backup-system"
}
参数 类型 说明
string JWT 字符串
cert string 用于验证签名的公钥证书(PEM 格式)
返回值 array [header, payload, signature] 三元组

3.4.2 其他常用内置函数

  • time.now_ns():获取当前时间(纳秒),用于灾备策略中的时间窗口校验。
  • sprintf:格式化字符串,用于生成合规报告消息。
  • regex.match:正则匹配,用于镜像标签或仓库地址的模式校验。
  • object.get:安全访问对象属性,避免未定义错误。
# 灾备合规示例:检查备份时间是否在 24 小时内
backup_compliant := true {
    last_backup := time.now_ns() - input.backup_timestamp
    last_backup < 86400000000000  # 24 小时纳秒值
}

3.5 Rego 策略执行流程

下图展示了从输入数据到策略决策的完整执行流程,帮助理解 Rego 规则在 OPA 中的运行机制。

Rego 策略执行流程 输入数据 input / data Rego 规则引擎 规则匹配与绑定 内置函数 io.jwt.decode_sign 策略决策 allow / deny 合规结果 数据绑定与迭代 变量赋值、数组遍历 策略组合 完整规则 + 部分规则

3.6 常见错误与避坑指南

常见错误 1:未定义变量导致规则静默失败
在 Rego 中,如果查询的变量在数据中不存在,规则会静默返回 undefined,而非报错。务必使用 object.get 或 default 兜底。
常见错误 2:集合与数组混淆
集合 {"a", "b"} 不支持索引访问,而数组 ["a", "b"] 支持。在遍历时注意区分,避免使用 [_] 对集合进行索引。
避坑提示:在编写镜像拉取策略时,JWT 解码函数 io.jwt.decode_sign 要求公钥证书为 PEM 格式。如果证书格式错误,函数会返回 undefined,导致策略无法正确评估。建议在测试阶段使用 trace 函数输出中间结果进行调试。

3.7 本章小结

本章覆盖了 Rego 语言的核心基础:

  • 语法入门:包声明、标量与复合值类型。
  • 规则定义:完整规则与部分规则的区别及默认值使用。
  • 数据查询:遍历、多条件组合与 every 关键字。
  • 内置函数:重点掌握 io.jwt.decode_sign 在凭证校验中的应用。