4. Rego 策略入门:编写第一条Rego规则、规则结构(package、import、rule)、输入与输出
在掌握了 OPA 的架构与部署之后,本章将正式进入 Rego 策略语言的核心。Rego 是专为云原生环境设计的声明式查询语言,其语法与逻辑与传统命令式编程有显著差异。本节将引导你从零开始编写第一条 Rego 规则,并深入理解其基本结构:package、import 与 rule,同时理清输入(input)与输出(output)的关系。
4.1 第一条 Rego 规则:从“允许”开始
我们从一个最简单的场景入手:判断某个用户是否被允许访问一个 Kubernetes 命名空间。在 Rego 中,规则本质上是一个条件判断,其结果可以是 true、false 或一组数据。
💡 核心概念: Rego 规则不是“执行”代码,而是“声明”一组事实。OPA 引擎根据输入数据与规则定义,计算出所有满足条件的输出。
以下是一个完整的 Rego 策略文件(example.rego):
package kubernetes.admission
# 默认拒绝所有请求
default allow = false
# 规则:如果用户是 admin,则允许访问
allow {
input.user == "admin"
}
# 规则:如果用户属于 managers 组,且命名空间不是 "production",则允许
allow {
input.group == "managers"
input.namespace != "production"
}
运行方式:
# 使用 OPA 命令行工具进行测试
echo '{"user": "admin", "group": "developers", "namespace": "staging"}' | opa eval --data example.rego --input - "data.kubernetes.admission.allow"
# 输出: true
echo '{"user": "dev-user", "group": "developers", "namespace": "production"}' | opa eval --data example.rego --input - "data.kubernetes.admission.allow"
# 输出: false
4.2 规则结构:package、import、rule
Rego 策略文件由三个核心元素构成,它们共同定义了策略的命名空间、依赖关系与逻辑主体。
| 元素 | 语法 | 作用 | 示例 |
|---|---|---|---|
| package | package <path> |
定义策略的命名空间,避免规则名冲突。所有规则通过 data.<path>.<rule_name> 访问。 |
package kubernetes.admission |
| import | import data.<path>import input |
引入外部数据或输入,简化规则中的引用路径。可以引入整个包或特定规则。 | import data.kubernetes.namespacesimport input.request.user |
| rule | <rule_name> { <conditions> } |
定义一条策略规则。规则体由一系列条件(表达式)组成,所有条件同时满足时规则为真。 | allow { input.user == "admin" } |
📌 最佳实践: 始终在文件顶部声明
package。使用 import 可以大幅提升可读性,尤其是在规则中多次引用同一数据源时。例如:import input.request.object.metadata 后,可以直接使用 metadata.name 而非冗长的全路径。
4.3 输入(input)与输出(output)
Rego 策略的执行模型围绕两个核心数据流展开:输入 和 输出。
- 输入(input):OPA 引擎在每次查询时接收的外部数据。在 Kubernetes 场景中,通常是准入审查请求(AdmissionReview)的 JSON 对象。输入通过
input关键字访问,其结构由调用方决定。 - 输出(output):规则计算后的结果。OPA 通过查询
data.<package>.<rule>返回规则的值。输出可以是布尔值、集合、对象或字符串,取决于规则的定义方式。
以下 SVG 图展示了 OPA 执行 Rego 策略时的数据流与规则结构关系:
4.4 规则类型:完整规则与部分规则
Rego 中的规则根据其返回值类型,可以分为两类:
| 类型 | 定义方式 | 返回值 | 典型用途 |
|---|---|---|---|
| 完整规则(Complete Rule) | rule_name { conditions } |
单个布尔值或标量 | 准入控制(允许/拒绝)、全局开关 |
| 部分规则(Partial Rule) | rule_name[key] { conditions } |
一组键值对或集合 | 生成违规列表、资源白名单 |
完整规则示例:
# 返回单个布尔值
deny {
input.role == "guest"
input.namespace == "production"
}
部分规则示例:
# 返回一个集合,包含所有违规的命名空间
violations[ns] {
ns := input.namespaces[_]
ns.env == "production"
ns.labels.managed != "true"
}
⚠️ 常见误区: 初学者容易混淆完整规则与部分规则的查询方式。完整规则直接返回
data.pkg.rule(如 true),而部分规则需要指定键或使用迭代查询,例如 data.pkg.violations 返回整个集合,data.pkg.violations["production"] 返回特定键的值。
4.5 使用 import 简化规则
当输入数据结构较深时,反复书写完整路径会使规则冗长且难以维护。import 关键字可以解决这个问题。
不使用 import:
package kubernetes.admission
allow {
input.request.object.metadata.labels.environment == "staging"
input.request.object.metadata.namespace == "default"
}
使用 import 优化后:
package kubernetes.admission
import input.request.object.metadata
allow {
metadata.labels.environment == "staging"
metadata.namespace == "default"
}
通过 import,我们将 input.request.object.metadata 映射为局部别名 metadata,规则体变得简洁且易于理解。
4.6 本章小结
- Rego 规则 是声明式条件集合,所有条件同时满足时规则为真。
- package 定义命名空间,import 简化路径引用,rule 封装逻辑主体。
- 输入(input) 是外部传入的 JSON 数据,输出 是规则计算后的结果,通过
data.<package>.<rule>查询。 - 区分完整规则(返回布尔值)与部分规则(返回集合/对象),根据场景选择合适的规则类型。