rossoctl / rossoctl/cortex

security: a gateway's cost header is trusted after a plausibility check, not authenticated

Open
#1,027 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
13
Forks
40
Avg merge
12h 17m
Merged PRs (30d)
156

Description

A gateway's x-litellm-response-cost header is treated as authoritative after a plausibility
check, and nothing authenticates the host that sent it. A hostile proxied upstream can publish a
forged figure that is persisted in the daily ledger and counted toward MaxBudget enforcement.

This is a known boundary, not a discoverypricing.MaxPlausibleRequestCostMicros states it
in as many words:

A BLAST-RADIUS CAP, NOT AUTHENTICATION. It says nothing about who reported the figure and
cannot: a gateway's cost header is an unauthenticated string on a response, and any host a
client is proxied to can put any number in it. […] What it does NOT do: stop a forgery UNDER
the cap (that is spend an operator has to reconcile against the gateway's own accounting),
authenticate the reporting host (a host allowlist would, and remains the stronger fix), or
bound the accumulated sum.

So the cap bounds one forged request to $10,000 and the ledger's per-minute accumulator bounds
how many rows one caller can create, but neither answers "should this host be believed at all".

What closing it needs, and why it is not a follow-up line of code. Every option is a
configuration and product decision rather than a code change:

  • a trusted-upstream allowlist for cost headers (which hosts, and what happens to traffic from
    the rest — unpriced, or modelled from tokens instead?);
  • deriving trust from listener-established authentication, where there is any: the transparent
    inbound path has mTLS peer identity, the forward proxy generally does not;
  • stripping both cost headers by default and requiring an opt-in per endpoint, which changes the
    out-of-the-box coverage number every existing deployment sees.

The modelled path already refuses a figure past the same $10,000 ceiling
(pricing.Cost), so the two paths agree on blast radius; what remains is authenticity.

Raised by review of #1012. Recording it as an issue rather than widening that PR: it needs the
trust model decided first, and a change of this shape wants its own review.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the pricing.Cost and pricing.MaxPlausibleRequestCostMicros definitions, then review the context from #1012 and the gateway paths described here. The work is not ready for a code change until the trust model, accepted upstreams, and handling of untrusted headers are decided; done would be an agreed design ready for its own review.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.