3. Rego 语言入门(一):Rego 语法基础、关键字、注释、标量类型、复合类型
Rego 是 OPA 的策略语言,专为表达云原生环境下的访问控制与合规规则而设计。本章从零开始,系统讲解 Rego 的语法骨架、关键字体系、注释规范、标量类型与复合类型,为后续编写策略门禁规则打下坚实基础。
3.1 Rego 语法基础与关键字
Rego 是一种声明式查询语言,其核心语法围绕 规则(Rule) 和 查询(Query) 展开。一个 Rego 文件通常由多个规则组成,每条规则定义一组条件与结果。
3.1.1 语法结构
Rego 的基本语法单元是 rule,其通用形式为:
package example
# 一条完整规则
allow := true {
input.method == "GET"
input.path == "/api/v1/health"
}
- package:声明当前规则所属的包,用于组织策略。
- 规则头:
allow := true定义规则名称与返回值。 - 规则体:花括号
{ ... }内的条件表达式,所有条件同时满足(逻辑与)时规则成立。 - 分号:可选,用于分隔同一行的多个表达式,通常省略。
- 赋值:
:=用于局部变量赋值;==用于相等比较。
3.1.2 关键字列表
Rego 保留以下关键字,不可用作变量名或规则名:
| 关键字 | 用途 | 示例 |
|---|---|---|
package |
声明策略包名 | package kubernetes.admission |
import |
导入其他包或数据 | import data.kubernetes.pods |
as |
为导入的包设置别名 | import data.kubernetes as k8s |
true |
布尔真值 | allowed := true |
false |
布尔假值 | denied := false |
null |
空值 | result := null |
not |
逻辑非 | not input.role == "admin" |
with |
模拟数据注入(测试用) | allow with input as {"method": "GET"} |
default |
定义规则的默认值 | default allow := false |
else |
规则分支 | allow := true { ... } else := false |
提示:Rego 中
:= 是赋值运算符,== 是比较运算符,初学者容易混淆。在规则体内,所有条件表达式之间是隐式“与”关系,无需显式写 and。
3.2 注释规范
Rego 支持两种注释方式,均以 # 开头:
- 单行注释:
# 这是一条注释 - 行尾注释:
allow := true # 允许通过
Rego 不支持多行注释(如 /* ... */),所有注释必须逐行以 # 起始。建议在复杂规则前添加注释说明意图:
# 检查请求是否来自内部网络
# 内部网段:10.0.0.0/8, 172.16.0.0/12
internal_request := true {
startswith(input.source_ip, "10.")
}
3.3 标量类型
Rego 的标量类型包括四种基本数据形式:
| 类型 | 说明 | 示例 |
|---|---|---|
| 字符串 | 双引号包裹的文本 | "hello", "namespace: default" |
| 数字 | 整数或浮点数 | 42, 3.14, -1 |
| 布尔 | 真/假 | true, false |
| null | 空值,表示缺失或未定义 | null |
标量类型在 Rego 中不可变,且可以直接用于比较和赋值:
# 字符串拼接
full_name := concat(" ", ["Alice", "Smith"])
# 数字运算
max_attempts := 5
remaining := max_attempts - input.attempts
# 布尔判断
is_admin := input.role == "admin"
3.4 复合类型
Rego 提供三种复合类型:对象(Object)、数组(Array) 和 集合(Set)。它们是构建策略规则的核心数据结构。
3.4.1 对象(Object)
对象是键值对的无序集合,键必须是字符串,值可以是任意类型。语法类似 JSON:
# 定义一个对象
deployment := {
"name": "nginx",
"replicas": 3,
"labels": {"app": "web", "env": "prod"},
"containers": ["nginx:1.21", "sidecar:1.0"]
}
# 访问对象属性
app_name := deployment.name # 点号访问
replicas := deployment["replicas"] # 方括号访问
对象常用于表示 Kubernetes 资源、请求参数或配置数据。
3.4.2 数组(Array)
数组是有序列表,索引从 0 开始,元素类型可以混合:
# 定义数组
ports := [80, 443, 8080]
mixed := ["text", 42, true, null]
# 访问元素
first_port := ports[0] # 80
last_port := ports[count(ports) - 1] # 8080
# 数组遍历(隐式)
allowed_ports := [80, 443]
every p in input.ports {
p in allowed_ports
}
3.4.3 集合(Set)
集合是无序、无重复元素的集合,使用花括号 {} 定义,但元素之间没有冒号:
# 定义集合
admin_users := {"alice", "bob", "charlie"}
allowed_methods := {"GET", "POST", "PUT"}
# 集合成员检查
is_admin := input.user in admin_users
# 集合运算
intersection := admin_users & {"bob", "dave"} # {"bob"}
union := admin_users | {"dave"} # {"alice", "bob", "charlie", "dave"}
difference := admin_users - {"bob"} # {"alice", "charlie"}
注意:集合与对象的语法区别在于:集合元素之间没有冒号,且所有元素必须唯一。集合不支持索引访问,只能通过
in 运算符检查成员关系。
3.5 类型对比与使用场景
| 特性 | 对象 | 数组 | 集合 |
|---|---|---|---|
| 有序性 | 无序 | 有序 | 无序 |
| 键类型 | 字符串 | 整数索引 | 无键 |
| 重复元素 | 键唯一 | 允许重复 | 不允许重复 |
| 访问方式 | obj.key 或 obj["key"] |
arr[0] |
elem in set |
| 典型用途 | 资源属性、配置映射 | 端口列表、容器列表 | 白名单、权限集合 |
3.6 Rego 类型体系结构图
下图展示了 Rego 类型体系的层次关系,帮助理解标量类型与复合类型在策略规则中的位置:
3.7 复合类型嵌套示例
实际策略中,三种复合类型经常嵌套使用。以下是一个模拟 Kubernetes 准入控制的 Rego 规则片段:
package kubernetes.admission
# 输入对象:包含请求的 Deployment 资源
input := {
"kind": "Deployment",
"metadata": {
"name": "web-app",
"namespace": "production",
"labels": {"tier": "frontend"}
},
"spec": {
"replicas": 3,
"template": {
"spec": {
"containers": [
{"name": "nginx", "image": "nginx:1.21", "ports": [80, 443]},
{"name": "sidecar", "image": "sidecar:1.0", "ports": [9090]}
]
}
}
}
}
# 规则:检查所有容器镜像是否来自允许的仓库
allowed_registries := {"docker.io/", "gcr.io/", "quay.io/"}
valid_images := true {
every container in input.spec.template.spec.containers {
startswith(container.image, allowed_registries)
}
}
# 规则:检查副本数是否在允许范围内
valid_replicas := true {
input.spec.replicas >= 2
input.spec.replicas <= 10
}
# 最终准入决策
allow := true {
valid_images
valid_replicas
}
关键点:上述示例中,
input 是一个嵌套对象,containers 是数组,allowed_registries 是集合。通过 every 关键字遍历数组,结合 in 运算符检查集合成员关系,实现了多条件组合策略。
3.8 常见陷阱与最佳实践
- 对象键必须是字符串:数字或布尔值不能直接作为对象键,需转换为字符串。
- 集合不能有重复元素:定义集合时若包含重复值,Rego 会自动去重。
- 数组越界访问:访问不存在的数组索引会导致规则不成立(而非报错),需用
count()检查长度。 - 变量未定义:使用未赋值的变量会导致规则失败,建议用
default关键字提供回退值。 - 类型转换:Rego 不支持隐式类型转换,字符串与数字比较需显式处理。