2. OPA 基础回顾:Rego 语言核心语法、OPA 数据模型、Input 与 Data 文档、内置函数与聚合

在开始多集群策略迁移之前,必须夯实 OPA 策略引擎的核心基础。本章将系统回顾 Rego 语言的语法结构、OPA 的数据模型、Input/Data 文档的交互机制,以及常用的内置函数与聚合操作。这些知识是后续编写和迁移策略的基石。

2.1 Rego 语言核心语法

Rego 是一种为 OPA 量身定制的声明式查询语言,灵感来源于 Datalog。它专注于“策略即代码”,通过规则(Rule)和查询(Query)来表达策略逻辑。

2.1.1 基本结构与规则

Rego 文件由 包(Package) 和 规则(Rule) 组成。规则是 Rego 的核心,它定义了一个布尔表达式或一个值。

package example.allow

# 默认规则:默认拒绝
default allow = false

# 规则:如果用户是 admin,则允许
allow {
    input.user.role == "admin"
}

# 规则:如果用户是 owner 且资源属于该用户,则允许
allow {
    input.user.role == "owner"
    input.resource.owner == input.user.name
}
关键概念: 在 Rego 中,规则名相同但条件不同的规则之间是 OR 关系。只要有一条规则的条件为真,整个规则集的结果就为真。默认规则 default allow = false 确保在没有规则匹配时返回 false。

2.1.2 迭代与推导

Rego 通过 迭代(Iteration) 和 推导(Comprehension) 来处理集合数据。推导类似于其他语言中的列表/集合/对象推导式。

# 列表推导:获取所有非 admin 用户的名称
non_admin_users := [name | name := input.users[_].name; input.users[_].role != "admin"]

# 对象推导:构建一个用户角色映射
user_roles := {name: role | name := input.users[_].name; role := input.users[_].role}

# 集合推导:获取所有唯一的命名空间
unique_namespaces := {ns | ns := input.deployments[_].namespace}

2.1.3 规则类型对比

规则类型 语法示例 返回值 典型用途
完整规则 allow { ... } 布尔值(true/false) 授权决策、准入控制
部分规则 roles[user] := role { ... } 键值对映射 数据转换、角色映射
函数规则 greet(name) = msg { ... } 自定义返回值 复用逻辑、字符串处理
默认规则 default allow = false 固定值 设置安全默认值

2.2 OPA 数据模型:Input 与 Data 文档

OPA 的策略评估依赖于两个核心文档:Input 和 Data。理解它们的区别是编写正确策略的前提。

  • Input 文档: 每次策略查询时由外部系统(如 Kubernetes API Server)传入的临时数据。它描述了当前请求的上下文,例如用户信息、操作类型、资源对象等。Input 是动态的、一次性的。
  • Data 文档: OPA 内部维护的全局数据缓存。它通常包含集群状态、配置规则、外部数据(如 LDAP 用户组)等。Data 是静态的、持久化的,可以通过 API 或 Bundle 更新。
实战要点: 在 Kubernetes 准入控制场景中,input 包含 AdmissionReview 请求的完整内容(如 Pod 的 YAML 定义),而 data 可以包含集群级别的白名单、命名空间标签、或从外部同步的合规规则库。

2.2.1 数据模型交互示例

# 检查 input 中的用户是否在 data 中的管理员列表中
allow {
    input.user.name == data.admin_users[_]
}

# 检查 input 中的命名空间是否在 data 中的允许列表中
allow {
    input.namespace == data.allowed_namespaces[_]
}

# 使用 data 中的规则进行复杂校验
deny[msg] {
    input.kind == "Deployment"
    not data.allowed_images[input.spec.template.spec.containers[_].image]
    msg := sprintf("镜像 '%s' 不在允许列表中", [input.spec.template.spec.containers[_].image])
}

2.3 内置函数与聚合

OPA 提供了丰富的内置函数,用于字符串处理、正则匹配、时间操作、加密计算等。聚合函数则用于对集合数据进行统计和汇总。

2.3.1 常用内置函数分类

类别 函数示例 说明
字符串 contains(str, substr)
startswith(str, prefix)
regex.match(pattern, str)
字符串包含、前缀匹配、正则匹配
集合/数组 count(collection)
union(s1, s2)
intersection(s1, s2)
计数、并集、交集
对象 object.get(obj, key, default)
object.keys(obj)
安全取值、获取键列表
编码 base64.decode(str)
json.marshal(value)
Base64 解码、JSON 序列化
时间 time.now_ns()
time.parse_rfc3339(str)
获取当前时间、解析时间字符串

2.3.2 聚合操作实战

聚合函数在策略中常用于资源配额校验、权限汇总等场景。

# 统计 input 中所有容器的资源请求总和
total_cpu_requests := sum([c.resources.requests.cpu | c := input.spec.template.spec.containers[_]])

# 检查是否所有容器都设置了资源限制
all_containers_have_limits := all([c.resources.limits | c := input.spec.template.spec.containers[_]])

# 检查是否存在任何特权容器
any_privileged := any([c.securityContext.privileged | c := input.spec.template.spec.containers[_]])

# 聚合规则:如果总 CPU 请求超过 4 核,则拒绝
deny[msg] {
    total_cpu_requests > 4
    msg := sprintf("CPU 请求总量 %.2f 超过限制 4 核", [total_cpu_requests])
}
注意: 聚合函数 sum、all、any 等要求输入必须是集合或数组。如果字段可能缺失,建议使用 object.get 或默认值来避免运行时错误。

2.4 OPA 策略评估流程(SVG 图)

下图展示了 OPA 在一次策略查询中的完整数据流,从外部请求到最终决策的生成过程。

OPA 策略评估流程 OPA 策略评估数据流 外部系统 Input 文档 Data 文档 Rego 策略引擎 规则解析 & 评估 决策输出 传入请求 查询 全局数据 评估结果 说明: Input 文档携带请求上下文(用户、操作、资源),Data 文档提供全局规则与状态。 Rego 引擎将两者结合,通过策略规则计算并输出决策(allow/deny/自定义消息)。

2.5 本章小结