JuliusBrussee/caveman · error

ssrf: destination %s (for host %q) is a private address bloc

Error message

ssrf: destination %s (for host %q) is a private address blocked in managed mode

What it means

checkAddr in ssrf: the address is RFC1918 private (10/8, 172.16/12, 192.168/16) and cfg.ManagedMode is true. Managed (multi-tenant) mode blocks all private ranges categorically — a tenant must not be able to probe the operator's internal network — with no allowlist bypass.

Source

Thrown at shared/platform/ssrf/ssrf.go:302

			if !cfg.ManagedMode {
				return nil
			}
			return fmt.Errorf("ssrf: destination %s (for host %q) is in blocked range %s", addr, host, p)
		}
	}

	for _, p := range blockedPrefixes {
		if p.Contains(addr) {
			// These ranges (link-local/metadata, ULA outside the narrow local-TUN
			// exception, multicast, unspecified, documentation) are absolutely
			// blocked — no allowlist escape in any mode.
			return fmt.Errorf("ssrf: destination %s (for host %q) is in blocked range %s", addr, host, p)
		}
	}

	if inRFC1918(addr) {
		if cfg.ManagedMode {
			return fmt.Errorf("ssrf: destination %s (for host %q) is a private address blocked in managed mode", addr, host)
		}
		// In self-hosted mode, RFC1918 is blocked unless the original hostname
		// OR the resolved IP literal appears in the allowlist.
		if !isInAllowList(host, port, cfg.AllowList) && !isInAllowList(addr.String(), port, cfg.AllowList) {
			return fmt.Errorf("ssrf: destination %s (for host %q) is a private address; add it to the allowlist to permit it", addr, host)
		}
	}
	return nil
}

func inRFC1918(addr netip.Addr) bool {
	for _, p := range privateRFC1918 {
		if p.Contains(addr) {
			return true
		}
	}
	return false
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Expose the internal service through a public/LB endpoint (https, port 443) and use that URL.
  2. If this deployment is actually self-hosted/single-tenant, set ManagedMode false — then the allowlist path applies.
  3. Never attempt to bypass: in managed mode the block is the security boundary.

Example fix

// before (managed deployment)
cfg := ssrf.Config{ManagedMode: true}
ssrf.ValidateURL(ctx, "https://10.0.4.12/api", cfg) // blocked

// after: use the public endpoint
ssrf.ValidateURL(ctx, "https://api.internal.example.com/api", cfg)
Defensive patterns

Strategy: validation

Validate before calling

if cfg.ManagedMode {
    if ip := net.ParseIP(host); ip != nil && ip.IsPrivate() {
        return fmt.Errorf("private addresses are not dialable in managed mode")
    }
}

Try / catch

if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {
    if cfg.ManagedMode && strings.Contains(err.Error(), "private address blocked in managed mode") {
        // ask for the public endpoint URL; no configuration fix exists
    }
}

Prevention

When it happens

Trigger: Validating or dialing any 10.x, 172.16-31.x, or 192.168.x address while ManagedMode is true, including via hostnames that resolve into those ranges.

Common situations: SaaS/managed deployment where a user configures a webhook to an internal address; operator mistakenly runs managed-mode config on-prem where the only reachable systems are RFC1918.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/c0a70ca672bb5cd5. Report an issue: GitHub.