Skip to content

Agentgateway 深度分析报告

目标项目agentgateway/agentgateway v1.0.0+ 分析日期:2026-08-05 重点关注:A2A、Agent ID 身份、权限、认证 关联上下文:NIO 车端 AI Agent 安全(30B~0.1B 多规模 LLM,端云混合,ASILD)


目录


一、项目定位与全景

agentgateway(前身 kgateway 的数据面)是一个为 AI Agent 系统 设计的开源数据平面代理:

"The first complete connectivity solution for Agentic AI. An open source proxy built on AI-native protocols (MCP & A2A) that provides drop-in security, observability, and governance for agent-to-LLM, agent-to-tool, and agent-to-agent communication."

四个一等公民场景:

场景协议角色
Agent → LLMOpenAI / Anthropic / Bedrock / Gemini / Vertex / Azure统一 OpenAI 兼容 API、budget/限流、prompt 增强、failover
Agent → ToolMCP (stdio / HTTP / SSE / Streamable HTTP)工具联邦、OAuth 鉴权、CEL 细粒度授权、guardrail
Agent ↔ AgentA2A (JSON-RPC over HTTP)能力发现 (agent card)、模态协商、任务协作
Self-hosted LLMKubernetes Inference Gateway extensionsGPU 利用率、KV cache、LoRA、队列深度选择

技术栈:Rust(数据面)、Go(K8s controller)、ProtoBuf(xDS)、CEL(策略表达式)、JSON Schema(配置/前端)。


二、Workspace 与 Crate 拓扑

agentgateway/ (Rust workspace)
├── crates/
│   ├── agentgateway         # 主二进制 (CLI、run、test)
│   ├── agentgateway-app     # 主库
│   ├── core                 # 通用基础(strng/arc/telemetry/prelude)
│   ├── http                 # HTTP 抽象 + JWT/AuthZ/ExtAuthz
│   ├── hbone                # HBONE(Envoy 隧道协议)
│   ├── llm                  # LLM provider 抽象(OpenAI/Anthropic/...)
│   ├── pool                 # 连接池
│   ├── xds                  # xDS 客户端
│   ├── protos               # Protobuf 生成代码
│   ├── cel-fork/cel         # 内部 fork 的 CEL 实现
│   ├── celx                 # CEL 扩展(CIDR/IP/strings/math/通用)
│   └── htpasswd-verify-fork # htpasswd fork
├── controller/              # Go:K8s Gateway API controller + xDS server
├── ui/                      # Web UI(探索/调试)
├── examples/                # 32+ 个 example(含 mcp-authentication、traffic-a2a、...)
├── architecture/            # 架构文档
├── design/                  # EP(Enhancement Proposal)
└── schema/                  # JSON schema + 文档

关键数据面模块(按 crates/agentgateway/src/ 拆)

模块关键文件作用
proxy/httpproxy.rs (4347 行)、gateway.rstcpproxy.rs主代理逻辑
a2a/mod.rs (222 行)A2A 协议识别 + agent card 重写
mcp/handler.rs (2026)、auth.rs (1096)、rbac.rssession.rsMCP 协议 + 鉴权 + 工具联邦
http/auth/mod.rsjws.rsjwt_sign.rsaws.rsazure.rsgcp.rsoauth/多种 AuthN 后端
http/oidc/mod.rs (370+)、callback.rssession.rsprovider.rsredirect.rsOIDC 浏览器登录
http/authorization.rs374 行RBAC + CEL 策略引擎
http/jwt.rs590 行JWT 验证
http/apikey.rs299 行API Key 认证(恒时比较)
http/basicauth.rs214 行Basic Auth(bcrypt/htpasswd)
http/ext_authz.rs1462 行Envoy ext_authz 协议 + CEL 元数据
http/ext_proc/多文件Envoy ext_proc(gRPC/HTTP)
cel/types.rs2370 行CEL 类型系统(Executor / ContextBuilder)
transport/tls.rs1100+mTLS / SPIFFE 身份提取
store/多文件内部表示(IR)构建与缓存
types/agent.rs3693 行用户面配置 + 编译期结构
control/多文件配置变更监听、热重载
telemetry/log.rs2466 行access log / 字段选择器 / CEL 日志

关键安全相关依赖

  • jsonwebtoken 10.4:JWS 验签
  • rustls 0.23 + aws-lc-rs:TLS 1.2/1.3、mTLS、PQ-Ready(X25519_MLKEM768)
  • x509-parser:证书解析、SPIFFE URI 提取
  • cel(fork)+ celx:策略引擎
  • secrecy:secret 内存保护
  • subtle:恒时比较(API Key、HMAC)
  • bcrypt 0.19:htpasswd 密码哈希
  • pwhash 1:另一种密码哈希
  • reqwest/hyper/hyper-rustls:HTTP 客户端

三、整体数据流(控制面 ↔ 数据面)

┌──────────────────────────────────────────────────────────────┐
│                      K8s / Standalone                         │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐       │
│  │ HTTPRoute /  │   │ Agentgateway-│   │ JWT Secret / │       │
│  │ Gateway /    │   │ Policy       │   │ TLS Profile  │       │
│  │ Backend      │   │              │   │              │       │
│  └──────┬───────┘   └──────┬───────┘   └──────┬───────┘       │
└─────────┼──────────────────┼──────────────────┼───────────────┘
          │                  │                  │
          ▼                  ▼                  ▼
┌──────────────────────────────────────────────────────────────┐
│  controller/ (Go) - agw controller                            │
│  ┌──────────────────────────────────────────────────┐         │
│  │  krt collection: 监听 K8s 资源 + 计算 IR        │         │
│  │  plugins/ (a2a, ai, backend_tls, jwt, oauth,…)  │         │
│  └────────────────────┬─────────────────────────────┘         │
│                       │  xDS (LDS/RDS/CDS/EDS)               │
│                       │  本机或 TLS(自动证书管理)            │
└───────────────────────┼──────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  data plane: agentgateway (Rust)                              │
│  ┌──────────────────────────────────────────────────┐         │
│  │  XdsUpdater ──> Store ──> ProxyInputs           │         │
│  │  (agent-xds) (types::Store) (Arc<ProxyInputs>)   │         │
│  └────────────────────┬─────────────────────────────┘         │
│                       ▼                                       │
│  ┌──────────────────────────────────────────────────┐         │
│  │  Gateway::run_bind() ── accept TCP/TLS          │         │
│  │   ├─ mTLS verify client cert (可选)              │         │
│  │   ├─ 提取 SPIFFE identity → SourceContext         │         │
│  │   ├─ SourceContext::from_stores (按 IP 解析)     │         │
│  │   ├─ NetworkAuthorizationSet.apply (L4 authz)   │         │
│  │   └─ spawn HTTPProxy::proxy()                    │         │
│  └────────────────────┬─────────────────────────────┘         │
│                       ▼                                       │
│  ┌──────────────────────────────────────────────────┐         │
│  │  HTTPProxy::proxy (per connection)               │         │
│  │  1. apply_gateway_policies  (CORS, OIDC, JWT,    │         │
│  │     Basic, APIKey, ExtAuthz, AuthZ, ExtProc,     │         │
│  │     Transformation)                              │         │
│  │  2. select_route → apply_route_policies (限流,   │         │
│  │     timeout, retry, MCP, A2A, LLM 等)            │         │
│  │  3. build_service_call (选 backend)              │         │
│  │  4. apply_backend_policies (backend_auth,        │         │
│  │     authorization, ext_authz, A2A 协议识别)     │         │
│  │  5. make_backend_call (TLS / 直连)                │         │
│  │  6. apply_to_response (A2A agent card 重写,       │         │
│  │     限流, transformation, MCP guardrail 等)      │         │
│  └──────────────────────────────────────────────────┘         │
└──────────────────────────────────────────────────────────────┘

三种配置源

配置用途加载机制
Static进程级(log、port)启动时一次,env/YAML
Local全部功能(bind、listener、route、backend、policy)文件 watch + 热重载 → IR
XDS同上但通过 controller 推送xDS stream → IR(无 URL fetch,节省控制面)

设计哲学:用户面 API ↔ XDS ↔ IR 接近直接映射,避免 Envoy 那种 listener→route list 的扇出膨胀。 见 architecture/configuration.md:21-56


四、核心数据结构(IR)

crates/agentgateway/src/types/agent.rs 定义了用户面的编译目标(同时也是 IR):

4.1 顶层容器

rust
// 简化的字段层次
struct Config {
  binds: Vec<Bind>,           // 监听端点
  // ...
}

struct Bind {
  port: u16,
  listeners: Vec<Listener>,
}

struct Listener {
  name: Strng,
  hostname: Strng,
  protocol: ListenerProtocol,    // HTTP / HTTPS / TLS / TCP
  routes: Vec<Route>,            // 或者 ListenerSet
  policies: FrontendPolices,     // 包含 network_authorization, tls, tcp
}

struct Route {
  matches: Vec<Match>,
  policies: RoutePolicies,       // jwt, oidc, basic, api_key, authz, ext_authz, llm, mcp, ...
  backends: Vec<RouteBackend>,
}

struct RouteBackend {
  backend: BackendReference,     // service / host / mcp / ai / static / ...
  policies: BackendPolicies,     // backend_auth, backend_tls, a2a, authz, ...
}

4.2 策略集(关键安全相关)

rust
// store/binds.rs:139-152
pub struct FrontendPolices {
  pub network_authorization: Option<NetworkAuthorizationSet>,  // L4 authz
  pub tls, pub tcp, pub http, pub proxy, pub connect: ...,
  // ...
}

// store/binds.rs:392-405
pub struct GatewayPolicies {
  pub cors, pub ext_proc, pub oidc, pub jwt, pub authorization, pub ext_authz,
  pub transformation, pub basic_auth, pub api_key, pub buffer,
}

// store/binds.rs:363-390
pub struct RoutePolicies {
  pub local_rate_limit, pub remote_rate_limit,
  pub authorization, pub jwt, pub oidc, pub basic_auth, pub api_key,
  pub ext_authz, pub ext_proc, pub transformation, pub csrf, pub direct_response,
  pub llm, pub timeout, pub retry, pub delay,
  pub request_header_modifier, pub response_header_modifier,
  pub request_redirect, pub url_rewrite, pub hostname_rewrite,
  pub request_mirror, pub cors, pub buffer,
}

// types/agent.rs:2690-2717
pub enum BackendTrafficPolicy {
  Authorization(Authorization),       // ← AuthZ
  McpAuthorization, McpAuthentication, McpGuardrails,
  A2a(A2aPolicy),                    // ← A2A 协议处理
  HTTP, TCP, Tunnel, BackendTLS,
  BackendAuth(BackendAuth),          // ← 后端认证
  InferenceRouting,
  AI(Arc<llm::Policy>),              // ← LLM 策略
  ExtAuthz(Arc<ext_authz::ExtAuthz>),
  SessionAffinity,
  Transformation, Health,
  RequestHeaderModifier, ResponseHeaderModifier,
  RequestRedirect, RequestMirror,
}

注意 A2A policy 是个空 structtypes/agent.rs:2731-2732)—— 它只做"协议识别 + agent card 重写",所有真正的鉴权/授权走常规 Authorization + Jwt + ExtAuthz 链。


五、请求生命周期(端到端时序)

mermaid
sequenceDiagram
    autonumber
    participant Client as Agent / Browser / LLM
    participant L4 as L4 (gateway.rs:run_bind)
    participant L4Authz as NetworkAuthorizationSet
    participant L7 as L7 (httpproxy.rs::proxy)
    participant GatePol as apply_gateway_policies
    participant RouteSel as select_route
    participant RoutePol as apply_route_policies
    participant MCP as MCP handler
    participant AuthN as AuthN (JWT/OIDC/APIKey/Basic)
    participant AuthZ as Authorization (CEL)
    participant ExtAuthz as ext_authz (Envoy ext_authz)
    participant BackendPol as apply_backend_policies
    participant A2A as A2A classifier
    participant Backend as Backend (mTLS/JWT sign/OAuth)
    participant Resp as Response chain

    Client->>L4: TCP/TLS handshake
    L4->>L4: rustls verify client cert (if mTLS)
    L4->>L4: extract SPIFFE identity (cert SAN URI)
    L4->>L4: SourceContext::from_stores(by IP) → unverified_workload
    L4->>L4Authz: NetworkAuthorizationSet.apply(SourceContext)
    L4Authz-->>L4: deny → RST
    L4->>L7: stream.ext.insert(SourceContext)
    L4->>L7: spawn HTTPProxy::proxy

    L7->>GatePol: apply_gateway_policies
    Note over GatePol: 顺序:CORS → OIDC → JWT → Basic → APIKey → ExtAuthz → AuthZ → ExtProc → Transformation
    GatePol->>AuthN: JWT/Basic/APIKey 验证
    AuthN-->>GatePol: 注入 Claims 到 req.extensions
    GatePol->>ExtAuthz: gRPC/HTTP ext_authz call
    ExtAuthz-->>GatePol: 注入 extauthz dynamic metadata
    GatePol->>AuthZ: CEL evaluation
    AuthZ-->>L7: deny → 403

    L7->>RouteSel: select_route (path/header/...)
    L7->>RoutePol: apply_route_policies
    Note over RoutePol: 再次 AuthN/AuthZ/ExtAuthz/限流/Transformation
    RoutePol->>MCP: MCP 协议入口 (mcp/handler.rs)
    MCP->>AuthN: mcpAuthentication (JWKS verify)
    MCP->>AuthZ: mcpAuthorization (per tool/prompt/resource/task)

    L7->>BackendPol: apply_backend_policies
    Note over BackendPol: 顺序:HTTP → AuthZ → ExtAuthz → backend_auth → transformation → request_header_modifier → request_redirect → A2A
    BackendPol->>BackendPol: backend_auth: Passthrough/Key/GCP/AWS/Azure/JwtSign/OAuth
    BackendPol->>A2A: a2a::apply_to_request (classify → AgentCard | Call(unknown | method))
    A2A-->>BackendPol: 设置 rp.a2a_type + log.backend_protocol

    L7->>Backend: 发起 backend call (mTLS if backendTLS, HTTP/1.1, HTTP/2, ...)
    Backend-->>L7: response
    L7->>A2A: a2a::apply_to_response (重写 agent card URL / 解析 JSON-RPC outcome)
    L7->>Resp: 限流/Transformation/MCP guardrail/ExtProc
    Resp-->>Client: HTTP response

关键安全特性

  • L4 → L7 → Backend 三层 AuthN/AuthZ(network_authorization 在 L4,authorization 在 L7,backend_auth 在调用后端前)
  • A2A 协议识别发生在 backend 阶段(不是 route 阶段),确保上游身份已验证
  • 每层失败都立即返回 4xx,不会让请求渗透到下一层

六、A2A 模块深挖

6.1 协议识别(crates/agentgateway/src/a2a/mod.rs

A2A v0.3+ 是 JSON-RPC over HTTP 协议。Agentgateway 把 A2A 当成一个语义识别层,而不是完整的协议翻译器:

rust
// a2a/mod.rs:11-55
pub async fn apply_to_request(_: &A2aPolicy, req: &mut Request<Body>) -> RequestType {
    classify_request(req).await
}

async fn classify_request(req: &mut Request<Body>) -> RequestType {
    match (req.method(), req.uri().path()) {
        // GET /.well-known/agent.json (v0.3-)  /  agent-card.json (v1.0+)
        (m, path) if m == GET && (path.ends_with("/.well-known/agent.json")
                                || path.ends_with("/.well-known/agent-card.json")) => {
            // 用 OriginalUrl 找到客户端发起的原始 URL(用于响应重写)
            let uri = req.extensions().get::<filters::OriginalUrl>()
                .map(|u| u.0.clone())
                .unwrap_or_else(|| req.uri().clone());
            let uri = crate::http::x_headers::apply_forwarded_scheme(uri, req.headers());
            RequestType::AgentCard(uri)
        },
        (m, _) if m == POST => {
            // 检查 Content-Type,peek JSON-RPC method
            let method = match classify_content_type(req.headers()) {
                WellKnownContentTypes::Json => match inspect_method(req).await {
                    Ok(method) => method,
                    Err(_) => Strng::from("unknown"),  // 解析失败不阻塞
                },
                _ => Strng::from("unknown"),
            };
            RequestType::Call(method)
        },
        _ => RequestType::Unknown,
    }
}

关键点:

  • A2A Policy 是空 structtypes/agent.rs:2731),本身不施加限制
  • agent card 路径识别支持 v0.3 的 agent.json 和 v1.0 的 agent-card.json
  • method 提取容错:JSON 解析失败时降级为 unknown(不让恶意输入导致解析器 panic)
  • OriginalUrl 处理:用客户端实际发起的 URL 而不是 backend URL 重写响应

6.2 Agent Card URL 重写(防 bypass 关键)

rust
// a2a/mod.rs:122-172
pub async fn apply_to_response(pol, a2a_type, resp) -> ... {
    match a2a_type {
        RequestType::AgentCard(uri) => {
            // 读取并解析 agent card
            let mut agent_card = json::from_body_with_limit::<Value>(body, buffer_limit).await?;
            let gateway_base = build_agent_path(uri);

            // v1.0: supportedInterfaces[] 数组
            if let Some(interfaces) = agent_card.get_mut("supportedInterfaces") {
                for iface in interfaces.as_array_mut()?.iter_mut() {
                    if let Some(url_val) = iface.get_mut("url")
                        && let Some(s) = url_val.as_str()
                        && let Ok(iface_uri) = s.parse::<Uri>() {
                        // 替换为 gateway 路径
                        let path_and_query = iface_uri.path_and_query()
                            .map(|pq| pq.as_str()).unwrap_or(iface_uri.path());
                        *url_val = Value::String(format!("{gateway_base}{path_and_query}"));
                    }
                }
            }
            // v0.3: 顶层 url
            else if let Some(url_field) = json::traverse_mut(&mut agent_card, &["url"]) {
                *url_field = Value::String(gateway_base);
            } else {
                anyhow::bail!("agent card missing URL");
            }
            *resp.body_mut() = json::to_body(agent_card)?;
        },
        RequestType::Call(_) => Ok(inspect_call_response(resp).await),  // 提取 result.kind, status.state
        _ => Ok(None),
    }
}

这个重写是核心安全特性——防止客户端看到后端真实地址(如 http://backend.internal:8080)后绕过 gateway 直连。

测试覆盖(a2a/tests.rs:175-280):

  • v0.3 单 url 字段
  • v1.0 单 interface
  • v1.0 多 interface(JSONRPC、GRPC 等)
  • v1.0 root path
  • X-Forwarded-Proto 头处理
  • 子路径处理

6.3 与 backend 策略链的串联

rust
// proxy/httpproxy.rs:381-396  apply_backend_policies 内的位置
if let Some(a2a) = a2a {
    let a2a_type = a2a::apply_to_request(a2a, req).await;
    if let a2a::RequestType::Call(method) = &a2a_type {
        log.add(|l| { l.a2a_method = Some(method.clone()); });  // 写入访问日志
    }
    if matches!(a2a_type, RequestType::Call(_) | RequestType::AgentCard(_)) {
        log.add(|l| { l.backend_protocol = Some(cel::BackendProtocol::a2a); });
    }
    rp.a2a_type = a2a_type;
}

位置很关键:A2A 分类发生在:

  • authorization (RBAC) 之后 ✅
  • ext_authz 之后 ✅
  • backend_auth(向后端插入凭证)之后 ✅
  • transformation 之后 ✅

也就是说 所有 AuthN/AuthZ 都先于 A2A 协议解析——安全边界正确。

6.4 A2A Backend Policy 激活路径

K8s 侧controller/pkg/agentgateway/plugins/a2a_plugin.go):

go
// 当 Service 端口标注 appProtocol: agentgateway.dev/a2a
// 自动生成 BackendPolicy { A2A: {} }
const (
    legacyA2aProtocol = "kgateway.dev/a2a"
    a2aProtocol       = "agentgateway.dev/a2a"
)

for _, port := range svc.Spec.Ports {
    if port.AppProtocol != nil && (*port.AppProtocol == a2aProtocol || ...) {
        // 生成 Policy{A2A: {}},target = Service
        policy := &api.Policy{
            Kind: &api.Policy_Backend{Backend: &api.BackendPolicySpec{
                Kind: &api.BackendPolicySpec_A2A_{A2A: &api.BackendPolicySpec_A2A{}},
            }},
        }
    }
}

Standalone 侧types/local.rs:2516):

yaml
policies:
  a2a: {}  # 空对象,仅触发识别
backends:
  - host: localhost:9999

6.5 A2A 在 CEL / 可观测中的暴露

rust
// cel/types.rs:148 - backend.protocol 枚举包含 a2a
pub enum BackendProtocol { Http, Tcp, A2a, Mcp, Llm }

// telemetry/log.rs:1400-1422 - 访问日志字段
("a2a.method", log.a2a_method.display()),
("a2a.response.outcome", ...),
("a2a.response.error_code", ...),
("a2a.result.kind", ...),       // task/message/event
("a2a.task.state", ...),         // submitted/working/completed/failed/canceled

重要洞见:A2A 是 唯一 把协议层方法/状态注入可观测性的协议。MCP 也类似(mcp.tool.name),但 A2A 走的是后端协议识别而非应用层解析

6.6 A2A 模块整体评价

维度实现评价
协议识别仅 POST JSON-RPC + GET well-known⭐⭐⭐⭐ 简洁、容错
协议翻译不翻译 body,只识别 + 重写 URL⭐⭐⭐⭐ 透明
端到端认证复用 Authorization 策略⭐⭐⭐⭐ 复用
端到端授权复用 Authorization 策略⭐⭐⭐⭐ 可写 backend.protocol == "a2a" && jwt.sub == "..."
Agent Card 保护URL 重写防 bypass⭐⭐⭐⭐⭐ 关键安全特性
任务层授权缺失❌ 无 per-method RBAC(vs MCP 完整)
任务级可观测result.kind / status.state⭐⭐⭐⭐

七、Agent ID 身份体系

Agentgateway 把 "Agent ID 身份" 拆成 三个独立来源,每种都有不同的信任强度。

7.1 L4 阶段:SPIFFE / mTLS 身份(强信任)

rust
// transport/tls.rs:947-998
pub struct TlsInfo {
    pub identity: Option<IstioIdentity>,
    pub subject_alt_names: Vec<Strng>,
    pub issuer: Strng,
    pub subject: Strng,
    pub subject_cn: Option<Strng>,
    pub certificate: Option<Strng>,  // PEM(仅在握手有客户端证书时存在)
}

pub struct IstioIdentity {
    trust_domain: Strng,
    namespace: Strng,
    service_account: Strng,
}

impl Display for IstioIdentity {
    fn fmt(&self, f) -> write!(f, "spiffe://{}/ns/{}/sa/{}",
        self.trust_domain, self.namespace, self.service_account)
}

提取逻辑transport/tls.rs:1000-1035):

rust
pub fn identity_from_connection(conn: &rustls::CommonState) -> Option<TlsInfo> {
    let cert = conn.peer_certificates()?.first()?;
    let (issuer, subject, subject_cn) = names(&cert);
    let (istio, sans) = sans(&cert).ok()?;
    Some(TlsInfo {
        identity: istio.into_iter().next().map(|i| {
            let Identity::Spiffe { trust_domain, namespace, service_account } = i;
            IstioIdentity { trust_domain, namespace, service_account }
        }),
        // ...
    })
}

// sans() 解析 URI SAN,识别 spiffe:// scheme
let istio = names.iter().filter_map(|n| {
    let id = match n {
        GeneralName::URI(uri) => Identity::from_str(uri),
        _ => return None,
    };
    // ...
});

mTLS 验证链types/agent.rs:488-518):

  • root_pem:CA roots(来自 Listener.tls.certificatescaCertificates
  • WebPkiClientVerifier:rustls 提供的证书验证
  • allow_insecure_mtls:当 true 时,证书验证失败但仍放行(不安全,仅测试用)

注意:信任域校验在 transport/tls.rs:704-820 的测试代码中体现。生产中需要配置 additional_trust_domains(见 v1.2.0-alpha.1 CHANGELOG: "Support additional trusted trust domains on hbone income")。

7.2 L4 阶段:按 IP 解析的 Workload 身份(弱信任)

rust
// cel/types.rs:269-318
pub struct SourceContext {
    pub address: IpAddr,        // 解析后地址(可能经过 PROXY protocol)
    pub port: u16,
    pub raw_address: IpAddr,    // 原始 TCP peer
    pub raw_port: u16,
    pub tls: Option<TlsInfo>,   // ← 来自 mTLS,强信任
    pub unverified_workload: Option<WorkloadContext>,  // ← 来自 IP 解析,弱信任
    pub connect_headers: http::HeaderMap,  // CONNECT 隧道头
}

关键代码注释cel/types.rs:294-297):

"All fields live under unverified to make it clear that the data is resolved by IP, not cryptographically verified. Policy authors should prefer source.identity.* for trust-sensitive checks."

rust
// cel/types.rs:375-394
impl WorkloadContext {
    pub fn from_stores(stores, network, addr) -> Option<WorkloadContext> {
        let discovery = stores.read_discovery();
        discovery.workloads.find_address(&NetworkAddress {
            network: network.clone(),
            address: addr,
        }).map(|w| WorkloadContext {
            name: w.name.clone(),
            namespace: w.namespace.clone(),
            service_account: w.service_account.clone(),
        })
    }
}

L4 串联proxy/gateway.rs:863-885):

rust
let unverified_workload = crate::cel::WorkloadContext::from_stores(
    &inputs.stores, &inputs.cfg.network, tcp.peer_addr.ip());

let mut src = crate::cel::SourceContext::from_tcp_connection(
    tcp,
    tls.and_then(|t| t.src_identity.clone()),  // 来自 mTLS
    unverified_workload,                       // 来自 IP
);

if let Some(network_authorization) = policies.network_authorization.as_ref()
    && let Err(e) = network_authorization.apply(&src)
{
    anyhow::bail!("network authorization denied: {e}");  // ← L4 立即拒绝
}

stream.ext_mut().insert(src);

7.3 L7 阶段:JWT / APIKey / Basic / OIDC 身份

详见第八节。这些身份通过 Request.extensions 注入,并在 CEL 上下文中以 jwt.*apiKey.*basicAuth.* 形式暴露。

7.4 Agent ID 身份的统一抽象:SourceContext + Extensions

rust
// proxy/httpproxy.rs:617-619  每个连接开始时注入
connection.copy::<cel::SourceContext>(req.extensions_mut());
connection.copy::<cel::DestinationContext>(req.extensions_mut());

// jwt.rs:550  JWT 验证后注入
req.extensions_mut().insert(claims);

// apikey.rs 类似
req.extensions_mut().insert(Claims { key, metadata });

整个请求处理链中,每个策略/后端调用都能从 extensions 里取出身份,避免重复解析。

7.5 信任层级总结

来源信任强度CEL 访问适用决策
mTLS SPIFFE🔴 强(密码学验证)source.identity.trustDomain/namespace/serviceAccount信任敏感
Workload by IP🟡 弱(网络可达性)source.unverifiedWorkload.*仅用于日志/路由
JWT claims🔴 强(JWKS 签名验证)jwt.*信任敏感
API Key🔴 强(配置侧定义)apiKey.* + metadata信任敏感
Basic Auth🟡 中(密码哈希)basicAuth.*一般信任
OIDC session🔴 强(PKCE + ID token 验签)jwt.*(同 JWT)信任敏感
ExtAuthz取决于外部服务extauthz.*由实现决定

设计原则source.identity.*(mTLS)做信任决策source.unverifiedWorkload.* 做路由/审计。两者在结构上明确分离。


八、认证(AuthN)全栈

8.1 体系总览

┌──────────────────────────────────────────────────────────────┐
│  AuthN 入口(按配置层级)                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐           │
│  │ Gateway     │  │ Route       │  │ Backend     │           │
│  │ Policies    │  │ Policies    │  │ Policies    │           │
│  └─────────────┘  └─────────────┘  └─────────────┘           │
│        │                │                │                   │
│        ▼                ▼                ▼                   │
│   ┌─────────┐     ┌──────────┐    ┌──────────────┐           │
│   │ CORS    │     │ CSRF     │    │ BackendAuth  │           │
│   │ OIDC    │     │ JWT      │    │ - Passthrough│           │
│   │ JWT     │     │ OIDC     │    │ - Key        │           │
│   │ Basic   │     │ Basic    │    │ - GCP        │           │
│   │ APIKey  │     │ APIKey   │    │ - AWS        │           │
│   │ ExtAuthz│     │ ExtAuthz │    │ - Azure      │           │
│   └─────────┘     │ MCP-Authn│    │ - Copilot    │           │
│                   └──────────┘    │ - JwtSign    │           │
│                                  │ - OAuthExch  │           │
│                                  │ - CrossApp   │           │
│                                  └──────────────┘           │
└──────────────────────────────────────────────────────────────┘

8.2 JWT 验证(http/jwt.rs,590 行)

三种 Modehttp/jwt.rs:179-192):

Mode行为默认安全建议
Strict必须有有效 JWT,否则 401✅ 生产推荐
Optional有则验证,无则放行⚠️ 需配合 AuthZ
Permissive有则验证,无效也放行🪲 仅调试

关键流程http/jwt.rs:490-552):

rust
pub async fn apply(&self, log, req) -> Result<(), TokenError> {
    let Some(token) = self.location.extract(req) else {
        if self.mode == Mode::Strict { return Err(TokenError::Missing); }
        return Ok(());  // 其他 mode 放过
    };
    let claims = match self.validate_claims(&token) {
        Ok(c) => c,
        Err(e) if self.mode == Mode::Permissive => return Ok(()),  // 验证失败也放行
        Err(e) => return Err(e),
    };
    if let Some(serde_json::Value::String(sub)) = claims.inner.get("sub") {
        log.jwt_sub = Some(sub.to_string());  // 写入访问日志
    }
    self.location.remove(req)?;  // ← **关键:从请求中删除 token,防止透传到后端**
    req.extensions_mut().insert(claims);  // 注入到 extensions
    Ok(())
}

安全特性

  • 验证后立即从请求中移除 token(防透传泄露)
  • 通过 JWKS 远程拉取(带缓存,见 controller 侧的 Modernize remote JWKS handling
  • 多 provider 支持(multi-issuer)
  • Custom Location(header/cookie/query)支持
  • 强制声明验证:exp, nbf, aud, iss, sub(其他如 iat 不强制)

Claims 在 CEL 中

rust
// http/jwt.rs:450-461
impl DynamicType for Claims {
    fn field(&self, field: &str) -> Option<cel::Value<'_>> {
        match field {
            "rawToken" => Some(crate::cel::secret_string_to_value(&self.jwt)),
            _ => self.inner.field(field),
        }
    }
}

注意 rawToken 标记为 secret(在 CEL 中默认 redact,需 .unredacted() 显式取出)。

8.3 API Key(http/apikey.rs,299 行)

安全特性

  • 常量时间比较http/apikey.rs:88-99):

    rust
    impl PartialEq for APIKey {
        fn eq(&self, other: &Self) -> bool {
            // 防止时序攻击:常量时间比较
            self.0.expose_secret().as_bytes()
                .ct_eq(other.0.expose_secret().as_bytes())
                .into()
        }
    }
  • SHA-256 哈希存储http/apikey.rs:111-115):

    rust
    impl APIKeyHash {
        pub fn from_raw_key(key: &str) -> Self {
            let digest = crate::crypto::digest::sha256(key.as_bytes());
            APIKeyHash(hex::encode(digest))
        }
    }
  • 三种 Mode:Strict / Optional / Permissive

  • 支持 metadata(任意 JSON)

CEL 访问apiKey.key(默认 redact,需 .unredacted()),apiKey.metadata.*

8.4 Basic Auth(http/basicauth.rs,214 行)

  • 使用 htpasswd-verify-fork(fork 自 htpasswd-verify)
  • bcrypt 密码哈希
  • CEL 访问:basicAuth.username(注意:没有 password 字段,password 验证后丢弃)

8.5 OIDC 浏览器登录(http/oidc/,自 v1.1.0)

完整 OAuth2/OIDC 流程,作为 oauth2-proxy 的原生替代:

agentgateway                    浏览器                  IdP
    │  GET /resource              │                       │
    │<── 302 Location: IdP /authorize?code_challenge ────│
    │── 302 转发到 IdP ──────────>│                       │
    │                             │<── IdP login page ───│
    │                             │<── IdP 返回 code ────│
    │<── 302 /callback?code&state ──│                     │
    │  校验 transaction cookie     │                     │
    │  ── IdP /token (code_verifier) ──────────────────>│
    │<─ id_token + access_token ─────────────────────────│
    │  验证 id_token, 设置 session cookie                │
    │  302 → /resource                                    │
    │  后续请求带 session cookie 验证

关键安全http/oidc/mod.rs:196-235):

rust
pub async fn apply(&self, log, req, client) -> Result<PolicyResponse, Error> {
    if let Some(response) = self.maybe_handle_callback(req, client.clone()).await? {
        return Ok(response);  // 优先处理 callback
    }
    if is_cors_preflight(req) { return Ok(PolicyResponse::default()); }
    if let Some(cookie) = read_request_cookie(req, &self.session.cookie_name) {
        match self.session.decode_browser_session(&cookie) {
            Ok(browser_session) => {
                if browser_session.policy_id == self.policy_id
                    && let Ok(claims) = self.provider.id_token_validator
                        .validate_claims(browser_session.raw_id_token.expose_secret())
                {
                    // ← **关键**:每次请求都验签 id_token,不是只信 session cookie
                    if let Some(Value::String(sub)) = claims.inner.get("sub") {
                        log.jwt_sub = Some(sub.clone());
                    }
                    req.extensions_mut().insert(claims);
                    return Ok(PolicyResponse::default());
                }
            },
            Err(err) => debug!(...),
        }
    }
    callback::start_login(self, req)  // 未认证 → 启动登录
}

安全特性

  • PKCE(每次请求都用 code_verifier)
  • CSRF 防护(transaction cookie 中的 state 与 callback state 必须匹配)
  • 每次请求都验签 id_token(session cookie 本身不可信)
  • policy_id 绑定(防止 session 被另一个 policy 复用)

8.6 ext_authz(http/ext_authz.rs,1462 行)

完整实现 Envoy ext_authz 协议:

rust
// http/ext_authz.rs:78-89
pub enum FailureMode {
    Allow,           // 外部失败时放行
    #[default] Deny, // 外部失败时拒绝(默认更安全)
    DenyWithStatus(u16),
}

pub enum Protocol {
    Grpc { context, metadata },           // envoy.service.auth.v3
    Http { path, redirect, body,
           include_response_headers, add_request_headers, metadata },
}

特性

  • 10000 条缓存
  • CEL 计算 path/headers/body
  • dynamic metadata 注入到 CEL 上下文 extauthz.*
  • gRPC 和 HTTP 双协议

8.7 BackendAuth(http/auth/mod.rs:74-111

向后端发起请求时插入凭证:

rust
pub enum BackendAuthKind {
    Passthrough { location },   // 透传验证过的 JWT
    Key { value, location },    // 静态 API key
    Gcp(gcp::GcpAuth),          // GCP Service Account
    Aws(aws::AwsAuth),          // AWS SigV4
    Azure(azure::AzureAuth),    // Azure AD
    Copilot,                    // GitHub Copilot
    JwtSign(Box<JwtSignAuth>),  // 短期 JWT(client_assertion)
    OAuthTokenExchange(Box<OAuthTokenExchangeAuth>),  // RFC 8693
    CrossAppAccess(Box<CrossAppAccessAuth>),          // RFC 8693 → ID-JAG → RFC 7523
}

JwtSign:每次请求用配置的私钥签短期 JWT(适合 client_credentials OAuth)

OAuthTokenExchangehttp/auth/oauth/mod.rs):

  • RFC 8693 完整实现
  • 支持 subject_token / actor_token(委托链)
  • 支持 chained_exchange(ID-JAG → access token)
  • Token 缓存(默认 8192 entries / 300s TTL)

CrossAppAccesshttp/auth/oauth/cross_app_access.rs):

  • 专门针对 Google/CAEP 跨域访问协议
  • 把 ID token 链式交换为 ID-JAG,再换为资源 access token

8.8 凭据保密

所有 secret 在内存中用 secrecy::SecretString 保护,调试输出自动 redact:

rust
// http/auth/mod.rs:84
#[serde(serialize_with = "ser_redact")]  // 序列化时输出 [REDACTED]
value: SecretString,

// http/auth/jws.rs:91
impl fmt::Debug for SigningKey {
    fn fmt(&self, f) -> f.write_str("[REDACTED]")  // Debug 也 redact
}

Header 设置 sensitive flag(http/auth/mod.rs:183):

rust
let mut header_value = HeaderValue::from_str(&value)?;
header_value.set_sensitive(true);  // 防止日志泄露

九、授权(AuthZ)引擎与 RBAC

9.1 核心算法(http/authorization.rs:255-275

rust
pub fn validate(&self, exec: &Executor) -> bool {
    let rule_sets = &self.0;
    let has_rules = self.has_rules();
    let allowed = if !has_rules {
        true                                          // 1. 无规则 → 默认允许
    } else if rule_sets.iter().any(|r| r.denies(exec)) {
        false                                         // 2. 任何 DENY 匹配 → 拒绝
    } else if rule_sets.iter().any(|r| !r.all_requires_match(exec)) {
        false                                         // 3. 任何 REQUIRE 不匹配 → 拒绝
    } else if rule_sets.iter().any(|r| r.allows(exec)) {
        true                                          // 4. 任何 ALLOW 匹配 → 允许
    } else {
        !rule_sets.iter().any(|r| r.has_allow_rules())  // 5. 只有 DENY(denylist)vs 都没有匹配(allowlist)
    };
    // ...
    allowed
}

三态规则

  • allowexpr == true 时放行
  • denyexpr == true 时拒绝(注意:表达式错误时不会 deny
  • require:所有 require 必须都满足(最小必备条件)

文档建议http/authorization.rs:131-141):

"Deny is not recommended because expression failures fail to deny; prefer Allow or Require. If used, design expressions defensively against evaluation errors."

9.2 MCP RBAC(mcp/rbac.rs

rust
pub enum ResourceType {
    Tool(ResourceId),       // MCP 工具
    Prompt(ResourceId),     // MCP prompt
    Resource(ResourceId),   // MCP resource
    Task(ResourceId),       // SEP-2663 task
}

impl McpAuthorizationSet {
    pub fn validate(&self, res: &ResourceType, cel: &CelExecWrapper) -> bool {
        if !self.0.has_rules() { return true; }
        let mcp = crate::mcp::MCPInfo::from(res);
        let exec = crate::cel::Executor::new_mcp_request(cel.0.as_ref(), &mcp);
        self.0.validate(&exec)
    }
}

典型策略examples/mcp-authorization/config.yaml):

yaml
mcpAuthorization:
  rules:
  # Allow anyone to call 'echo'
  - 'mcp.tool.name == "echo"'
  # Only test-user can call 'get-sum'
  - 'jwt.sub == "test-user" && mcp.tool.name == "get-sum"'
  # Any user with the nested.key claim can call 'get-env'
  - 'mcp.tool.name == "get-env" && jwt.nested.key == "value"'

重要安全特性

  • 工具名可被 CEL 访问
  • JWT claims 可被 CEL 访问
  • CEL 错误 → 工具被拒绝(fail-closed)

9.3 Network Authorization(store/binds.rs:2403-2413

L4 阶段授权,只看 SourceContext:

rust
fn create_network_authorization_policy(cidr: &str) -> FrontendPolicy {
    FrontendPolicy::NetworkAuthorization(NetworkAuthorization(RuleSet::new(
        PolicySet::new(
            vec![Arc::new(cel::Expression::new_strict(
                format!(r#"cidr("{cidr}").containsIP(source.address)"#)
            ).unwrap())],
            vec![], vec![],
        )
    )))
}

支持基于:

  • source.address + cidr("10.0.0.0/8").containsIP()
  • source.identity.trustDomain
  • source.port

9.4 测试覆盖的关键边界(http/authorization_tests.rs

代码中有详尽测试覆盖了:

  • 默认 allow / deny
  • 多个 RuleSet 合并
  • Require 失败
  • Allow 失败
  • 表达式错误处理
  • CEL 错误 → 默认拒绝

十、CEL 上下文与安全相关表达式

10.1 ContextBuilder:按需捕获(性能关键)

rust
// 编译期:扫描表达式,提取依赖的变量
fn attributes_for(program.expression()) -> FlagSet<Attributes>;

// 请求期:只把表达式需要的字段加到上下文
ctx.with_request_body_if_needed();   // body 特别贵
ctx.with_headers_if_needed();         // headers 也贵

核心设计architecture/cel.md:31-37):

"During CEL policy parsing (which happens on configuration change, not each request), we extract which variables are referenced in the expression. During request processing, ContextBuilder.with_xxx() is called to conditionally add fields into the context. For example, if an expression requires request.body we will store a copy of the body in the context, but if no expression requires it the data will be ignored. This ensures users only pay for what they use."

10.2 安全相关表达式示例

需求CEL 表达式
限制只有 prod-sa 的 pod 能访问source.identity.serviceAccount == "prod-sa" && source.identity.namespace == "prod"
限制 IP 段cidr("10.0.0.0/8").containsIP(source.address)
限制 JWT issuerjwt.iss == "https://auth.example.com"
限制 JWT audience"my-api" in jwt.aud
限制 JWT scope"admin" in jwt.scope.split(" ")
限制 mcp 工具mcp.tool.name == "echo" && jwt.sub == "user-1"
强制 TLS 版本source.subject.startsWith("CN=") (扩展)
阻断 A2A 特定方法backend.protocol == "a2a" && a2a_method == "tasks/send" && jwt.sub == "trusted-agent"
限制 HTTP 方法request.method == "POST"
限制 path 前缀request.path.startsWith("/api/v1/")

10.3 内置函数(schema/cel-functions.md

标准 CELcontains size has map filter all max startsWith endsWith string bytes double int uint matches

时间/数学duration timestamp math.least math.greatest math.floor math.isInf

字符串扩展:参考 cel-go strings ext

K8s CIDRcidr("10.0.0.0/8").containsIP("10.1.2.3")

通用工具crates/celx/src/general.rs):

  • json(string) / toJson(value)
  • unvalidatedJwtPayload(token)⚠️ 不验签,只解码
  • base64.encode/decode / url.encode/decode / form.encode/decode
  • sha1.encode / sha256.encode / md5.encode
  • regexReplace / uuid / random
  • default / coalesce / fail

10.4 自定义函数(crates/celx/src/custom.rs

支持用户自定义 CEL 函数(带属性依赖追踪)。配置期注册,运行时求值。

10.5 完整 CEL Schema 速查(参考 schema/cel.md

yaml
request:           { method, uri, host, scheme, path, pathAndQuery, version, headers, body, bodyPrefix, startTime, endTime }
response:          { code, grpcStatus, headers, body, bodyPrefix }
proxy:             { bind, gateway, listener, route, requestProcessingDuration, upstreamDuration, responseProcessingDuration }
env:               { podName, namespace, gateway }
jwt:               { ...claims..., rawToken (secret) }
apiKey:            { key (secret), metadata }
basicAuth:         { username }
llm:               { streaming, requestModel, responseModel, provider, inputTokens, outputTokens, prompt[], completion[], toolCalls[], params, cost, costRates }
llmRequest:        <raw LLM request>
source:            { address, port, rawAddress, rawPort, identity{trustDomain,namespace,serviceAccount}, subjectAltNames, issuer, subject, subjectCn, certificate, unverifiedWorkload{name,namespace,serviceAccount}, connectHeaders }
destination:       { address, port }
mcp:               { methodName, sessionId, tool{target,name,arguments,result,error}, prompt{target,name}, resource{target,name}, task{target,name} }
backend:           { name, type{ai,mcp,static,dynamic,service,unknown}, protocol{http,tcp,a2a,mcp,llm} }
extauthz:          <ext_authz dynamic metadata>
extproc:           <ext_proc dynamic metadata>
mcpGuardrails:     <mcp guardrail dynamic metadata>
metadata:          <transformation metadata>

十一、对 NIO 车端 AI Agent 安全的可借鉴点

NIO 场景(基于 User Profile):座舱 8295+QTEE / 智驾 Orin+OP-TEE / 车控 S32G+OP-TEE+HSM 30B~0.1B 多规模 LLM,离线优先,车控 50Hz,ASIL-D

11.1 直接可复用的设计

1. 三层身份模型

NIO 可以把车端 Agent 身份拆成:

  • 强信任:HSM 中存储的 SPIFFE/VCert 证书(车机/智驾 ECU)
  • 中信任:TEE 中 attest 出的 workload 身份
  • 弱信任:CAN 总线地址(按 IP 类比)

参考 SourceContext 结构,明确分 identityunverified 字段,文档强制写明只信任前者。

2. RBAC + CEL 授权

http/authorization.rs:255-275 的三态算法(allow/deny/require)非常适合车端场景:

  • require "ispeed < 0 && has(jwt) && jwt.vin matches VIN_PATTERN"(高速场景必须验证)
  • allow "jwt.role == 'OEM' && mcp.tool.name in ['OTA', '诊断']"
  • deny "request.path.startsWith('/brake') && jwt.role != 'OEM-Diagnostic'"

比硬编码 ACL 灵活,比 BPF/eBPF 易审计

3. A2A 协议识别 + Agent Card 重写

NIO 多 ECU 通信(座舱 ↔ 智驾 ↔ 车控)天然是 A2A 场景。a2a/mod.rs:122-172 的 agent card URL 重写正是车端防 bypass 的关键

  • 防止智驾 agent 看到车控 IP:port 后绕过 gateway 直接控制刹车
  • 重写为 gateway URL,强制走完整 AuthN/AuthZ

4. ext_authz 委托权威决策

车端某些决策需要"硬实时"或"特殊权限"(如安全气囊触发),

  • 默认路径:agentgateway 内 CEL 决策(快速)
  • 关键决策:ext_authz → 委托给独立 ASIL-D 域决策(严格)

5. mTLS + SPIFFE

完全借鉴:智驾 Orin+OP-TEE 域内每个 Agent 配 SPIFFE 身份。 车控 S32G+OP-TEE+HSM 域使用 HSM 存储 CA 根证书。

6. Backend 端 JwtSign + OAuthTokenExchange

车端调用云端 LLM 时:

  • JwtSign 用车机私钥签短期 JWT(client_assertion)
  • OAuthTokenExchange 拿 ID token 换 access token(受车端用户身份约束)
  • 缓存 减少网络往返(车端常弱网)

7. 凭据内存保护

secrecy::SecretString + set_sensitive(true) 是车端必须:

  • Debug 日志自动 redact
  • Header 标记为 sensitive(防止 tracing 工具意外导出)

8. Network AuthZ (L4)

车端可用 SourceContext 做:

cel
cidr("10.0.0.0/8").containsIP(source.address)  // 仅内网
source.identity.serviceAccount in ["ada-pilot", "adas-controller"]  // 仅 ECU

车控 50Hz 场景:L4 决策比 L7 决策快一个数量级,适合 hard real-time 过滤。

11.2 适配车端的扩展建议

车端需求agentgateway 已支持需要的扩展
离线运行xDS 缓存、文件 watch、本地配置增加"完全离线模式"(无控制面),参考 controller/cmd 启动
车规级 TLSrustls + aws-lc-rs(已 PQ-Ready)验证 PQ 算法 X25519_MLKEM768 在 ECU 上的可用性
50Hz 周期L4 authz 极快Cel::Expression 预编译 + 零分配(已实现)
整车 CA 在车端CA 根证书 PEM 加载验证 HSM 集成路径(已支持 allow_insecure_mtls 等价物)
多 LLM 部署LLM provider 抽象完整增加 "local-only" provider(不依赖云端)
时延敏感L4 fast path把关键策略编译为字节码(已部分实现)

11.3 NIO 应该直接 ship 的最小子集

如果只取 agentgateway 一部分代码到 NIO 车端,最小可用配置:

yaml
# nio-a2a-minimal.yaml
binds:
- port: 8443
  tls:
    mode: TERMINATE
    certificates:
    - cert: hsm://vcert/cockpit
      key: hsm://vcert/cockpit-key
      root: |
        # ECU CA 根证书(PEM)
  listeners:
  - routes:
    - matches:
      - path: { prefix: "/a2a" }
      policies:
        jwt:
          mode: Strict
          issuer: "spiffe://nio.local/ns/adas/sa/orchestrator"
          audiences: ["spiffe://nio.local/ns/cockpit/sa/agent"]
          jwks:
            inline: { ... }  # 本地缓存的 JWKS
        authorization:
          rules:
          - require: 'source.identity.serviceAccount in ["cockpit-agent", "adas-agent"]'
          - require: 'cidr("10.0.0.0/8").containsIP(source.address)'
          - allow: 'jwt.role == "adas-orchestrator" && mcp.tool.name == "perception"'
          - deny: 'request.path.contains("/brake")'  # 永远不允许 A2A 调刹车
        ext_authz:
          failureMode: Deny
          service: ...
    - matches:
      - path: { exact: "/.well-known/agent.json" }
      policies:
        a2a: {}  # 触发 A2A 协议识别
    backends:
    - mcp:
        targets:
        - name: cockpit
          stdio: { ... }
        policies:
          backend_auth:
            jwtSign:
              algorithm: ES256
              key: hsm://vcert/cockpit-sign

十二、风险点与未覆盖的洞

风险位置说明缓解
unvalidatedJwtPayload 不验签celx/src/general.rs:339仅解码 base64,攻击者可以伪造任何 payload仅用于日志/可观测,不用于 auth 决策
Deny 规则在 CEL 错误时触发http/authorization.rs:255CEL 表达式失败会跳过 deny文档建议用 require/allow 替代
Optional 是默认 JWT modehttp/jwt.rs:187不强制 JWT 验证必须显式设置 Strict
OIDC session cookie 不签名则不可信http/oidc/session.rs必须配合加密 session 校验每次请求都验签 id_token
配额泄漏:Express 错误暴露内部信息一些错误处理anyhow::bail!("agent card invalid JSON")错误信息应统一 sanitize
mTLS allow_insecure_mtls 选项types/agent.rs:97测试模式,正式禁用默认 false,需显式开启
unverifiedWorkload 名字暗示"未验证"cel/types.rs:333-345文档清楚但需用户仔细读命名清晰 + 文档明确
JWT Token 透传到 backend 风险http/auth/mod.rs:76-80Passthrough 模式会转发KeyJwtSign 替代
ExtAuthz 失败模式 Allowhttp/ext_authz.rs:83外部服务挂了放行不安全默认 Deny
大量 MCP 工具 = 大量 CEL 表达式mcp/rbac.rs每个工具都有 RBAC 规则规则数大时启动慢
Network authz 在 L4 阶段,无 HTTP contextproxy/gateway.rs:881不能基于 HTTP path 决策配 L7 authz
A2A 任务级授权缺失a2a/mod.rs不能区分 tasks/send vs tasks/cancel通过 backend protocol = "a2a" + CEL 表达式实现
Prometheus metrics 可能含 PIItelemetry/metrics.rssource IP、user-agent 暴露字段过滤

附录 A:关键源码索引

数据面(Crate: crates/agentgateway

路径行数作用
src/lib.rs-模块入口
src/app.rs-应用装配
src/proxy/httpproxy.rs4347主代理(核心)
src/proxy/gateway.rs-L4 入口
src/proxy/tcpproxy.rs-TCP 代理
src/proxy/dtrace.rs909请求级调试追踪器
src/a2a/mod.rs222A2A 协议识别
src/a2a/tests.rs578A2A 测试(含 agent card URL 重写)
src/mcp/handler.rs2026MCP 协议处理
src/mcp/auth.rs1096MCP 认证(OAuth / JWKS)
src/mcp/rbac.rs246MCP RBAC
src/mcp/session.rs1121MCP 会话管理
src/mcp/guardrails/-MCP 内容过滤
src/mcp/upstream/-MCP upstream 多路复用
src/http/auth/mod.rs497BackendAuth 总入口
src/http/auth/jws.rs121JWS 签名
src/http/auth/jwt_sign.rs196JwtSign Auth
src/http/auth/aws.rs1372AWS SigV4
src/http/auth/azure.rs463Azure AD
src/http/auth/gcp.rs456GCP Service Account
src/http/auth/copilot.rs176GitHub Copilot
src/http/auth/oauth/mod.rs912OAuth Token Exchange
src/http/auth/oauth/cross_app_access.rs306Cross-App Access (ID-JAG)
src/http/auth/oauth/client_auth.rs578Private Key JWT
src/http/auth/oauth/cache.rs451Token 缓存
src/http/oidc/mod.rs370+OIDC 浏览器登录
src/http/oidc/callback.rs-OIDC callback
src/http/oidc/session.rs-加密 session cookie
src/http/oidc/provider.rs-OIDC provider discovery
src/http/oidc/redirect.rs-重定向 URI 验证
src/http/authorization.rs374RBAC + CEL 引擎
src/http/jwt.rs590JWT 验证
src/http/apikey.rs299API Key(恒时比较)
src/http/basicauth.rs214Basic Auth
src/http/ext_authz.rs1462Envoy ext_authz
src/http/ext_proc.rs-Envoy ext_proc
src/http/csrf.rs-CSRF 保护
src/http/backendtls.rs-后端 TLS
src/http/transformation_cel.rs-CEL-based transformation
src/cel/types.rs2370CEL 类型系统
src/cel/mod.rs442CEL 入口
src/cel/custom.rs675自定义 CEL 函数
src/cel/properties.rs77属性依赖追踪
src/cel/query.rs314Query accessor
src/transport/tls.rs1100+TLS / mTLS / SPIFFE 提取
src/transport/stream.rs-TCP/TLS 流
src/crypto/jwt.rs19JWT crypto provider
src/crypto/aead.rs-AEAD (AES-GCM, ChaCha20)
src/crypto/digest.rs-哈希(SHA-256 等)
src/crypto/tls.rs-TLS 配置
src/store/binds.rs4000+IR 存储 + 策略解析
src/types/agent.rs3693用户面配置 schema
src/types/agent_xds.rs-xDS ↔ IR 转换
src/types/backend.rs-Backend 类型
src/types/frontend.rs-Frontend 类型
src/telemetry/log.rs2466访问日志
src/telemetry/metrics.rs-Prometheus 指标
src/control/-配置变更监听
src/store/discovery.rs-Service discovery

Crate: crates/celx

路径行数作用
src/lib.rs123入口
src/general.rs501通用函数(json、base64、sha256)
src/strings.rs435字符串扩展
src/math.rs378数学扩展
src/cidr.rs186CIDR / IP
src/flatten.rs98flatten for logging
src/optimize.rs134性能优化
src/function_tests.rs647函数测试

Crate: crates/core

路径作用
src/lib.rsPrelude
src/strng.rs不可变字符串优化
src/arc.rsArc 优化
src/telemetry.rs基础遥测

K8s Controller

路径作用
controller/pkg/agentgateway/plugins/a2a_plugin.goA2A Service 自动发现
controller/pkg/agentgateway/plugins/ai_policies.goAI 策略转换
controller/pkg/agentgateway/plugins/backend_tls_plugin.goBackendTLS 转换
controller/pkg/agentgateway/plugins/jwks_lookup.goJWKS 远程获取
controller/pkg/agentgateway/plugins/registry.go插件注册
controller/cmd/Controller 启动
controller/test/e2e/a2a_test.goA2A e2e 测试
controller/hack/testbox/a2a.goA2A 测试 server

配置与文档

路径作用
architecture/README.md架构入口
architecture/configuration.md配置体系
architecture/cel.mdCEL 体系
design/README.mdEP 流程
design/288-inferencepool-ai-policies.mdInferencePool AI 策略(示例)
schema/cel.mdCEL 上下文 schema(必读)
schema/cel-functions.mdCEL 函数参考(必读)
schema/cel-functions.rs函数定义源
schema/cel.jsonCEL JSON schema
examples/traffic-a2a/config.yamlA2A 示例
examples/mcp-authentication/config.yamlMCP 认证示例
examples/mcp-authorization/config.yamlMCP 授权示例
examples/traffic-jwt-sign/JWT 签名示例
examples/traffic-token-exchange/Token Exchange 示例
examples/traffic-oidc/OIDC 示例
examples/traffic-tailscale-auth/Tailscale auth 示例

关键 RBAC 规则测试

文件测试目标
http/authorization_tests.rsRBAC 三态规则、合并
mcp/rbac.rs 内 test 模块MCP RBAC 完整覆盖
http/oidc/tests.rsOIDC 浏览器登录
http/jwt_tests.rsJWT 模式(Strict/Optional/Permissive)
http/apikey_tests.rsAPI Key 恒时比较
http/ext_authz_tests.rsext_authz 协议

本报告基于 agentgateway/agentgateway v1.0.0 之后的源码(commit hash 与 1.3.0-alpha.1 CHANGELOG 描述一致)。 仓库地址:/Users/sphinx.shi/workspace/research/agentgateway/ 报告地址:/Users/sphinx.shi/workspace/research/agentgateway-analysis/

基于 VitePress 构建