fix(x402): inject agent upstream auth#556
Conversation
46e63fd to
f4bd11f
Compare
OisinKyne
left a comment
There was a problem hiding this comment.
I hope those service offers really can't leak those secrets.... maybe lets talk about it on sync
|
@OisinKyne — chased your concern and posting the answer here so we have it on the record. Can a ServiceOffer leak the But there's a related confused-deputy issue worth fixing now. Not exploitable today (single Hermes mother → no second tenant to confuse the deputy against; mother already has cluster-wide `agents`/`secrets` write over its children). It becomes exploitable as soon as we split sub-agent SAs out of the mother or land multi-mother marketplace mode. Proposed fix — single-line invariant in `resolveAgentOffer`, fail-closed: ```go Plus one regression test in `agent_resolver_test.go` asserting the cross-namespace ref returns an error and leaves `status.AgentResolution` nil, and one in `serviceoffer_source_test.go` asserting an offer with mismatched ref produces zero routes. Pushing this as a follow-up PR — calling it out here first so we can agree on the invariant on the sync. |
|
Aggregated into #566 (release/v0.10.0-rc8 → main). The discussion thread above — in particular @OisinKyne's approval review and the follow-up analysis on the API_SERVER_KEY leak question — is the canonical review record for the upstream-auth fix and the confused-deputy guard in #564. |
Summary
Fixes the
obol sell agentpaid path where the x402 verifier successfully verifies payment, forwards the request to the seller's per-agent Hermes upstream, and then receives401 Invalid API keybecause Hermes requiresAPI_SERVER_KEYbut the verifier had no way to inject it.The fix keeps the current security boundary: Hermes remains bearer-token protected, buyers never see the key, and the verifier injects
Authorization: Bearer <API_SERVER_KEY>only after x402 verification has accepted the request.This PR also pins the embedded verifier deployment to a branch-built multi-arch image containing this source change:
ghcr.io/obolnetwork/x402-verifier:46e63fd@sha256:a8cd7946884c9a702b5cfcfad28d1f5eac1037899303eb4e0157e3ffab7a572cArchitecture
Before this change, agent offers produced a valid paid route, but the route source only knew how to read LiteLLM upstream credentials:
sequenceDiagram participant Buyer participant Verifier as x402-verifier participant SO as ServiceOffer route source participant Hermes as per-agent Hermes SO->>SO: Watch ServiceOffers SO->>SO: Watch litellm-secrets only SO->>Verifier: RouteRule{UpstreamURL: Hermes, UpstreamAuth: empty} Buyer->>Verifier: POST /services/agent/... + X-PAYMENT Verifier->>Verifier: Verify x402 payment Verifier->>Hermes: Forward request without Authorization Hermes-->>Verifier: 401 Invalid API key Verifier-->>Buyer: 401, no settlementAfter this change, the verifier watches the per-agent API secret and uses the existing
RouteRule.UpstreamAuthinjection mechanism:sequenceDiagram participant Buyer participant Verifier as x402-verifier participant SO as ServiceOffer route source participant Secret as hermes-api-server Secret participant Hermes as per-agent Hermes SO->>Secret: Watch hermes-api-server in agent namespaces SO->>SO: Decode API_SERVER_KEY SO->>Verifier: RouteRule{UpstreamURL: Hermes, UpstreamAuth: Bearer key} Buyer->>Verifier: POST /services/agent/... + X-PAYMENT Verifier->>Verifier: Verify x402 payment Verifier->>Hermes: Forward request with Authorization Hermes-->>Verifier: 200 response Verifier->>Verifier: Settle after upstream success Verifier-->>Buyer: 200 + X-PAYMENT-RESPONSEChanges
litellm-secretsandhermes-api-server.type=agentServiceOffers, resolveUpstreamAuthfromspec.agent.ref.namespaceso cross-namespace references use the agent namespace, not the offer namespace.x402-verifierClusterRole to read/watch only the additionalhermes-api-serverSecret name.Validation
Image publication:
Remaining smoke gate
Full flow smoke is running from a combined branch with forced local dev images, but it is currently blocked on the local sudo prompt. The LLM-gated dual-stack flows are also blocked because
silvermesh.v1337.lan:8081is reachable on the LAN but refusing TCP connections.