第十五章:OPA — 通用策略引擎
“策略应该与代码分离,就像数据应该与逻辑分离一样。”
mindmap
root((OPA))
核心概念
Policy
Data
Query
Decision
Rego 语言
规则
函数
集合操作
测试
部署模式
Sidecar
独立服务
库模式
WASM
应用场景
K8s Admission
API Gateway
微服务授权
Terraform
15.1 OPA 是什么
OPA(Open Policy Agent)是 CNCF 毕业项目,提供通用的策略引擎。它将策略决策从应用代码中解耦出来。
OPA 工作流程:
┌──────────┐ ┌──────────────────┐ ┌──────────┐
│ 应用 │───▶│ OPA │◀───│ 策略 │
│ │ │ │ │ (Rego) │
│ "Alice能 │ │ ┌────────────┐ │ │ │
│ 删除X吗?"│ │ │ 策略评估 │ │ │ allow if │
│ │ │ │ 引擎 │ │ │ ... │
│ │◀───│ └────────────┘ │ │ │
│ 允许/拒绝 │ │ ▲ │ └──────────┘
└──────────┘ │ │ │
│ ┌─────┴──────┐ │
│ │ 数据 │ │
│ │ (JSON) │ │
│ └────────────┘ │
└──────────────────┘
15.2 Rego 语言
Rego 是 OPA 的策略语言,声明式风格:
基本规则
package authz
import rego.v1
# 默认拒绝
default allow := false
# 管理员允许所有操作
allow if {
input.user.role == "admin"
}
# 用户可以读取自己的数据
allow if {
input.action == "read"
input.resource.owner == input.user.id
}
# 编辑者可以编辑非机密文档
allow if {
input.action == "edit"
input.user.role == "editor"
input.resource.classification != "confidential"
}
输入和查询
// 输入(input)
{
"user": {
"id": "alice",
"role": "editor",
"department": "engineering"
},
"action": "edit",
"resource": {
"type": "document",
"id": "doc-123",
"owner": "bob",
"classification": "internal"
}
}
// 查询:data.authz.allow → true
高级特性
package authz
import rego.v1
# 集合推导:获取用户的所有权限
user_permissions contains perm if {
some role in input.user.roles
some perm in data.role_permissions[role]
}
# 函数
is_owner(resource) if {
resource.owner == input.user.id
}
# 条件组合
allow if {
input.action == "delete"
is_owner(input.resource)
input.resource.status != "archived"
}
# 基于时间的策略
allow if {
input.action == "access"
time.weekday(time.now_ns()) in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
hour := time.clock(time.now_ns())[0]
hour >= 9
hour < 18
}
Rego 测试
package authz_test
import rego.v1
import data.authz
test_admin_allowed if {
authz.allow with input as {
"user": {"role": "admin"},
"action": "delete",
"resource": {"id": "doc-1"}
}
}
test_viewer_cannot_delete if {
not authz.allow with input as {
"user": {"role": "viewer"},
"action": "delete",
"resource": {"id": "doc-1"}
}
}
test_owner_can_read if {
authz.allow with input as {
"user": {"id": "alice", "role": "viewer"},
"action": "read",
"resource": {"owner": "alice"}
}
}
# 运行测试
opa test . -v
15.3 OPA 部署模式
模式 |
描述 |
延迟 |
适用场景 |
|---|---|---|---|
Sidecar |
与应用同 Pod |
极低(本地) |
Kubernetes 微服务 |
独立服务 |
独立部署的 OPA 服务 |
低(网络) |
集中策略管理 |
库模式 |
嵌入应用进程 |
极低 |
Go 应用 |
WASM |
编译为 WebAssembly |
极低 |
边缘计算、浏览器 |
Sidecar 模式:
┌─────────────────────────────────┐
│ Kubernetes Pod │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 应用 │ │ OPA Sidecar │ │
│ │ 容器 │──▶│ 容器 │ │
│ │ │ │ localhost │ │
│ └──────────┘ └──────────────┘ │
└─────────────────────────────────┘
15.4 应用场景
Kubernetes Admission Control(Gatekeeper)
# 禁止使用 latest 标签的镜像
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowedTags
metadata:
name: no-latest-tag
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
tags: ["latest"]
API Gateway 授权
package gateway
import rego.v1
default allow := false
# 公开端点无需认证
allow if {
input.path in ["/health", "/metrics", "/docs"]
}
# 已认证用户可以访问 API
allow if {
input.token.valid
input.method == "GET"
startswith(input.path, "/api/v1/")
}
# 管理员可以访问管理端点
allow if {
input.token.valid
"admin" in input.token.roles
startswith(input.path, "/admin/")
}
# 速率限制
rate_limit := limit if {
"premium" in input.token.roles
limit := 1000
} else := limit if {
limit := 100
}
Terraform 策略检查(Conftest)
package terraform
import rego.v1
# 禁止公开的 S3 存储桶
deny contains msg if {
some resource in input.resource_changes
resource.type == "aws_s3_bucket"
resource.change.after.acl == "public-read"
msg := sprintf("S3 bucket %s must not be public", [resource.address])
}
# 要求所有资源有标签
deny contains msg if {
some resource in input.resource_changes
not resource.change.after.tags.Environment
msg := sprintf("Resource %s must have Environment tag", [resource.address])
}
15.5 Python 集成
import httpx
from fastapi import FastAPI, Depends, HTTPException, Request
app = FastAPI()
OPA_URL = "http://localhost:8181/v1/data/authz/allow"
async def check_opa_policy(request: Request, user: dict) -> bool:
"""调用 OPA 检查授权"""
opa_input = {
"input": {
"user": user,
"action": request.method.lower(),
"path": request.url.path,
"resource": {
"type": request.url.path.split("/")[2] if len(request.url.path.split("/")) > 2 else None,
}
}
}
async with httpx.AsyncClient() as client:
response = await client.post(OPA_URL, json=opa_input)
result = response.json()
return result.get("result", False)
async def authorize(request: Request, user: dict = Depends(get_current_user)):
"""授权中间件"""
allowed = await check_opa_policy(request, user)
if not allowed:
raise HTTPException(status_code=403, detail="Access denied by policy")
return user
@app.delete("/api/documents/{doc_id}")
async def delete_document(doc_id: str, user: dict = Depends(authorize)):
return {"message": f"Document {doc_id} deleted by {user['id']}"}
15.6 OPA vs OpenFGA
维度 |
OPA |
OpenFGA |
|---|---|---|
模型 |
ABAC(策略驱动) |
ReBAC(关系驱动) |
语言 |
Rego |
DSL |
擅长 |
复杂条件逻辑 |
资源级权限继承 |
数据 |
策略 + JSON 数据 |
关系元组图 |
查询 |
“允许吗?” |
“允许吗?” + “列出所有” |
适用 |
K8s、API 策略、合规 |
文档协作、SaaS 权限 |
组合 |
✅ 可以组合使用 |
✅ 可以组合使用 |
最佳实践:用 OpenFGA 管理”谁与什么资源有什么关系”,用 OPA 管理”在什么条件下允许什么操作”。
15.7 小结
OPA 是 CNCF 毕业的通用策略引擎,将策略从代码中解耦
Rego 是声明式策略语言,支持复杂的条件逻辑
OPA 支持多种部署模式:Sidecar、独立服务、库、WASM
广泛应用于 Kubernetes Admission、API 授权、IaC 合规
OPA 和 OpenFGA 互补:OPA 擅长策略逻辑,OpenFGA 擅长关系管理