Skip to content

Add support for private-network upstreams by exempting hosts from the SSRF dial gate - #51

Open
Tilian wants to merge 2 commits into
git-pkgs:mainfrom
Tilian:feat/allow-private-hosts
Open

Add support for private-network upstreams by exempting hosts from the SSRF dial gate#51
Tilian wants to merge 2 commits into
git-pkgs:mainfrom
Tilian:feat/allow-private-hosts

Conversation

@Tilian

@Tilian Tilian commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Currently the safehttp dial gate refuses loopback and link-local addresses on every upstream dial.

When the proxy runs inside a private network next to its upstream (the Kubernetes use-case of pointing an upstream at a local registry service like maven-mirror.internal.svc.cluster.local, which resolves to a cluster IP) the gate makes that upstream unreachable by design.
Today the only workaround is going through a public ingress, which costs an extra network round-trip and forces an otherwise-internal registry to be externally exposed.

@Tilian

Tilian commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Context: We want to support a multitude of Maven upstreams, so we have a Reposilite instance sitting in between our proxy instance(s) and the actual upstreams. This now needs a public ingress to work.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an explicit per-host allowlist to relax the SSRF dial gate for specific upstreams, enabling proxy deployments where upstream registries are only reachable via private network IPs (e.g., Kubernetes ClusterIP-backed services).

Changes:

  • Added WithAllowPrivateHosts(...) option to whitelist specific upstream hosts for relaxed SSRF dial gating.
  • Introduced Fetcher.gateOptions(host) to select safehttp gate behavior per dial target.
  • Added unit test coverage for the new allowlist behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
fetch/fetcher.go Adds host allowlist storage and per-host selection of safehttp.CheckIP options during dialing.
fetch/fetcher_test.go Adds a unit test validating allowlist normalization and strict-by-default behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread fetch/fetcher.go
Comment on lines +113 to +134
// WithAllowPrivateHosts exempts the named hosts from the dial gate's loopback and private-address checks.
func WithAllowPrivateHosts(hosts ...string) Option {
return func(f *Fetcher) {
if f.allowPrivate == nil {
f.allowPrivate = make(map[string]bool, len(hosts))
}
for _, h := range hosts {
if h = strings.TrimSpace(h); h != "" {
f.allowPrivate[strings.ToLower(h)] = true
}
}
}
}

// gateOptions returns the safehttp options for a dial to host.
// Zero-value strict gate unless the host was whitelisted via WithAllowPrivateHosts.
func (f *Fetcher) gateOptions(host string) safehttp.Options {
if f.allowPrivate[strings.ToLower(host)] {
return safehttp.Options{AllowLoopback: true, AllowPrivate: true}
}
return safehttp.Options{}
}
Comment thread fetch/fetcher.go
Comment on lines +129 to +132
func (f *Fetcher) gateOptions(host string) safehttp.Options {
if f.allowPrivate[strings.ToLower(host)] {
return safehttp.Options{AllowLoopback: true, AllowPrivate: true}
}
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.

2 participants