3. OPA 与 Rego 语言入门
OPA(Open Policy Agent)是一个开源的、通用的策略引擎,它将策略从软件中解耦出来。Rego 是 OPA 的原生查询语言,专门用于定义和查询策略。理解 OPA 的架构和 Rego 的语法是构建基线核验策略的基础。
3.1 OPA 架构概述
OPA 采用客户端-服务器(C/S)架构,核心组件包括策略引擎、数据文档和查询接口。它不直接管理资源,而是通过“决策请求”来评估策略。
架构要点:OPA 将策略决策与策略执行分离。外部系统(如 Kubernetes API Server)发送查询请求,OPA 引擎结合 Rego 规则和数据文档做出决策,返回 allow/deny 结果。
3.2 Rego 语言基本语法
Rego 是一种声明式查询语言,灵感来源于 Datalog。它专注于描述“策略是什么”,而不是“如何执行策略”。
3.2.1 基础规则与赋值
package example.auth
# 常量赋值
default allow = false
# 规则定义:如果用户是 admin,则允许访问
allow {
input.user == "admin"
}
# 使用变量
has_permission {
user := input.user
user != ""
}
3.2.2 迭代与集合
package example.roles
# 定义用户角色数据
user_roles := {
"alice": ["admin", "dev"],
"bob": ["dev"],
"charlie": ["viewer"]
}
# 规则:检查用户是否拥有特定角色
is_admin {
roles := user_roles[input.user]
"admin" == roles[_]
}
# 使用 some 关键字迭代
has_role(role) {
some r
r := user_roles[input.user][_]
r == role
}
3.2.3 规则与查询
在 Rego 中,规则定义策略逻辑,查询用于评估规则并获取结果。
| 概念 | 描述 | 示例 |
|---|---|---|
| 完整规则 | 返回单个值(布尔、字符串、数字) | allow := true { ... } |
| 部分规则 | 返回一组值(集合、对象) | violations[msg] { ... } |
| 默认规则 | 为规则提供默认值,避免未定义 | default allow = false |
| 查询 | 在 OPA 中执行,返回规则结果 | data.example.auth.allow |
查询示例:使用
opa eval 命令或 REST API 查询规则结果。
# 命令行查询
opa eval --data policy.rego --input input.json "data.example.auth.allow"
# REST API 查询
curl -X POST http://localhost:8181/v1/data/example/auth/allow \
-H "Content-Type: application/json" \
-d '{"input": {"user": "admin"}}'
3.3 数据与文档模型
OPA 将外部数据视为文档,这些文档被加载到 OPA 的内存中,形成分层的数据树。Rego 规则通过 data 和 input 关键字访问这些文档。
3.3.1 数据文档模型
数据文档是静态的、全局的,通常由外部系统通过 API 推送或拉取。它们被组织成层次结构,类似于 JSON 对象。
# 数据文档示例 (data.cluster)
{
"cluster": {
"name": "production",
"namespaces": ["default", "kube-system", "monitoring"],
"nodes": [
{"name": "node-1", "labels": {"env": "prod"}},
{"name": "node-2", "labels": {"env": "prod"}}
]
}
}
# Rego 规则访问数据
allowed_namespace {
data.cluster.namespaces[_] == input.namespace
}
3.3.2 输入文档模型
输入文档是临时的、请求级别的,由外部系统在每次查询时提供。它描述了需要被评估的当前请求上下文。
# 输入文档示例 (input)
{
"user": "alice",
"action": "create",
"resource": {
"kind": "Pod",
"name": "nginx-pod",
"namespace": "default"
}
}
# Rego 规则检查输入
deny[msg] {
input.action == "delete"
input.resource.kind == "Namespace"
msg := "不允许删除命名空间"
}
3.3.3 文档模型对比
| 特性 | 数据文档 (data) | 输入文档 (input) |
|---|---|---|
| 生命周期 | 持久化,由外部系统管理 | 临时,每次查询时创建 |
| 作用域 | 全局,所有规则可见 | 请求级别,仅当前查询可见 |
| 更新方式 | 通过 OPA API 推送或拉取 | 由查询请求的 body 提供 |
| 典型用途 | 集群状态、角色定义、白名单 | 用户身份、操作类型、资源详情 |
注意:在 Gatekeeper 中,
input 文档由准入 Webhook 请求自动生成,包含 review、parameters 等字段。不要手动构造 input,而是通过约束模板中的 violation 规则来访问。
3.4 综合示例:基线核验策略
以下是一个简单的 Rego 规则,用于核验 Pod 是否设置了资源限制,这是基线安全策略的一部分。
package k8sbaseline
# 默认允许
default deny = false
# 规则:如果 Pod 没有设置 resources.limits,则拒绝
deny[msg] {
input.review.kind.kind == "Pod"
container := input.review.object.spec.containers[_]
not container.resources.limits
msg := sprintf("容器 %v 必须设置资源限制", [container.name])
}
# 规则:如果 Pod 设置了 privileged 安全上下文,则拒绝
deny[msg] {
input.review.kind.kind == "Pod"
container := input.review.object.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("容器 %v 不允许使用特权模式", [container.name])
}
实践建议:在编写 Rego 规则时,始终从
input 文档入手,使用 opa fmt 格式化代码,并通过 opa test 编写单元测试来验证规则的正确性。