4. Rego 语言核心语法(一):规则、表达式与数据结构
Rego 是 Open Policy Agent (OPA) 的策略语言,专为在云原生环境中表达策略而设计。本章将深入解析 Rego 的核心语法基础,包括规则(Rules)、表达式、标量值、复合值、引用以及数组与对象推导。掌握这些基础是编写高效、可读性强的策略即代码(Policy as Code)的关键。
4.1 规则(Rules):策略的基本单元
在 Rego 中,规则(Rule) 是定义策略逻辑的基本构建块。每条规则本质上是一个条件语句,用于生成一个值(布尔值、集合、对象等)。规则通常由头部(Head)和主体(Body)组成。
- 完整规则(Complete Rule):当主体中的所有表达式都为真时,规则为真。常用于定义布尔策略。
- 部分规则(Partial Rule):通过变量绑定生成一组值,常用于定义集合或对象。
- 默认规则(Default Rule):为规则提供一个默认值,避免未定义错误。
关键概念: 规则是声明式的,Rego 引擎会自动搜索所有满足条件的数据。规则之间可以相互引用,形成策略的层次结构。
4.1.1 规则语法示例
# 完整规则:判断请求是否允许
allow {
input.method == "GET"
input.path == ["api", "v1", "data"]
}
# 部分规则:返回允许的角色列表
allowed_roles[role] {
role := input.user.roles[_]
role != "guest"
}
# 默认规则:默认拒绝
default allow = false
4.2 表达式(Expressions):构建逻辑的基石
表达式是规则主体中的逻辑单元,用于比较、赋值和迭代。Rego 支持多种表达式类型,包括比较表达式、赋值表达式和迭代表达式。
| 表达式类型 | 语法示例 | 说明 |
|---|---|---|
| 比较表达式 | input.role == "admin" |
使用 ==, !=, <, >, <=, >= |
| 赋值表达式 | x := input.user.id |
使用 := 绑定变量 |
| 迭代表达式 | input.items[_] |
使用 [_] 遍历数组或集合 |
| 逻辑表达式 | allow { ... } |
规则主体中的隐式 AND 连接 |
最佳实践: 使用
:= 进行变量赋值,使用 == 进行比较。避免在规则主体中使用 =,因为它可能引起歧义。
4.3 标量值(Scalar Values)与复合值(Composite Values)
Rego 支持多种数据类型,包括标量值和复合值。标量值是单一的值,而复合值可以包含多个值。
4.3.1 标量值
- 字符串(String):
"hello","admin" - 数字(Number):
42,3.14 - 布尔值(Boolean):
true,false - 空值(Null):
null
4.3.2 复合值
| 复合类型 | 语法示例 | 说明 |
|---|---|---|
| 数组(Array) | [1, 2, 3] |
有序列表,索引从 0 开始 |
| 对象(Object) | {"key": "value"} |
键值对集合,键必须是字符串 |
| 集合(Set) | {"a", "b", "c"} |
无序唯一值集合 |
# 复合值示例
allowed_ports := [80, 443, 8080]
metadata := {"name": "nginx", "version": "1.21"}
blocked_ips := {"10.0.0.1", "192.168.1.100"}
4.4 引用(References):访问数据与规则
引用是 Rego 中访问数据、规则和变量的方式。引用使用点号(.)或方括号([])来导航嵌套结构。
- 点号引用:
input.user.role - 方括号引用:
input["user"]["role"] - 变量引用:
data.packages.allow - 通配符引用:
input.items[_]遍历所有元素
常见陷阱: 当键包含特殊字符(如空格或连字符)时,必须使用方括号引用。例如:
input["my-key"]。
4.5 数组与对象推导(Comprehensions)
推导是 Rego 中强大的功能,允许你从现有集合中生成新的数组或对象。推导语法类似于规则,但内联在表达式中。
4.5.1 数组推导
数组推导生成一个新的数组,包含满足条件的元素。
# 从输入中提取所有非空名称
names := [name | name := input.users[_].name; name != ""]
# 生成端口列表,排除低于 1024 的端口
high_ports := [port | port := input.ports[_]; port >= 1024]
4.5.2 对象推导
对象推导生成一个新的对象,键值对由条件决定。
# 将用户列表转换为对象,键为用户名,值为角色
user_roles := {user.name: user.role | user := input.users[_]}
# 过滤并转换对象
active_users := {user.id: user.email | user := input.users[_]; user.active == true}
性能提示: 推导在大型数据集上可能消耗较多资源。建议在推导前使用
count() 或 any() 等内置函数进行预过滤,以提高策略执行效率。
4.6 综合示例:Kubernetes 准入控制策略
以下是一个完整的 Rego 策略示例,用于 Kubernetes 准入控制,展示规则、表达式、引用和推导的综合应用。
package kubernetes.admission
# 默认拒绝
default allow = false
# 允许规则:检查命名空间标签和容器安全上下文
allow {
input.request.kind.kind == "Pod"
input.request.namespace == "production"
input.request.object.spec.containers[_].securityContext.runAsNonRoot == true
input.request.object.spec.containers[_].securityContext.capabilities.drop[_] == "ALL"
}
# 推导:提取所有容器的镜像名称
container_images := [image |
container := input.request.object.spec.containers[_]
image := container.image
]
# 检查是否使用了禁止的镜像标签
deny["镜像标签 'latest' 不被允许"] {
container_images[_] == "*:latest"
}
# 检查是否设置了资源限制
deny["容器缺少资源限制"] {
container := input.request.object.spec.containers[_]
not container.resources.limits
}
实战建议: 在编写准入控制策略时,始终从
default allow = false 开始,确保默认拒绝。然后逐步添加允许规则,减少安全风险。
4.7 本章小结
本章深入介绍了 Rego 语言的核心语法,包括规则、表达式、标量值、复合值、引用和推导。这些基础概念是编写高效、可维护的策略即代码的关键。通过掌握这些语法,你可以构建复杂的准入控制策略,实现自动化回滚和安全合规检查。
| 语法元素 | 核心作用 | 典型应用场景 |
|---|---|---|
| 规则 | 定义策略逻辑 | 准入控制、授权决策 |
| 表达式 | 构建条件判断 | 比较、赋值、迭代 |
| 标量值 | 表示单一数据 | 配置参数、状态标志 |
| 复合值 | 组织复杂数据 | 列表、映射、集合 |
| 引用 | 访问数据层次 | 读取输入、规则结果 |
| 推导 | 生成新集合 | 数据转换、过滤 |