stacklok / stacklok/toolhive

Harden DCR resolver against SSRF when operator trust boundary changes

Open
#5,135 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

authentication enhancement go security
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Context

Raised by code review on #5042 (comment).

DCRUpstreamConfig.DiscoveryURL and DCRUpstreamConfig.RegistrationEndpoint are operator-supplied URLs validated only for HTTPS-or-loopback. There is no allowlist or RFC1918 / link-local / metadata-service guard. The resolveDCRCredentials resolver issues outbound requests to whatever the operator configures — possibly with the RFC 7591 initial access token attached.

Today the operator role is fully trusted, so this is acceptable. The runtime trust assumption is now documented on DCRUpstreamConfig itself (see pkg/authserver/config.go).

When this becomes a problem

If the trust boundary ever changes — e.g.:

  • A multi-tenant operator deployment where a tenant role can populate DCRConfig.
  • Federated CRD authoring across less-trusted namespaces.
  • A future surface that lets less-privileged roles supply DCR fields.

…then the resolver becomes a confused-deputy: an attacker who can write to the field can coerce the authserver into making outbound requests to internal addresses (cloud-provider metadata services, RFC1918 ranges, link-local 169.254.169.254, etc.), with the bearer token attached.

Hardening options

  1. Block RFC1918 / loopback (when not explicitly opted in) / link-local / cloud metadata addresses at validation time. pkg/networking.IsPrivateIP already exists.
  2. Operator-level allowlist of permitted DCR hosts.
  3. Egress-controller / network-policy approach (out-of-process).

Acceptance

  • DCR resolver rejects URLs resolving to private / link-local / metadata-service addresses unless explicit opt-in.
  • Loopback-for-development carve-out preserved (existing behavior).
  • Documented in DCRUpstreamConfig doc comment, replacing the trust-assumption note.

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 with resolveDCRCredentials and the DCRUpstreamConfig definition in pkg/authserver/config.go, then inspect pkg/networking.IsPrivateIP. Compare the existing HTTPS-or-loopback validation with the acceptance criteria and determine how private, link-local, and metadata addresses should be handled while preserving the development carve-out. Done means the resolver enforces the selected protection and the DCRUpstreamConfig comment documents the updated trust boundary.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend-api-design, networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.