# 第十五章:OPA — 通用策略引擎 > "策略应该与代码分离,就像数据应该与逻辑分离一样。" ```{mermaid} 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 的策略语言,声明式风格: ### 基本规则 ```rego 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" } ``` ### 输入和查询 ```json // 输入(input) { "user": { "id": "alice", "role": "editor", "department": "engineering" }, "action": "edit", "resource": { "type": "document", "id": "doc-123", "owner": "bob", "classification": "internal" } } // 查询:data.authz.allow → true ``` ### 高级特性 ```rego 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 测试 ```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"} } } ``` ```bash # 运行测试 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) ```yaml # 禁止使用 latest 标签的镜像 apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sDisallowedTags metadata: name: no-latest-tag spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: tags: ["latest"] ``` ### API Gateway 授权 ```rego 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) ```rego 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 集成 ```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 擅长关系管理