4. OPA 策略引擎基础:Rego 语言简介、OPA 架构、数据与查询模型

在云原生安全基线体系中,OPA(Open Policy Agent)是策略决策的核心引擎。本章将深入 OPA 的底层原理,包括其声明式策略语言 Rego、组件化架构以及独特的数据与查询模型,为后续在 Istio 中集成 OPA 进行容量水位巡检奠定基础。

4.1 OPA 整体架构与组件

OPA 采用解耦的“策略决策”与“策略执行”分离架构。它本身不执行任何操作(如拒绝请求、杀死 Pod),而是根据输入和策略数据返回决策结果(Allow/Deny)。

核心架构组件:
  • 服务(Service):对外暴露 HTTP/gRPC 接口,接收来自 Envoy、Kubernete API Server 或其他客户端的查询请求。
  • 策略引擎(Engine):核心决策单元,加载并评估 Rego 策略。
  • 数据存储(Data Store):存储基础策略数据(如白名单、配额、集群状态快照),支持 Bundle 拉取和增量更新。
  • Bundle 系统:将策略和数据打包为 tar.gz 文件,通过 HTTP 或 OCI 分发至 OPA 实例。
  • 决策日志(Decision Logs):记录每一次策略评估的输入、输出和元数据,用于审计和调试。
OPA 组件架构与数据流 OPA 组件架构与数据流 外部客户端 (Envoy / K8s API) 查询请求 OPA Service HTTP / gRPC 策略引擎 Rego 评估 缓存 / 编译 读取数据 数据存储 JSON / Bundle Bundle 系统 加载策略 决策日志 审计记录 决策响应 (Allow/Deny)
架构要点:OPA 不存储业务数据,只存储策略和策略依赖的静态数据(如集群节点数、Pod 资源限制)。业务数据(如当前 CPU 使用率)通过查询请求的 input 字段传入。

4.2 Rego 语言基础

Rego 是 OPA 的声明式查询语言,灵感来源于 Datalog。它专注于“策略即代码”,通过规则(Rules)定义策略逻辑。

4.2.1 核心语法元素

元素 语法示例 说明
规则(Rule) allow { input.role == "admin" } 定义一条策略规则,当条件满足时规则为真。
变量(Variable) user := input.user 使用 := 声明并赋值局部变量。
迭代(Iteration) some i; input.items[i].value > 10 使用 some 关键字遍历集合或数组。
默认值(Default) default allow = false 为规则设置默认值,避免未定义错误。
内置函数(Built-in) count(input.pods) > 5 提供 count、split、time.now_ns 等函数。

4.2.2 完整策略示例:检查 Pod 资源限制

package istio.capacity

# 默认拒绝
default allow = false

# 规则:如果 Pod 设置了 CPU 和内存限制,则允许
allow {
    input.kind == "Pod"
    input.spec.containers[_].resources.limits.cpu
    input.spec.containers[_].resources.limits.memory
}

# 规则:如果 Pod 没有设置限制,生成一条违规消息
violation[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    not container.resources.limits.cpu
    msg := sprintf("容器 %v 未设置 CPU 限制", [container.name])
}

violation[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    not container.resources.limits.memory
    msg := sprintf("容器 %v 未设置内存限制", [container.name])
}
常见陷阱:Rego 中的 _ 是匿名变量,用于忽略不需要的值。在迭代容器时,input.spec.containers[_] 会匹配所有容器,但不会绑定具体索引。如果需要索引,使用 some i; input.spec.containers[i]。

4.3 数据与查询模型

OPA 的数据模型分为三个层次:全局数据(data)、输入数据(input)和规则输出(output)。理解这三者的关系是编写正确策略的前提。

4.3.1 数据层次对比

层次 来源 生命周期 典型内容
data Bundle 加载、API 推送、文件加载 持久化,随 OPA 重启或 Bundle 更新而变 集群节点列表、服务白名单、配额阈值
input 每次查询请求的 JSON 负载 仅当前请求有效 当前 Pod 的 CPU 使用率、请求的 HTTP 方法
规则输出 Rego 规则评估结果 每次查询动态计算 allow、violation、deny 等自定义规则

4.3.2 查询模型:Partial Evaluation 与完整评估

OPA 支持两种查询模式:

  • 完整评估(Full Evaluation):客户端发送完整 input,OPA 返回最终决策(true/false)。适用于实时准入控制。
  • 部分评估(Partial Evaluation):客户端不提供 input,OPA 返回一个“剩余条件”表达式。适用于预计算和策略编译优化。
OPA 数据与查询模型 OPA 数据与查询模型 data 层(全局数据) 集群节点: [n1, n2, n3] 配额: {cpu_max: 4, mem_max: 8Gi} 白名单: ["svc-a", "svc-b"] input 层(请求数据) kind: "Pod" metadata.name: "nginx-xxx" spec.containers[0].resources 规则输出层 allow: true / false violation: ["msg1", "msg2"] deny: {reason: "..."} 引用 匹配 查询模式 完整评估: input + data → 最终决策 | 部分评估: data → 剩余条件表达式 决策响应 → 客户端

4.4 策略分发与 Bundle 机制

在生产环境中,OPA 策略通过 Bundle 进行分发。Bundle 是一个包含策略文件(.rego)和数据文件(.json)的压缩包,OPA 客户端定期从远端 Bundle 服务器拉取并加载。

Bundle 结构示例:
bundle.tar.gz
├── .manifest          # 元数据文件,包含版本号、根包名
├── policies/
│   ├── main.rego      # 主策略文件
│   └── utils.rego     # 工具函数
└── data/
    ├── clusters.json  # 集群静态数据
    └── quotas.json    # 配额数据

OPA 支持多种 Bundle 分发方式:

  • HTTP Bundle:OPA 定期轮询 HTTP 端点,下载并解压 Bundle。
  • OCI Bundle:将 Bundle 打包为 OCI 镜像,通过容器镜像仓库分发。
  • Kubernetes ConfigMap:将 Bundle 内容挂载为 ConfigMap,OPA 通过文件系统监听变化。
最佳实践:在 Istio 容量巡检场景中,建议将集群节点规格、Pod 资源配额等静态数据放在 Bundle 的 data/ 目录中,将实时 CPU/内存使用率通过 input 传入。这样既保证了策略的灵活性,又避免了频繁更新 Bundle。

4.5 本章小结