4. Rego 语言基础:Rego 语法、规则、查询、数据模型、内置函数入门

Rego 是 OPA 的策略语言,专为在云原生环境中表达和评估策略而设计。理解 Rego 的核心概念是编写有效准入控制策略的前提。本节将系统性地介绍 Rego 的语法、规则、查询方式、数据模型以及常用内置函数。

4.1 Rego 数据模型:输入与数据

Rego 的策略评估基于两个核心数据源:input 和 data。

数据源 说明 在准入控制中的典型内容
input 当前请求的瞬时数据,由调用方(如 Kubernetes API Server)传入。 AdmissionReview 对象,包含 Deployment 的完整 YAML 定义。
data 持久化的全局数据,通过 OPA 的 Bundle 或外部数据源加载。 白名单镜像列表、允许的命名空间标签、全局安全基线配置。
关键理解: 策略规则通过 input 检查当前请求是否合规,并通过 data 引用全局配置或外部上下文。两者都是 JSON 对象,Rego 使用点号(.)或方括号([])进行访问。

4.2 Rego 语法与规则结构

Rego 规则是策略的基本单元。一个规则由 头部(head) 和 主体(body) 组成。

  • 头部: 定义规则名称和输出值(可选)。
  • 主体: 包含一个或多个表达式,所有表达式必须为 true 规则才成立。
# 规则头部:规则名称为 "allowed_image",输出值为 true 或 false
allowed_image := true {
    # 规则主体:表达式
    input.request.object.spec.template.spec.containers[_].image == "nginx:1.21"
}

Rego 支持两种主要规则类型:

规则类型 语法特征 用途
完整规则 使用 := 或 = 赋值,返回单个值。 定义布尔值、字符串、数字等标量结果。
部分规则 使用 [...] 或 {...} 定义集合/对象。 生成一组结果,如所有违规容器列表。
# 完整规则:返回布尔值
deny[msg] {
    input.request.operation == "CREATE"
    msg := "创建操作需要额外审批"
}

# 部分规则:返回集合
violating_containers[container] {
    container := input.request.object.spec.template.spec.containers[_]
    container.image == "latest"
}

4.3 查询与迭代

Rego 使用 迭代器 遍历数组或对象。最常见的迭代模式是使用下划线 _ 作为通配符索引。

# 遍历所有容器,检查是否使用了 "latest" 标签
violates_latest {
    some container
    container := input.request.object.spec.template.spec.containers[container]
    endswith(container.image, ":latest")
}

# 使用通配符 _ 简化写法
violates_latest_short {
    container := input.request.object.spec.template.spec.containers[_]
    endswith(container.image, ":latest")
}
查询模式: 在准入控制中,通常使用 deny 或 violation 规则名。如果规则返回非空结果,则请求被拒绝。空结果表示通过。

4.4 内置函数入门

Rego 提供丰富的内置函数,用于字符串处理、正则匹配、集合操作、时间比较等。以下是准入控制中最常用的函数:

函数 签名 示例
startswith startswith(str, prefix) startswith(image, "harbor.internal/")
endswith endswith(str, suffix) endswith(image, ":latest")
regex.match regex.match(pattern, str) regex.match("^[a-z0-9-]+$", name)
count count(collection) count(containers) > 1
object.get object.get(obj, key, default) object.get(metadata, "namespace", "default")
sprintf sprintf(format, values) sprintf("容器 %s 使用了禁止镜像", [name])
# 综合示例:检查镜像是否来自允许的仓库
allowed_registries := ["harbor.internal", "docker.io/library"]

deny[msg] {
    container := input.request.object.spec.template.spec.containers[_]
    not startswith(container.image, allowed_registries[_])
    msg := sprintf("容器 %s 的镜像 %s 来自未授权仓库", [container.name, container.image])
}

4.5 Rego 规则评估流程

下图展示了 Rego 引擎在处理准入控制请求时的核心评估路径:

Rego 规则评估流程 Rego 规则评估流程 K8s AdmissionReview 加载 input + data 数据 遍历 deny / violation 规则 规则主体 全部为 true? 是 拒绝请求 + 返回消息 否 允许请求通过 评估要点 • 规则主体所有表达式必须为 true • 使用迭代器遍历数组/对象 • 非空 deny 结果 = 拒绝

4.6 常见模式与避坑指南

  • 默认值处理: 使用 object.get 或 default 关键字避免访问不存在的属性导致规则失败。
  • 迭代器绑定: 在复杂嵌套中,使用 some 关键字显式声明变量,提高可读性。
  • 否定陷阱: not 操作符在集合为空时返回 true,务必确认逻辑意图。
  • 规则覆盖: 同名规则会合并结果,确保不会意外覆盖已有策略。
# 使用 default 处理缺失字段
default allowed := false

allowed := true {
    input.request.object.metadata.labels.team == "platform"
}

# 使用 some 显式迭代
deny[msg] {
    some i
    container := input.request.object.spec.template.spec.containers[i]
    not container.securityContext.readOnlyRootFilesystem
    msg := sprintf("容器 %s 未设置只读根文件系统", [container.name])
}
常见错误: 在 Rego 中,= 用于统一赋值和比较,而 := 仅用于局部变量声明。在规则主体中误用 = 可能导致意外的变量覆盖。