{"record":{"id":"152f1f3c17641545","repo":"gofiber/fiber","slug":"w-s-lookup-failed-w","errorCode":null,"errorMessage":"%w: %s lookup failed: %w","messagePattern":"%w: (.+?) lookup failed: %w","errorType":"validation","errorClass":"ErrUpstreamHostBlocked","httpStatus":null,"severity":"error","filePath":"middleware/proxy/security.go","lineNumber":402,"sourceCode":"func validateHostForSSRF(host string) error {\n\tif host == \"\" {\n\t\treturn ErrUpstreamHostInvalid\n\t}\n\t// strip brackets from IPv6 literals (url.Hostname already does this\n\t// in most cases, but keep the guard for defensive callers).\n\thost = trimBrackets(host)\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif isBlockedIP(ip) {\n\t\t\treturn fmt.Errorf(\"%w: %s\", ErrUpstreamHostBlocked, ip)\n\t\t}\n\t\treturn nil\n\t}\n\t// Bound the lookup so a slow resolver cannot stall the caller.\n\tctx, cancel := context.WithTimeout(context.Background(), dnsLookupTimeout)\n\tdefer cancel()\n\taddrs, err := dnsResolver.Load().LookupIPAddr(ctx, host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%w: %s lookup failed: %w\", ErrUpstreamHostBlocked, host, err)\n\t}\n\tif len(addrs) == 0 {\n\t\treturn fmt.Errorf(\"%w: %s has no addresses\", ErrUpstreamHostBlocked, host)\n\t}\n\tfor _, addr := range addrs {\n\t\tif isBlockedIP(addr.IP) {\n\t\t\treturn fmt.Errorf(\"%w: %s -> %s\", ErrUpstreamHostBlocked, host, addr.IP)\n\t\t}\n\t}\n\treturn nil\n}\n\n// newSSRFDialer returns a fasthttp DialFunc that resolves the target host\n// (with a bounded timeout), rejects the connection if any resolved\n// address falls in a blocked range, and then dials a validated address.\n// Performing the check at dial time — rather than only up front — defeats\n// DNS-rebinding attacks (the check/use gap) where a resolver returns a\n// public address during validation and a private one at connect time. It","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/proxy/security.go#L384-L420","documentation":"validateHostForSSRF performs a bounded DNS lookup (dnsLookupTimeout = 5s) when the host is not a literal IP. If LookupIPAddr fails — network/DNS error, NXDOMAIN with an error response, resolver unreachable, or the 5s timeout elapses — the error is wrapped with ErrUpstreamHostBlocked and the underlying resolver error preserved.","triggerScenarios":"Proxy target hostname cannot be resolved at validation time: NXDOMAIN, DNS server down, SERVFAIL, resolver timeout (>5s), hoster DNS rate-limiting, or IPv6-only resolver with no AAAA and a buggy return code.","commonSituations":"Transient DNS outage at deploy time; recently created hostname not yet propagated; private DNS zone only resolvable inside a VPC that the app host left; resolver config (/etc/resolv.conf) missing or pointing at a stale server.","solutions":["Verify the hostname resolves from the app host: dig +short hostname, nslookup hostname.","Confirm /etc/resolv.conf or container DNS policy points at a resolver that knows the zone (e.g. cluster DNS for in-cluster services).","Retry the request — transient resolver failures are common and the limiter/gateway layer can re-attempt validation.","If the hostname is internal, ensure the resolver reachable by the Go net package is the internal one (net.DefaultResolver).","For a deliberately unreachable target the operator accepts, wrap the call and treat ErrUpstreamHostBlocked as a 502 to the client."],"exampleFix":"// before: in-cluster service not resolvable from app netns\nproxy.Do(ctx, \"http://my-svc.cluster.local\")\n\n// after: correct resolver / dnsPolicy\n// k8s: dnsPolicy: ClusterFirst; ensure /etc/resolv.conf has the cluster DNS","handlingStrategy":"retry","validationCode":"// Pre-resolve to give an early, clear error.\nif _, err := net.LookupHost(host); err != nil {\n  return fmt.Errorf(\"upstream %q not resolvable: %w\", host, err)\n}","typeGuard":null,"tryCatchPattern":"if err := proxy.Do(c); err != nil {\n  if errors.Is(err, proxy.ErrUpstreamHostBlocked) && isDNSError(err) { return c.Status(502).SendString(\"upstream DNS failure\") }\n}","preventionTips":["Use a highly available resolver.","Confirm /etc/resolv.conf and container dnsPolicy are correct.","Retry transient resolver failures at the gateway layer.","Monitor resolver error rate."],"tags":["proxy","ssrf","dns","network","resolution"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}