3. OPA 基础回顾:Rego 语言入门、Policy 结构、Data 与 Input 概念

在深入 Helm 与 OPA 集成之前,必须牢固掌握 OPA 的核心——Rego 语言。Rego 是一种为云原生策略引擎设计的声明式查询语言,其设计灵感来源于 Datalog。本章将快速梳理 Rego 的核心概念,包括策略结构、data 与 input 的区分,以及如何编写可测试的策略规则。

3.1 Rego 语言核心特征

特征 说明 与通用语言(如 Python/Go)的差异
声明式 只描述“策略应该是什么”,不描述“如何执行” 无循环、无变量赋值(变量只绑定一次)
基于 Datalog 规则由一组逻辑子句组成,支持递归与聚合 无副作用,所有规则都是纯函数
嵌套对象查询 使用点号(.)和方括号([])访问嵌套 JSON 类似 JavaScript 对象路径,但不可变
内置聚合 count、sum、any、all 等 无需外部库,原生支持集合操作
关键记忆点: Rego 中没有“if-else”语句,而是通过规则匹配和默认值(default)实现条件分支。所有规则要么为真(true),要么未定义(undefined),要么返回具体值。

3.2 Policy 结构:包、规则与元数据

一个标准的 OPA 策略文件(.rego)由三个基本层次构成:

  1. 包声明(Package):定义策略的命名空间,避免规则冲突。
  2. 导入语句(Import):引入 data 或 input 中的子路径,简化规则书写。
  3. 规则定义(Rule):核心逻辑,分为完整规则(返回单个值)和部分规则(返回集合)。
package kubernetes.admission

import data.kubernetes.namespaces

# 完整规则:返回布尔值
default allow = false

# 部分规则:返回一个集合
violations[msg] {
    input.request.kind.kind == "Pod"
    not input.request.object.metadata.namespace == "default"
    msg := sprintf("Pod %v 不允许部署在非 default 命名空间", [input.request.object.metadata.name])
}
规则类型速查:
  • 完整规则:allow = true { ... } —— 返回一个标量值。
  • 部分规则:violations[msg] { ... } —— 返回一个集合(数组或对象)。
  • 函数规则:greet(name) = "hello " + name —— 带参数的规则,可复用。

3.3 Data 与 Input 概念

这是 OPA 策略执行中最容易混淆的两个概念。理解它们的区别是编写正确策略的前提。

维度 data input
来源 外部加载的静态/动态数据(如集群资源清单、配置 JSON) 每次查询时由调用方传入的请求上下文
生命周期 持久化,跨多次查询保持不变(除非重新加载) 瞬态,仅存在于单次查询中
典型用途 白名单、黑名单、命名空间元数据、RBAC 规则 准入 Webhook 请求体、API 调用参数、用户身份
访问方式 data.<package>.<path> input.<path>
常见陷阱: 在 Helm 集成场景中,data 通常用于存储集群的基线配置(如允许的镜像仓库列表),而 input 则代表 Helm 渲染后的资源对象。切勿将 input 中的字段误认为 data 中的全局变量,否则会导致策略在不同环境下行为不一致。

3.4 Rego 策略执行流程(SVG 图)

下图展示了 OPA 在接收到查询请求后,如何将 input 与 data 结合,通过规则引擎生成决策结果。

Rego 策略执行流程 Input (请求上下文) Data (外部数据源) Rego 规则引擎 包 + 规则 + 内置函数 决策结果 Input 每次查询不同 Data 跨查询共享 结果:allow / deny 或 violations 集合

3.5 编写第一个 Rego 策略:准入控制示例

以下是一个完整的 Kubernetes 准入控制策略,用于检查 Deployment 是否设置了资源限制。该策略同时使用了 input(请求对象)和 data(全局配置)。

package kubernetes.resources

import data.kubernetes.allowed_registries

# 默认拒绝
default deny = false

# 规则1:检查资源限制
deny {
    input.request.kind.kind == "Deployment"
    container := input.request.object.spec.template.spec.containers[_]
    not container.resources.limits
    not container.resources.requests
}

# 规则2:检查镜像来源
deny {
    input.request.kind.kind == "Deployment"
    container := input.request.object.spec.template.spec.containers[_]
    image := container.image
    not startswith(image, data.kubernetes.allowed_registries[_])
}

# 规则3:生成详细违规信息
violations[msg] {
    deny
    msg := sprintf("Deployment %v 违反策略:%v", [
        input.request.object.metadata.name,
        "缺少资源限制或镜像来源不在白名单中"
    ])
}
测试策略: 使用 opa eval 命令可以快速验证策略逻辑:
# 模拟 input 文件
opa eval --data policy.rego --input input.json "data.kubernetes.resources.deny"

# 查看所有违规
opa eval --data policy.rego --input input.json "data.kubernetes.resources.violations"

3.6 Rego 常见模式与避坑指南

  • 迭代模式:使用 some i 或 _ 通配符遍历数组/集合。例如 container := input.containers[_]。
  • 否定模式:使用 not 关键字检查条件不成立。注意 not 只能用于规则体内部,不能用于顶层。
  • 聚合模式:count(violations) > 0 判断是否有违规,all(some_condition) 检查所有元素是否满足条件。
  • 避坑:变量作用域:Rego 中变量一旦绑定就不能重新赋值。如果需要“或”逻辑,使用多条同名规则(OR 语义)。
  • 避坑:未定义 vs false:规则未定义(undefined)不等于 false。在策略中应显式使用 default 声明默认值。

3.7 本章小结

通过本章回顾,你应该能够:

  • 区分 Rego 中的完整规则与部分规则。
  • 明确 data 与 input 在策略执行中的角色差异。
  • 编写基础的 Kubernetes 准入控制策略。
  • 使用 opa eval 进行本地策略验证。