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 引擎在处理准入控制请求时的核心评估路径:
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 中,
= 用于统一赋值和比较,而 := 仅用于局部变量声明。在规则主体中误用 = 可能导致意外的变量覆盖。