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中的文档流转:
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查看规则求值的完整路径