{"record":{"id":"c0a70ca672bb5cd5","repo":"JuliusBrussee/caveman","slug":"ssrf-destination-s-for-host-q-is-a-private-ad","errorCode":null,"errorMessage":"ssrf: destination %s (for host %q) is a private address blocked in managed mode","messagePattern":"ssrf: destination (.+?) \\(for host %q\\) is a private address blocked in managed mode","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/ssrf/ssrf.go","lineNumber":302,"sourceCode":"\t\t\tif !cfg.ManagedMode {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"ssrf: destination %s (for host %q) is in blocked range %s\", addr, host, p)\n\t\t}\n\t}\n\n\tfor _, p := range blockedPrefixes {\n\t\tif p.Contains(addr) {\n\t\t\t// These ranges (link-local/metadata, ULA outside the narrow local-TUN\n\t\t\t// exception, multicast, unspecified, documentation) are absolutely\n\t\t\t// blocked — no allowlist escape in any mode.\n\t\t\treturn fmt.Errorf(\"ssrf: destination %s (for host %q) is in blocked range %s\", addr, host, p)\n\t\t}\n\t}\n\n\tif inRFC1918(addr) {\n\t\tif cfg.ManagedMode {\n\t\t\treturn fmt.Errorf(\"ssrf: destination %s (for host %q) is a private address blocked in managed mode\", addr, host)\n\t\t}\n\t\t// In self-hosted mode, RFC1918 is blocked unless the original hostname\n\t\t// OR the resolved IP literal appears in the allowlist.\n\t\tif !isInAllowList(host, port, cfg.AllowList) && !isInAllowList(addr.String(), port, cfg.AllowList) {\n\t\t\treturn fmt.Errorf(\"ssrf: destination %s (for host %q) is a private address; add it to the allowlist to permit it\", addr, host)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc inRFC1918(addr netip.Addr) bool {\n\tfor _, p := range privateRFC1918 {\n\t\tif p.Contains(addr) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/ssrf/ssrf.go#L284-L320","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Expose the internal service through a public/LB endpoint (https, port 443) and use that URL.","If this deployment is actually self-hosted/single-tenant, set ManagedMode false — then the allowlist path applies.","Never attempt to bypass: in managed mode the block is the security boundary."],"exampleFix":"// before (managed deployment)\ncfg := ssrf.Config{ManagedMode: true}\nssrf.ValidateURL(ctx, \"https://10.0.4.12/api\", cfg) // blocked\n\n// after: use the public endpoint\nssrf.ValidateURL(ctx, \"https://api.internal.example.com/api\", cfg)","handlingStrategy":"validation","validationCode":"if cfg.ManagedMode {\n    if ip := net.ParseIP(host); ip != nil && ip.IsPrivate() {\n        return fmt.Errorf(\"private addresses are not dialable in managed mode\")\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {\n    if cfg.ManagedMode && strings.Contains(err.Error(), \"private address blocked in managed mode\") {\n        // ask for the public endpoint URL; no configuration fix exists\n    }\n}","preventionTips":["In managed deployments, expose internal services through public LBs and validate endpoints at intake.","Do not copy self-hosted config (private IPs) into managed environments."],"tags":["ssrf","network","security","rfc1918","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}