Skip to content

Harden proxy request handling: 32 MiB body cap, loopback backend gate, fail-closed peer TLS - #79

Open
mkalkere wants to merge 3 commits into
NVIDIA:mainfrom
mkalkere:fix/proxy-request-hardening
Open

mkalkere wants to merge 3 commits into
NVIDIA:mainfrom
mkalkere:fix/proxy-request-hardening

Conversation

@mkalkere

@mkalkere mkalkere commented Sep 14, 2026

Copy link
Copy Markdown

Description

Hardens the request path shared by both inference proxies (services/ollama-proxy, services/lmstudio-proxy): a loopback HTTP ingress plus a cluster mTLS ingress, ordered failover across node candidates, and model eligibility checks before routing. Three fixes, no behavior change for legitimate traffic, no JSON-RPC surface changes:

  1. Request bodies are capped at 32 MiB. handleHTTP buffered the entire body (io.ReadAll) so failover attempts could replay it -- uncapped, so any loopback client could grow the proxy process until it OOMed. bufferBodyAndModel now reads through a LimitReader; over-cap bodies get a 413 JSON error before candidate selection or any engine work.
  2. The local backend target is now gated to loopback. The "always loopback" invariant was documented but never enforced -- the broker-supplied host was dialed unchecked. Non-loopback backends are now refused.
  3. An unpinned peer now fails closed. Previously a pin-less peer fell back to an unpinned transport object; now it returns an always-failing transport and dial errors flow through the existing failover/502 paths.

Scope

Included: P1+P2+P3 in both proxies, proxy_hardening_test.go in both, version bumps, docs/proxy-request-hardening.mdx. Excluded: the proxies' loopback CORS posture and unpinned download paths -- deliberately left alone as separate concerns.

Validation

  • go test -race ./... in services/ollama-proxy and services/lmstudio-proxy: pass.
  • New tests cover: over-cap body returns 413 with a JSON error body; at-cap body accepted; non-loopback backends (192.168.1.5, 10.0.0.2, example.com, ::ffff:192.168.1.5) refused; 127.0.0.1/::1 accepted; unpinned peer dial fails closed.
  • Toolchain: Go 1.26.8 (repo requires Go 1.25+), Linux sandbox. go vet clean, gofmt clean, node scripts/spdx-headers.mjs reports 0 missing headers on every branch.
  • The services/tests cross-process suite was not re-run: mDNS is blocked in this sandbox and those tests fail identically on pristine upstream.

Risk

  • The 32 MiB cap is a behavior change for anyone sending larger single inference requests -- over-cap requests now fail fast with 413 instead of being proxied.
  • The loopback gate assumes the broker always sends 127.0.0.1; verified in nvpair-ui-broker/advertiser.go.
  • Fail-closed peers surface as 502s through existing failover paths if a peer legitimately lacks a pin (pins are provisioned at pairing).

Checklist

  • I have read the Contributing Guidelines.
  • Every commit is signed off (git commit -s), certifying the Developer Certificate of Origin.
  • New or existing tests cover the change.
  • Relevant documentation is updated.
  • I checked the diff, changed filenames, and commit messages for credentials, private data, internal URLs, internal issue identifiers, and generated artifacts.
  • I recorded the validation commands and results above.
  • I bumped any affected component in services/versions.json, and described user-visible changes above so they reach the release notes.

Cap proxied request bodies at 32 MiB (413 before routing), enforce the
loopback-only local engine invariant, and fail closed when a peer
certificate pin is missing.

Signed-off-by: mkalkere <14184493+mkalkere@users.noreply.github.com>
Same three fixes as the Ollama proxy: 32 MiB request body cap with a 413
before routing, loopback-only local engine gate, and fail-closed peer
transports when the certificate pin is absent.

Signed-off-by: mkalkere <14184493+mkalkere@users.noreply.github.com>
Sequence and flow diagrams for the body cap, the loopback backend gate,
and fail-closed peer TLS, plus a reading-order entry in the README.

Signed-off-by: mkalkere <14184493+mkalkere@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant