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.namespaces
import 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 策略时的数据流与规则结构关系:

Rego 规则结构与输入输出数据流 Rego 规则结构与输入输出数据流 输入 (input) JSON 对象 Rego 策略文件结构 package kubernetes.admission import input.request.object rule: allow { ... } 条件1: input.user == "admin" 输出 true / false 📋 数据流说明 1. OPA 引擎接收外部 JSON 数据作为 input。 2. 引擎加载 Rego 文件,解析 package、import 和 rule 结构。 3. 规则体中的条件逐一求值,所有条件满足时规则为真。 4. 最终通过 data.<package>.<rule> 查询输出结果。

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> 查询。
  • 区分完整规则(返回布尔值)与部分规则(返回集合/对象),根据场景选择合适的规则类型。