Skip to content

Proxy: request signing / credential transformations (HMAC, AWS SigV4) #997

Description

@theoephraim

Summary

The credential proxy currently protects secrets that are used as static values: the agent holds a placeholder, and the proxy swaps in the real value (an API key, a bearer token) on the wire toward an allowlisted host. That covers most Authorization: Bearer … / X-Api-Key APIs.

It does not cover credentials that are never sent verbatim, but are instead used to compute a per-request signature. The canonical cases:

  • AWS SigV4 — the request is signed with the secret access key over a canonical form of the request (method, path, headers, body hash, timestamp). The secret itself is never transmitted.
  • Generic HMAC — many webhook/partner APIs require an HMAC-SHA256 of the request body (or a canonical string) under a shared secret, sent in a signature header.

For these, static substitution can't help: there's no placeholder to swap because the secret never appears in the request. To keep the broker model (the agent never holds the real key), the proxy has to hold the signing key and compute the signature at the wire, after the agent's request arrives and before it's forwarded upstream.

This is a natural extension of the existing @proxy model: same custody story (key stays in the proxy, in memory), same wire-injection boundary, just a computed value instead of a copied one.

Proposed surface

A signing mode on the @proxy rule, e.g.:

# @sensitive
# @proxy(domain="*.amazonaws.com", sign="aws-sigv4")
AWS_SECRET_ACCESS_KEY=yourPreferredPlugin()
# @sensitive
# @proxy(domain="api.partner.com", sign="hmac-sha256", signInto="header:x-signature")
PARTNER_WEBHOOK_SECRET=yourPreferredPlugin()

The proxy computes the signature over the (already policy-checked, already substituted) outbound request and writes it into the designated location, then forwards.

Scope / phasing

  • Phase 1: generic HMAC. Smallest, most broadly useful primitive. Needs: which secret, which algorithm, what canonical string (body vs. a named set of headers/path), and where the signature goes (signInto). This also becomes the building block the SigV4 implementation reuses.
  • Phase 2: AWS SigV4. The common real-world ask. Needs the access key id alongside the secret key, region/service (often derivable from the host or an explicit param), and the full canonical-request construction. Interacts with the substitution guards (SigV4 signs headers, so the guard model needs to understand a "computed" injection that isn't a placeholder swap).
  • Out of scope here: OAuth token exchange / minting short-lived tokens from long-lived material. Tracked separately.

Open questions

  • How does signing compose with the substitution guards? A signature isn't a placeholder occurrence, so substituteIn / maxOccurrences don't directly apply; signing needs its own "where does the computed value land" declaration (signInto).
  • SigV4 signs over the body and a timestamp, so it can't run on a streamed/unbounded request body: likely require a bounded body (same class of constraint as response redaction).
  • Region/service derivation for SigV4: infer from host, or require explicit params (region=, service=)?
  • Do we need per-request canonicalization hooks for less-standard HMAC schemes, or is a fixed "HMAC over raw body → header" enough for Phase 1?

Related

  • Request signing keeps the same custody + verified-upstream-TLS injection model as @proxy; it's an additional transform in the same pipeline, not a new boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions