4. Rego 语言基础(一):Rego 语法入门、规则与表达式、数据查询与迭代

Rego 是 OPA 的专用策略语言,专为在复杂 JSON/YAML 数据上进行声明式查询与策略评估而设计。理解 Rego 的语法核心——规则、表达式与迭代机制——是编写 Gatekeeper 约束模板的前提。本章从零构建 Rego 的认知模型,重点覆盖其声明式逻辑、规则结构、常用表达式以及数据查询的迭代模式。

4.1 Rego 的声明式逻辑模型

Rego 不是命令式语言(如 Python、Go),而是基于 Datalog 的声明式逻辑语言。你只需描述策略的“是什么”,而非“如何计算”。OPA 引擎会自动搜索所有可能的数据绑定,使规则成立。

特性 命令式语言(Python/Go) 声明式语言(Rego)
核心思维 控制流 + 状态变更 逻辑断言 + 数据关系
变量赋值 可多次赋值,可变 一次赋值,不可变(逻辑绑定)
循环 for/while 显式迭代 隐式迭代(集合推导)
条件分支 if/else 控制流 规则体中的逻辑与/或
错误处理 try/except 规则未定义(undefined)
关键认知:在 Rego 中,变量一旦被绑定,就不能在同一规则体内重新赋值。这种“不可变性”是避免策略副作用的核心保障。

4.2 规则(Rules)基础

Rego 中的规则是策略的基本单元。每条规则定义了一个逻辑断言:当规则体中的所有条件都为真时,规则头部的变量或文档就被视为“真”或“已定义”。

4.2.1 规则结构

# 完整规则:头部 + 体
allow_request if {
    input.method == "GET"
    input.path == "/healthz"
}

# 简化规则(单行)
allow_request if input.method == "GET"

# 默认规则(防止未定义)
default allow_request := false
  • 规则头:allow_request 是规则名,可带参数。
  • 规则体:花括号 { } 内的逻辑表达式,所有表达式必须同时为真(逻辑与)。
  • if 关键字:Rego v0.48+ 推荐使用 if 提升可读性。
  • default:为规则提供兜底值,避免未定义导致查询失败。

4.2.2 完整规则与部分规则

类型 语法 返回值 示例
完整规则 r := value { ... } 单个值(布尔、字符串、数字) allowed := true { ... }
部分规则 r[key] = value { ... } 键值对集合(对象) ports[service] = port { ... }
集合规则 r contains item { ... } 无序集合(数组去重) violations contains msg { ... }
# 完整规则:返回布尔值
is_admin if input.user.role == "admin"

# 部分规则:返回对象 { "nginx": 80, "redis": 6379 }
service_ports[service] := port {
    some service in input.services
    port := service.port
}

# 集合规则:返回违规消息集合
violations contains msg {
    input.containers[_].image == "latest"
    msg := "禁止使用 latest 标签"
}

4.3 表达式与操作符

Rego 的表达式覆盖比较、逻辑、算术、集合操作和模式匹配。所有表达式都返回布尔值或绑定变量。

4.3.1 比较与逻辑操作符

类别 操作符 说明
比较 == != < > <= >= 严格相等/不等,数值比较
逻辑 not and or 逻辑非、与、或(and 通常用换行隐式表示)
成员 in _ 集合/数组/对象成员检查;_ 表示匿名变量
赋值 := = := 局部赋值;= 统一(声明式绑定)
# 比较与逻辑组合
deny if {
    input.replicas > 5
    not input.namespace == "production"
}

# 成员检查
allowed_regions := {"us-east-1", "eu-west-1"}
valid_region if input.region in allowed_regions

# 匿名变量:忽略具体索引
containers_with_privilege[container_name] {
    container := input.containers[_]
    container.securityContext.privileged == true
    container_name := container.name
}

4.3.2 聚合与内置函数

Rego 提供丰富的内置函数(builtins),用于字符串处理、网络 CIDR 计算、时间比较、JSON 操作等。

# 字符串操作
contains(input.name, "prod")
startswith(input.namespace, "kube-")
endswith(input.image, ":latest")

# 网络 CIDR
cidr_contains("10.0.0.0/8", input.podIP)

# 聚合
count(input.containers) > 1
max([1, 2, 3]) == 3
sum([1, 2, 3]) == 6

# 时间比较
time.now_ns() > time.parse_rfc3339_ns("2025-01-01T00:00:00Z")
注意:Rego 内置函数是纯函数,无副作用。不要期望它们修改外部状态或产生日志输出。

4.4 数据查询与迭代模式

Rego 没有显式的 for 循环,而是通过隐式迭代和集合推导遍历数据。核心机制是 some 关键字和匿名变量 _。

4.4.1 some 关键字:显式绑定迭代变量

# 遍历数组,绑定索引和元素
violations contains msg {
    some i, container in input.containers
    container.image == "nginx:latest"
    msg := sprintf("容器 %s 使用了 latest 标签", [container.name])
}

# 遍历对象键值对
allowed_ports[service_name] := port {
    some service_name, port in input.service_ports
    port < 1024
}

4.4.2 匿名变量 _:忽略索引

# 只关心元素,不关心索引
privileged_containers[container.name] {
    container := input.containers[_]
    container.securityContext.privileged == true
}

4.4.3 集合推导与复合迭代

# 嵌套迭代:遍历所有容器的挂载卷
host_path_mounts contains mount {
    some container in input.containers
    some mount in container.volumeMounts
    mount.hostPath != ""
}

# 使用 every 关键字(Rego v0.48+):全称量化
all_containers_have_readiness_probe if {
    every container in input.containers {
        container.readinessProbe != null
    }
}

4.5 Rego 数据查询流程(SVG 图)

下图展示了 Rego 引擎从输入数据到策略决策的完整查询流程,突出规则体中的迭代与绑定机制。

Rego 数据查询与迭代流程 input / data 规则体 { ... } 隐式迭代引擎 some i, item in array 显式索引 + 元素 array[_] 匿名变量忽略索引 every item in set 全称量化断言 规则输出 迭代引擎自动为每个绑定组合评估规则体,所有成功绑定构成规则输出集合

4.6 实战示例:从 Pod 清单中提取违规容器

结合上述语法,编写一个完整的 Rego 规则,检查 Pod 中是否有容器以 root 用户运行(securityContext.runAsUser == 0)。

package example.pod_security

# 默认:无违规
default has_root_container := false

# 完整规则:返回布尔值
has_root_container if {
    some container in input.containers
    container.securityContext.runAsUser == 0
}

# 集合规则:返回所有 root 容器的名称
root_containers contains container.name {
    some container in input.containers
    container.securityContext.runAsUser == 0
}

# 带消息的违规集合
violations contains msg {
    some container in input.containers
    container.securityContext.runAsUser == 0
    msg := sprintf("容器 '%s' 以 root 用户运行 (UID=0)", [container.name])
}
执行结果示例:当输入 {"containers": [{"name": "web", "securityContext": {"runAsUser": 0}}, {"name": "sidecar", "securityContext": {"runAsUser": 1000}}]} 时:
has_root_container → true
root_containers → ["web"]
violations → ["容器 'web' 以 root 用户运行 (UID=0)"]

4.7 常见陷阱与避坑指南

陷阱 错误示例 正确做法
变量重复赋值 x := 1; x := 2 使用不同变量名或 some 重新绑定
误用 = 代替 := x = 5 在规则头外 局部赋值用 :=,统一用 =
忘记 some 导致未定义 container := input.containers[0] 硬编码 使用 some i, container in input.containers
集合规则返回重复项 集合规则内多次绑定相同值 Rego 自动去重,无需手动处理
未定义规则导致查询失败 直接引用未定义规则 使用 default 提供兜底值

4.8 本章小结

  • Rego 是声明式逻辑语言:描述策略约束,而非计算步骤。
  • 规则三形态:完整规则(单值)、部分规则(对象)、集合规则(集合)。
  • 迭代核心:some 显式绑定、_ 匿名忽略、every 全称量化。
  • 表达式:比较、逻辑、成员检查、内置函数构成规则体。
  • 不可变性:变量一旦绑定,不可在同一规则体内修改。