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() 等内置函数进行预过滤,以提高策略执行效率。
Rego 核心语法结构图 规则 (Rules) 表达式 (Expressions) 标量值 (Scalars) 复合值 (Composites) 引用 (References) 推导 (Comprehensions) 完整规则 部分规则 默认规则 比较 赋值 迭代 数组 对象 集合 图:Rego 核心语法层次结构,展示规则、表达式、标量值、复合值、引用和推导之间的关系 每个类别包含其子类型,形成完整的策略语言基础

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 语言的核心语法,包括规则、表达式、标量值、复合值、引用和推导。这些基础概念是编写高效、可维护的策略即代码的关键。通过掌握这些语法,你可以构建复杂的准入控制策略,实现自动化回滚和安全合规检查。

语法元素 核心作用 典型应用场景
规则 定义策略逻辑 准入控制、授权决策
表达式 构建条件判断 比较、赋值、迭代
标量值 表示单一数据 配置参数、状态标志
复合值 组织复杂数据 列表、映射、集合
引用 访问数据层次 读取输入、规则结果
推导 生成新集合 数据转换、过滤