ssrfguard

package
v0.4.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

Documentation

Overview

Package ssrfguard is an opt-in SSRF egress guard for flate's outbound source fetches. When flate renders UNTRUSTED input (e.g. konflate rendering a fork PR), a fork can place attacker-chosen URLs in the tree — kustomize remote resources/bases and GitRepository/OCIRepository/HelmRepository/Bucket spec.url — which flate would otherwise fetch server-side from inside the cluster, reaching cloud metadata (169.254.169.254), in-cluster services, and RFC1918/loopback hosts.

The guard installs a net.Dialer.Control hook on every fetch transport (via WrapTransport, wired into source.NewHTTPTransport, the helm getter, and the kustomize remote-fetch client). Control runs at dial time, AFTER DNS resolution, for every connection INCLUDING redirect targets, so one hook closes both DNS-rebinding and redirect SSRF without a CheckRedirect.

It is OFF by default and gated by a single process-global toggle (Restrict): flate normally renders TRUSTED repos that legitimately fetch from private/LAN hosts (self-hosted Gitea, in-cluster registries), so an always-on private block would break the common case. A consumer rendering untrusted input (konflate fork mode, or the `flate --restrict-egress` flag) turns it on. The toggle is process-global, not per-render, because go-git v5 installs its HTTPS transport on a process-global protocol map with no per-clone hook — so a per-render git egress policy is impossible, and a process-global toggle is the honest model. Set it once at process init; last writer wins.

Boundaries (deliberately NOT covered):

  • A configured HTTP proxy is a trust boundary the operator owns: with Transport.Proxy set, the dial (and thus Control) sees the proxy IP, not the CONNECT target.
  • SSH git (ssh://) dials via ssh.Dial, outside this HTTP transport; SSH to a metadata/RFC1918 endpoint is implausible and out of scope.
  • This blocks reaching INTERNAL addresses; full egress-deny (incl. public hosts) is the consumer's NetworkPolicy job.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Blocked

func Blocked(a netip.Addr) bool

Blocked reports whether a resolved address must not be dialed. It is pure (no I/O) and is the unit-testable core of the guard. The standard SSRF set (loopback, RFC1918+ULA, link-local incl. 169.254.169.254, multicast, unspecified) plus the ranges and IPv6-embedded-IPv4 forms net/netip misses.

func Restrict

func Restrict(on bool)

Restrict enables or disables the egress guard process-wide. The orchestrator sets it from Config.RestrictEgress; tests reset it. Safe for concurrent use, but it is a single process-level setting (last writer wins) — see the package doc on why per-render is not possible.

func Restricted

func Restricted() bool

Restricted reports whether the guard is currently enabled.

func WrapTransport

func WrapTransport(tr *http.Transport)

WrapTransport installs the guard's Control hook on tr's dialer. It mirrors http.DefaultTransport's dialer settings (Timeout/KeepAlive) so that with the guard off the transport behaves identically to an unwrapped one. Idempotent and safe to call on every transport flate builds.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL