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 类型体系的层次关系,帮助理解标量类型与复合类型在策略规则中的位置:

Rego 类型体系结构 Rego 数据类型 标量类型 复合类型 特殊值 字符串 数字 布尔 对象 数组 集合 null 说明: • 标量类型是数据的最小单元,不可再分解。 • 复合类型可以嵌套:对象的值可以是数组,数组元素可以是集合。 • null 表示缺失值,常用于规则默认值或未定义状态。 • 集合不支持索引访问,但支持高效的成员检查与集合运算。

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 不支持隐式类型转换,字符串与数字比较需显式处理。