2. OPA基础回顾:Rego语言核心语法、数据模型、input与data文档、规则与查询

在深入容器网络运维场景之前,必须牢固掌握OPA的策略语言——Rego。Rego是一种为云原生环境设计的声明式查询语言(Datalog变体),其核心思想是将策略定义为对结构化数据的查询。本章将系统梳理Rego的四大支柱:数据模型、核心语法、文档体系以及规则与查询机制。

2.1 Rego数据模型:文档与层级

Rego将一切数据视为文档(Document)。文档是嵌套的JSON/YAML结构,OPA通过input和data两个全局文档来访问外部请求与内部策略数据。

文档 来源 典型内容(容器网络场景)
input API请求时动态传入 待创建的Pod spec、NetworkPolicy、Ingress规则
data OPA加载的静态数据 + 规则输出 集群节点拓扑、已批准的CIDR白名单、服务依赖图

Rego通过点号(.)访问嵌套字段,例如:input.request.operation 或 data.cluster.nodes。所有文档都是不可变的,规则只负责生成新的派生文档。

关键认知: Rego没有“函数”或“方法”,只有规则(Rule)。每条规则定义了一个布尔值、一个集合或一个对象,最终被合并到 data 文档中。

2.2 Rego核心语法速览

Rego语法简洁,但与传统编程语言差异较大。以下是最常用的语法元素:

  • 规则定义:allow { ... } 或 deny[msg] { ... }
  • 变量赋值:x := 42(单次赋值,不可变)
  • 迭代与条件:some i, pod in input.pods; pod.metadata.labels.app == "frontend"
  • 比较与逻辑:==(等于)、!=(不等于)、not(否定)、and(隐式连接)
  • 内置函数:count()、split()、net.cidr_contains()(网络专用)
# 示例:检查Pod是否使用了允许的镜像仓库
allow {
    input.kind == "Pod"
    some container in input.spec.containers
    startswith(container.image, "registry.trusted.io/")
}

# 示例:拒绝暴露非标准端口
deny[msg] {
    input.kind == "Service"
    input.spec.type == "NodePort"
    input.spec.ports[_].port > 30000
    msg := sprintf("端口 %v 超出NodePort允许范围", [input.spec.ports[_].port])
}

2.3 input与data文档的协作模式

在容器网络准入控制中,input 和 data 的协作是策略生效的关键。下图展示了OPA在Kubernetes准入Webhook中的文档流转:

OPA准入控制:input与data文档协作流程 K8s API Server AdmissionReview OPA 策略引擎 Rego 规则评估 input + data input data 文档 集群拓扑 / 白名单 已批准策略基线 data 决策输出 allow / deny / warn AdmissionResponse 协作要点 input 描述“要做什么” data 描述“环境允许什么”

2.4 规则类型与查询模式

Rego规则根据输出类型分为三类,在容器网络策略中各有典型用途:

规则类型 语法特征 容器网络场景示例
布尔规则 allow { ... } 判断Pod是否允许使用hostNetwork
集合规则 deny[msg] { ... } 收集所有违反网络策略的Pod名称
对象规则 config = { ... } { ... } 生成动态的NetworkPolicy规则模板

查询(Query)是Rego的入口。在OPA中,查询可以是简单的data.network.allow,也可以是带参数的data.network.deny[x]。OPA会穷举所有满足条件的绑定,返回完整结果集。

运维提示: 在Kubernetes准入场景中,OPA通常只查询 data.kubernetes.admission 包下的 allow 和 deny 规则。如果 deny 集合非空,则请求被拒绝;否则允许通过。

2.5 完整策略示例:容器网络端口白名单

以下是一个完整的Rego策略,演示了input/data协作、集合规则以及内置网络函数的用法:

package kubernetes.network

# 从data中加载允许的端口范围
allowed_ports := data.cluster.security.allowed_nodeport_range

# 拒绝规则:NodePort Service使用了白名单之外的端口
deny[msg] {
    input.kind == "Service"
    input.spec.type == "NodePort"
    port := input.spec.ports[_].nodePort
    not port_in_range(port, allowed_ports)
    msg := sprintf("端口 %v 不在允许范围 %v 内", [port, allowed_ports])
}

# 辅助规则:检查端口是否在范围内
port_in_range(port, range) {
    range.min <= port
    port <= range.max
}

# 允许规则:所有未触发deny的请求默认允许
allow {
    not deny[_]
}
常见陷阱: Rego中 not deny[_] 的语义是“不存在任何一条deny消息”。如果 deny 规则本身因为数据缺失而求值失败(undefined),not deny[_] 也会失败,导致请求被拒绝。务必确保 data 文档中的引用字段存在默认值。

2.6 调试与测试技巧

在编写容器网络策略时,推荐使用以下方法验证Rego规则:

  • opa eval:直接查询规则,例如 opa eval -d policy.rego -i input.json "data.kubernetes.network.deny"
  • opa test:编写单元测试,使用 data.with 模拟input和data文档
  • trace:通过 opa eval --explain=full 查看规则求值的完整路径