jhawlwut/apparitor
GitHub: jhawlwut/apparitor
apparitor 为 AI Agent 的工具调用、MCP 请求和 Agent 间通信提供统一的授权拦截层,将访问决策委托给组织已有的策略引擎(OpenFGA/Cedar/OPA),填补内容安全扫描无法覆盖的权限校验缺口。
Stars: 0 | Forks: 0
# apparitor
[](https://github.com/jhawlwut/apparitor/actions/workflows/ci.yml)
[](https://github.com/jhawlwut/apparitor/actions/workflows/pip-audit.yml)
[](https://github.com/jhawlwut/apparitor/actions/workflows/ci.yml)
[](LICENSE)
[](pyproject.toml)
**你的 agent 绕过了你现有的授权机制。** apparitor 将它们重新
纳入管控。每个 agent 操作(一次 LLM 工具调用、一个 MCP 请求,或一次 agent 间的
调用)都会在执行 *之前*,与你已信任的策略引擎(OpenFGA、Cedar、OPA)进行核对。
它回答了内容安全层从未提出的问题:这个 agent *被允许* 执行此操作吗?采用供应商中立原则,基于 AuthZEN 1.0 互操作性标准构建,
Apache-2.0。
## 差距
你技术栈中的每个安全层都在检查 agent 操作的 *内容*:提示词
是否越狱,生成的代码是否恶意。但没有一个提出你的安全
模型实际依赖的问题。这个 agent 现在 **被允许** 为该用户、针对
该资源执行此操作吗?
最关键的操作往往是那些看起来无害的操作。一个 agent 读取客户
记录是良性的文本;而读取 *另一个租户的* 记录就是违规。没有任何内容扫描器
能区分它们。区别不在于文本,而在于谁在操作以及他们拥有什么
权限。
```
Agent for alice@acme → read_records(tenant="globex")
│
▼
Safety scanning → "Is this prompt malicious?" → PASS (benign request)
│
▼
??? nothing ??? → "May alice@acme read globex's records?" → NO CHECK
│
▼
Tool executes. Cross-tenant data returned.
```
这个缺失的环节就是授权决策,而且你几乎可以肯定正在运行一个
已经做出这些决策的引擎。它只是没有被连接到 agent 实际操作的地方。apparitor
就是这种连接:它将每个 agent 操作路由到策略决策点(PDP),并将
裁决结果映射到执行点的 `ALLOW` / `BLOCK` / `HUMAN_IN_THE_LOOP` 模型上。
```
Agent for alice@acme → read_records(tenant="globex")
│
▼
Safety scanning (PromptGuard, AlignmentCheck, CodeShield, …) → PASS
│
▼
apparitor ──────────POST /access/v1/evaluation──────▶ Policy engine (OpenFGA / Cedar / OPA / …)
│ │
│ ◀────────────────── { "decision": false } ────────┘
▼
BLOCK: "alice@acme is not authorized to call read_records for tenant globex"
```
## 为什么不自己写检查逻辑呢?
最简单的版本,`if allowed: run()`,在四个方面存在安全漏洞,而这正是 apparitor 旨在
解决的问题:
- **主体是一个 [confused-deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)
陷阱。** 防火墙层看到的是模型输出,而不是经过身份验证的主体。如果从
工具调用中推断 *谁在操作*,agent 就可以指定自己的特权主体。
apparitor 从宿主环境中获取主体,限定在请求范围内(在 MCP 边界,从 *已验证的* OAuth token 获取),绝不从模型输出获取。参见
[身份标识](#identity-who-the-agent-acts-for)。
- **默认故障是 fail-open。** PDP 超时、`5xx` 错误、缺失或非布尔的
`decision`、无法解析的调用:每一个都会导致 `allowed` 为假值,从而被你的 `if` 语句放行。
apparitor 将每一种情况都解决为 BLOCK 或人工审查;不存在 fail-open 选项。参见
[默认 Fail-closed](#fail-closed-by-default)。
- **你需要写四次。** 检查应该位于防火墙、MCP server
以及 agent 间的边界,每一处都有不同的对象和不同的身份来源。
apparitor 是四个适配器背后的单一引擎。
- **agent 应该比其用户受到更多限制。** 为特权用户操作的越狱 agent
绝不能借用该用户的权限。apparitor 可以评估用户的授权 *以及*
agent 自身的权限边界,仅在两者都允许时才继续执行。
这是一项经过单独审计的控制措施,适用于所有引擎。参见
[Level 2](#identity-who-the-agent-acts-for)。
而且你不需要编写任何新策略:它保留在你组织已经编写策略的引擎中,
与你其他授权逻辑所在的位置一起接受审计。
**四个执行点,一个引擎。** 检查运行在技术栈允许你
拦截操作的任何地方:在 agentic 防火墙内(作为
[LlamaFirewall](https://github.com/meta-llama/PurpleLlama/tree/main/LlamaFirewall)
扫描器或 [NeMo Guardrails](https://github.com/NVIDIA/NeMo-Guardrails) 轨道),在
MCP 边界作为 FastMCP server middleware,或在 agent 间边界作为 A2A
executor。相同的引擎,到处都是相同的 fail-closed 语义;只有边界不同。
**一次集成,多个策略引擎。** apparitor 采用
[AuthZEN 1.0](https://openid.net/specs/authorization-interop-spec-1_0.html) 互操作性
标准,因此相同的连接可以触及你已经在编写策略的引擎:
**OpenFGA**(Zanzibar / ReBAC,实验性)、**Cedar**(policy-as-code)和 **OPA /
Rego**,无需重写策略。OPA 和 Cedar 也有跳过
AuthZEN 环节的原生后端。
## 安装说明
apparitor 尚未在 PyPI 上发布;请从源码安装,并固定到某个发布标签:
```
pip install "apparitor @ git+https://github.com/jhawlwut/apparitor@v0.1.1"
```
每个执行点和进程内的 Cedar 后端都是可选的附加项
(`[llamafirewall]`、`[nemo]`、`[fastmcp]`、`[a2a]`、`[cedar]`)。`[llamafirewall]` 会拉取
torch / ML 技术栈;纯安装和所有其他附加项都不会。有关完整的矩阵和每个附加项的安装
命令,请参见
[docs/setup.md](docs/setup.md#installation)。
## 快速入门
选择你技术栈中已有的执行点;它们背后的引擎是相同的。
**在 LlamaFirewall 内部:** 将扫描器指向任何兼容 AuthZEN 的策略决策
点(PDP)并将其绑定到助手角色,以便它在工具调用被分发之前对其进行
拦截。OpenAI、Anthropic 和 LangChain 格式的工具调用会被自动检测和
标准化;未识别的格式将被拦截(fail closed):
```
from llamafirewall import LlamaFirewall, Role
from apparitor import AuthZENScanner, ScannerConfig
# 指向任何兼容 AuthZEN 的 PDP。安全默认设置:fail-closed,TLS-verified。
# Subject 必须是可解析的:设置 config.agent_id,或针对每个请求设置 current_subject。
scanner = AuthZENScanner(config=ScannerConfig(pdp_url="https://pdp.internal", agent_id="travel-bot"))
firewall = LlamaFirewall(scanners={Role.ASSISTANT: [scanner]})
result = await firewall.scan_async(assistant_message) # ALLOW / BLOCK / HUMAN_IN_THE_LOOP
```
每次请求时,提供 agent 实际代理的真实最终用户(推荐使用,而不是静态的
`agent_id`)。参见 [身份标识:agent 代理的对象](#identity-who-the-agent-acts-for)。
相同的 `AuthorizationEngine` 运行在其他三个执行点之后;只是
边界和身份来源不同:
- **NeMo Guardrails 轨道** — `pip install "apparitor[nemo]"`, `NeMoAuthorizationRails`。
注册为自定义操作;该轨道拒绝被拒绝的工具调用,在
NeMo 的映射下遵循 fail-closed。在 [`examples/three-peps/`](examples/three-peps/) 中有演练。
- **FastMCP server middleware** — `pip install "apparitor[fastmcp]"`,
`FastMCPAuthorizationMiddleware`。在工具运行之前在服务器端拦截 `tools/call`、`resources/read` 和 `prompts/get`
;主体是 *已验证的* OAuth `sub`,绝不是
宿主断言。请在你的 auth middleware **之后** 注册它。在
[`examples/gateway/`](examples/gateway/) 中有可用的代理示例。
- **A2A agent executor** — `pip install "apparitor[a2a]"`, `A2AAuthorizationExecutor`。
拦截每个 agent 间的 `agent.invoke`;主体是服务器已验证的
对等方。
每个适配器都有更多选项(列表过滤、双主体边界、针对 hook 的选择退出),记录在其模块的 docstring 中;有关
每个引擎的连接方式,请参见 [docs/setup.md](docs/setup.md)。AuthZEN 客户端和模型是 **无适配器** 的,可以独立使用
— `from apparitor.models import EvaluationRequest` 不需要任何防火墙依赖。
## 身份标识:agent 代理的对象
每个决策都需要一个 **主体:** 你的策略所针对的 principal。apparitor
绝不从模型或工具输出中推断它(那将是一个 [confused
deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem));**宿主** 在
请求范围内提供它。这里有一个包含三个级别的成熟度阶梯:
- **Level 0 — 静态 agent 身份。** 设置 `agent_id`;每次调用都作为该
agent 进行授权。足以应对不依赖于最终用户的策略(*"任何 agent 都不得调用
破坏性工具"*)。
- **Level 1 — 每次请求的真实最终用户(推荐)。** 使用
`subject_scope(Subject(...))` 为 agent 运行绑定经过验证的用户;它会在退出时重置,因此主体不会
泄漏到重用相同任务/事件循环的后续请求中。
- **Level 2 — agentic 权限边界(用户 ∧ agent)。** `DualPrincipalMapper`
会评估用户的授权 *以及* agent 自身的边界,仅在两者都允许时才
继续执行,因此越狱的 agent 绝不能借用其用户的权限。
如果没有可解析的主体,扫描将以 **closed** 状态失败。带有自身 *已验证* 身份的执行点
会填充相同的接缝:FastMCP middleware 读取
已验证 OAuth token 的 `sub`,并且它的优先级高于任何宿主断言的主体。有关
完整的解析顺序、带有代码的三个级别以及双主体连接方式,请参见
[docs/setup.md](docs/setup.md#identity-resolving-the-subject)。
## 默认 Fail-closed
每一条无法产生明确 ALLOW 的路径都会被拒绝:无法访问或超时的 PDP、
格式错误的响应(缺失或非布尔的 `decision` 是一个错误,绝不会强制转换为
allow)、缺失的主体、无法解析的工具调用。不存在 fail-open 选项;PDP
故障根据 `on_error` 解决为 `deny`(默认)或 `human_review`。PDP URL 必须
是 HTTPS 并通过 SSRF 防护,需验证 TLS 且绝不遵循重定向(唯一的退出选项
是显式的 `allow_insecure_pdp` 标志,用于本地开发)。`review_predicate` 只能
将决策 *升级* 为 `HUMAN_IN_THE_LOOP`,绝不降级。决策缓存默认
关闭,启用时仅缓存 ALLOW,以完整请求
元组的摘要作为键,并带有严格的 TTL。有关完整的故障处理和缓存设计,请参见
[docs/requirements.md](docs/requirements.md)(§3.6–3.9)。
## 可观测性
每个决策都会被计时和计数。扫描器(以及独立的 `AuthorizationEngine`)
暴露了一个 `metrics` 接收器 — 默认是一个带有延迟
直方图和决策/缓存计数器的进程内 `InMemoryMetrics`;传入你自己的 `MetricsSink` 以转发到
Prometheus/OpenTelemetry,或传入 `NoopMetrics()` 来禁用。
```
m = scanner.metrics
m.latency_histogram() # [(le_seconds, cumulative_count), …, (+Inf, n)]
m.decisions # {("allow", "success"): 12, ("block", "error"): 1}
```
每个决策还会发出一条结构化的审计日志行(裁决结果、状态、主体 ID、
correlation id、资源 ID、参数 *指纹*);原始参数和 token 绝不会被
记录。主体 ID 是决策主体(FastMCP 下的 OAuth `sub`,可能
是一个电子邮件地址),因此请将 `apparitor` logger 视为敏感信息。日志格式是
`0.1.0` 以来的稳定性契约。请参见 [docs/audit-log.md](docs/audit-log.md) 和
[docs/requirements.md](docs/requirements.md)(§3.10)。
## apparitor 连接的内容
**执行点**(apparitor 插入的 agent 侧 hook):
| 执行点 | 供应商 | 状态 |
| --- | --- | --- |
| [**LlamaFirewall**](https://github.com/meta-llama/PurpleLlama/tree/main/LlamaFirewall) | Meta | 发布中 (`AuthZENScanner`) |
| [**NeMo Guardrails**](https://github.com/NVIDIA/NeMo-Guardrails) | NVIDIA | 发布中 (`NeMoAuthorizationRails`) |
| [**FastMCP**](https://github.com/PrefectHQ/fastmcp) server middleware | Prefect | 发布中 (`FastMCPAuthorizationMiddleware`) |
| [**A2A**](https://a2a-protocol.org/) agent executor | Linux Foundation | 发布中 (`A2AAuthorizationExecutor`) |
**策略引擎**(做出授权决策的地方)。apparitor 通过
AuthZEN 连接这些引擎;OPA 和 Cedar 也有跳过
AuthZEN 环节的原生后端,通过配置选择(`backend="opa"` / `backend="cedar"`):
| 引擎 | 范式 | apparitor 连接方式 | 示例 |
| --- | --- | --- | --- |
| **Mock PDP** (测试/演示) | n/a | AuthZEN | [`examples/mock_pdp/`](examples/mock_pdp/) |
| **OpenFGA** | Zanzibar / ReBAC | 原生 AuthZEN (实验性) | [`examples/openfga/`](examples/openfga/) |
| **Cedar** | policy-as-code (ABAC) | AuthZEN 网关 · 原生进程内 (`backend="cedar"`) | [`examples/cedar/`](examples/cedar/) |
| **OPA / Rego** | policy-as-code | AuthZEN 网关 · 原生 Data API (`backend="opa"`) | [`examples/opa/`]( ) |
| **Amazon Verified Permissions** | 托管 Cedar | [AWS AuthZEN 接口](https://github.com/aws-samples/sample-authzen-interface-verified-permissions) | [`examples/avp/`](examples/avp/) |
| 任何 AuthZEN 1.0 PDP (Cerbos, Topaz, …) | 多样 | AuthZEN | [`docs/setup.md`](docs/setup.md) |
## 文档
- [技术要求与设计决策](docs/requirements.md)
- [架构](docs/architecture.md)
- [设置:连接到策略引擎](docs/setup.md)
- [欧盟 AI 法案 / CADA 合规参考](docs/eu-ai-act.md)
- [路线图](ROADMAP.md)
- [贡献指南](CONTRIBUTING.md) · [安全策略](SECURITY.md) · [更新日志](CHANGELOG.md)
## 许可证
[Apache License 2.0](LICENSE)。
标签:AI代理, MCP, 人工智能, 授权系统, 用户代理, 用户模式Hook绕过, 策略引擎, 网络安全挑战, 自定义请求头, 身份与访问控制, 逆向工具