4. Rego 语言入门(二):标量类型、复合类型与引用
在上一节中,我们搭建了 Rego 的运行环境并编写了第一条策略。本节将深入 Rego 的类型系统,这是编写正确、高效策略的基础。Rego 的类型系统分为标量类型和复合类型,理解它们的特性以及如何通过引用访问数据,是掌握 OPA 策略编写的关键一步。
4.1 标量类型:策略中的基本原子
标量类型是 Rego 中最基础的数据单元,它们不可再分。在策略评估中,标量类型常用于定义常量、阈值或简单的状态值。
| 类型 | 描述 | 示例 | 典型用途 |
|---|---|---|---|
| null | 空值,表示不存在或未定义 | null |
表示缺失的配置项或无效的输入 |
| boolean | 布尔值,真或假 | true, false |
策略决策结果(允许/拒绝) |
| number | 数值,包含整数和浮点数 | 42, 3.14 |
端口号、资源配额、时间戳 |
| string | 字符串,由双引号包裹 | "nginx:latest" |
镜像名称、命名空间、标签值 |
避坑提示: Rego 中字符串必须使用双引号
",单引号 ' 不被识别为字符串,会导致解析错误。数字类型在比较时遵循常规数学规则,但需注意浮点数精度问题。
4.2 复合类型:构建策略的数据结构
复合类型允许我们将多个标量值或嵌套的复合值组织在一起。Rego 提供了三种核心复合类型:对象(Object)、数组(Array) 和 集合(Set)。
4.2.1 对象(Object):键值对映射
对象类似于其他语言中的字典或哈希表,由键值对组成。键通常是字符串,值可以是任意类型。对象是 Rego 中最常用的数据结构,用于表示 Kubernetes 资源、配置文档等。
# 定义一个表示容器配置的对象
container_config := {
"image": "nginx:1.21",
"ports": [80, 443],
"security_context": {
"run_as_non_root": true,
"read_only_root_filesystem": true
}
}
4.2.2 数组(Array):有序列表
数组是有序的元素序列,索引从 0 开始。数组中的元素类型可以不一致,但在实际策略中通常保持类型一致以便于遍历。
# 定义一个数组,包含允许的镜像列表
allowed_images := ["nginx:1.21", "redis:6.2", "alpine:3.15"]
# 访问数组元素
first_image := allowed_images[0] # 结果为 "nginx:1.21"
4.2.3 集合(Set):无序唯一元素集
集合是无序的、元素唯一的数据结构。集合中的元素不能重复,且没有索引。集合非常适合用于去重、成员资格检查等场景。
# 定义一个集合,包含禁止使用的端口
blocked_ports := {22, 3389, 3306}
# 检查某个端口是否在集合中
is_blocked := 22 in blocked_ports # 结果为 true
关键区别: 对象使用
{} 且键值对用冒号分隔;数组使用 [];集合也使用 {} 但元素之间没有冒号。集合元素必须是标量类型或复合类型的引用,不能包含变量。
4.3 引用:访问嵌套数据的路径
引用是 Rego 中访问数据的方式,类似于文件系统中的路径。通过引用,我们可以从复杂的嵌套结构中提取所需的值。引用使用点号 . 或方括号 [] 进行导航。
# 假设输入数据如下
input := {
"kind": "Pod",
"metadata": {
"name": "my-pod",
"labels": {"app": "frontend", "env": "prod"}
},
"spec": {
"containers": [
{"name": "web", "image": "nginx:latest"},
{"name": "sidecar", "image": "fluentd:latest"}
]
}
}
# 引用示例
pod_name := input.metadata.name # 结果为 "my-pod"
app_label := input.metadata.labels["app"] # 结果为 "frontend"
first_container_image := input.spec.containers[0].image # 结果为 "nginx:latest"
常见错误: 当引用的键或索引不存在时,Rego 不会抛出异常,而是返回
undefined。这可能导致策略意外通过或拒绝。建议在引用前使用 in 操作符或默认值进行安全检查。
4.4 类型系统全景图
下面的 SVG 图展示了 Rego 类型系统的完整层次结构,以及标量类型与复合类型之间的关系。
4.5 类型操作与模式匹配
Rego 支持通过模式匹配来解构复合类型,这在编写复杂策略时非常有用。以下是一些常见的操作模式:
- 对象解构: 使用
some key, value遍历对象键值对 - 数组迭代: 使用
some i获取索引,或直接使用in检查成员 - 集合操作: 支持并集
&、交集|、差集-操作
# 对象遍历示例
some image_name, image_config in input.images
# 检查所有容器镜像是否来自可信仓库
all_trusted := all(container in input.spec.containers | startswith(container.image, "registry.example.com/"))
# 集合运算示例
allowed_ports := {80, 443, 8080}
exposed_ports := {80, 22, 443}
violation_ports := exposed_ports - allowed_ports # 结果为 {22}
4.6 引用与默认值
在实际策略中,输入数据可能不完整。使用 default 关键字可以为引用设置默认值,避免因数据缺失导致策略行为异常。
# 为可能缺失的字段设置默认值
default pod_security_level := "low"
# 如果 input.metadata.annotations["security-level"] 存在,则使用其值
pod_security_level := input.metadata.annotations["security-level"] {
input.metadata.annotations["security-level"]
}
# 策略中使用默认值
deny[msg] {
pod_security_level == "low"
msg := "Pod 安全等级为低,拒绝部署"
}
最佳实践: 在编写生产级策略时,建议为所有可能缺失的引用设置默认值,或者使用
in 操作符先检查键是否存在。这可以避免因输入数据变化导致的策略意外失效。