2. OPA 基础回顾:Rego 语言核心语法、策略包结构、数据文档与查询
在深入 OPA 与日志平台的集成测试之前,必须牢固掌握 Rego 语言的基础。Rego 是 OPA 的原生策略语言,专为在复杂 JSON/YAML 数据上进行声明式查询与规则评估而设计。本节将系统梳理 Rego 的核心语法、策略包结构以及数据文档与查询机制,为后续的权限规则编写与测试奠定基础。
2.1 Rego 语言核心语法
Rego 是一种声明式查询语言,其语法受 Datalog 启发,但扩展了对现代数据格式(如 JSON)的原生支持。理解其核心语法是编写有效策略的前提。
2.1.1 规则(Rules)
规则是 Rego 的基本构建单元。一个规则定义了一个决策或一个值。规则分为完整规则和部分规则。
- 完整规则:返回单个值(布尔值、字符串、数字等)。
- 部分规则:返回一个集合或对象,通常用于定义一组允许的操作或资源。
# 完整规则:判断用户是否为管理员
default is_admin = false
is_admin {
input.user.role == "admin"
}
# 部分规则:定义允许的操作集合
allowed_actions[action] {
action := input.user.actions[_]
action != "delete"
}
2.1.2 迭代与变量
Rego 使用 some 关键字和匿名变量 _ 进行迭代。变量在规则体中被赋值,且一旦赋值即不可变。
some i:声明一个局部变量,用于遍历数组或集合。_:匿名变量,用于忽略不需要的值。:=:赋值操作符,用于将值绑定到变量。
# 遍历用户角色列表,检查是否包含 "auditor"
has_auditor_role {
some role
input.user.roles[role] == "auditor"
}
# 使用匿名变量忽略索引
any_admin {
input.users[_].role == "admin"
}
2.1.3 内置函数与聚合
Rego 提供了丰富的内置函数,用于字符串处理、集合操作、时间比较等。聚合函数如 count、sum、max 等常用于统计与校验。
| 函数类别 | 示例函数 | 说明 |
|---|---|---|
| 字符串 | startswith(str, prefix) |
检查字符串是否以指定前缀开头 |
| 集合 | union(set1, set2) |
返回两个集合的并集 |
| 时间 | time.now_ns() |
返回当前时间的纳秒级时间戳 |
| 聚合 | count(array) |
返回数组或集合中的元素数量 |
# 使用内置函数校验日志来源
valid_log_source {
startswith(input.log.source, "app-")
}
# 聚合:检查违规操作数量是否超过阈值
violation_count := count([op | op := input.operations[_]; op.status == "violation"])
threshold_exceeded {
violation_count > 5
}
2.2 策略包结构
策略包(Package)是 Rego 中组织策略的基本单元,类似于其他语言中的命名空间。一个包可以包含多个规则、导入语句以及元数据注释。合理的包结构有助于策略的模块化、复用与测试。
2.2.1 包声明与导入
每个 Rego 文件以 package 关键字开头,声明该文件所属的包。通过 import 关键字可以引入其他包中的规则或数据。
package platform.logs.access_control
import data.platform.logs.common
import future.keywords.if
import future.keywords.in
# 使用导入的规则
allow if {
common.is_authenticated(input.user)
input.action in ["read", "write"]
}
2.2.2 推荐的包组织方式
在日志平台权限规则测试场景中,建议按功能模块或资源类型组织包结构,以提升可维护性。
| 包路径 | 职责 | 包含规则示例 |
|---|---|---|
platform.logs.auth |
用户认证与身份校验 | is_authenticated, _valid |
platform.logs.rbac |
基于角色的访问控制 | allowed_roles, user_has_role |
platform.logs.audit |
审计日志过滤与告警 | sensitive_operation, audit_required |
platform.logs.common |
通用工具与辅助规则 | is_internal_ip, time_in_window |
2.2.3 元数据注释
Rego 支持使用 # METADATA 注释块为规则添加描述、标题、自定义属性等,便于生成文档和策略审计。
# METADATA
# title: 允许管理员读取审计日志
# description: 仅当用户角色为 admin 且操作类型为 read 时允许访问审计日志
# custom:
# severity: high
allow_audit_log_read {
input.user.role == "admin"
input.operation == "read"
input.resource.type == "audit_log"
}
main 规则作为该包的入口点,便于 OPA 查询时统一调用。
2.3 数据文档与查询
OPA 将输入数据(input)和外部数据(data)视为文档(Documents)。理解数据文档的结构与查询方式是编写正确策略的关键。
2.3.1 输入文档(input)
input 是每次策略评估时由调用方传入的临时数据,通常包含请求上下文,如用户信息、操作类型、目标资源等。
# 典型的 input 结构(JSON)
{
"user": {
"id": "user-001",
"role": "viewer",
"department": "security"
},
"operation": "read",
"resource": {
"type": "log_stream",
"id": "stream-prod-01",
"tags": ["production", "critical"]
},
"timestamp": "2025-03-21T10:00:00Z"
}
2.3.2 数据文档(data)
data 是 OPA 加载的外部数据,可以是静态 JSON 文件、从外部服务拉取的策略数据,或其他 Rego 包中定义的规则结果。数据文档是全局的,可在所有包中引用。
# 外部数据示例:角色-权限映射表(roles.json)
{
"roles": {
"admin": ["read", "write", "delete", "manage"],
"viewer": ["read"],
"auditor": ["read", "export"]
}
}
# 在 Rego 中引用 data 文档
allowed_operations[op] {
op := data.roles[input.user.role][_]
}
2.3.3 查询与结果
OPA 的查询分为完整规则查询和部分规则查询。完整规则返回布尔值或具体值;部分规则返回集合或对象。查询结果通过 REST API 或 Go/Java SDK 获取。
| 查询类型 | Rego 规则示例 | 查询路径 | 返回结果示例 |
|---|---|---|---|
| 完整规则 | allow { ... } |
/v1/data/platform/logs/access_control/allow |
{"result": true} |
| 部分规则 | allowed_actions[action] { ... } |
/v1/data/platform/logs/access_control/allowed_actions |
{"result": ["read", "write"]} |
| 数据文档 | data.roles |
/v1/data/roles |
{"result": {"admin": [...], "viewer": [...]}} |
input 文档通常由 API 网关或日志采集器动态构造,而 data 文档中的角色映射、资源标签等静态数据应定期从配置中心同步,避免策略评估时数据不一致。
2.4 Rego 策略执行流程示意图
下图展示了从输入请求到策略评估再到决策输出的完整流程,帮助理解 Rego 规则在 OPA 中的执行路径。
2.5 数据文档与查询关系图
为了更清晰地展示 input、data 与 Rego 规则之间的数据流动关系,下图以日志平台权限校验为例,呈现了典型的数据查询路径。
input 与 data 的组合。在日志平台权限规则测试中,测试用例应覆盖不同的 input 组合(如不同角色、不同操作)以及 data 的边界情况(如空角色映射、缺失标签),以确保策略的健壮性。