{"record":{"id":"5cdca433858cbb5e","repo":"Tencent/WeKnora","slug":"connection-blocked-s-resolves-to-restricted-ip-5cdca4","errorCode":null,"errorMessage":"connection blocked: %s resolves to restricted IP %s (%s)","messagePattern":"connection blocked: (.+?) resolves to restricted IP (.+?) \\((.+?)\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":841,"sourceCode":"\t\t}\n\t}\n\n\t// Resolve the hostname once, validate every answer, and then dial one of\n\t// those exact IPs. Dialing the original hostname here would make the\n\t// standard dialer resolve it a second time, leaving a DNS-rebinding window\n\t// between validation and connection establishment.\n\tips, err := net.DefaultResolver.LookupIPAddr(ctx, host)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"DNS resolution failed for %s: %w\", host, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn nil, fmt.Errorf(\"DNS resolution returned no addresses for %s\", host)\n\t}\n\n\t// Validate all resolved IPs\n\tfor _, ipAddr := range ips {\n\t\tif restricted, reason := isRestrictedIP(ipAddr.IP); restricted {\n\t\t\treturn nil, fmt.Errorf(\"connection blocked: %s resolves to restricted IP %s (%s)\", host, ipAddr.IP.String(), reason)\n\t\t}\n\t}\n\n\t// If we get here, all IPs are safe. Pin the connection to the validated DNS\n\t// answers; TLS still uses the request hostname for SNI/certificate checks.\n\tdialer := &net.Dialer{\n\t\tTimeout:   30 * time.Second,\n\t\tKeepAlive: 30 * time.Second,\n\t}\n\tvar lastErr error\n\tfor _, ipAddr := range ips {\n\t\tpinnedAddr := net.JoinHostPort(ipAddr.IP.String(), port)\n\t\tconn, dialErr := dialer.DialContext(ctx, network, pinnedAddr)\n\t\tif dialErr == nil {\n\t\t\treturn conn, nil\n\t\t}\n\t\tlastErr = dialErr\n\t}","sourceCodeStart":823,"sourceCodeEnd":859,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L823-L859","documentation":"After resolving the hostname, SSRFSafeDialContext validates every returned IP with isRestrictedIP; if any answer is private, loopback, link-local, metadata, or otherwise restricted, the whole dial is blocked. This closes DNS-rebinding attacks where a public name resolves to an internal address. The reason string in the message names the specific restricted category.","triggerScenarios":"A hostname whose DNS answer includes any restricted IP (e.g. resolves to 127.0.0.1, 10.x/172.16.x/192.168.x, 169.254.169.254, or IPv6 equivalents) dialed through SSRFSafeDialContext / SSRFSafeGRPCDialer without a whitelist entry; malicious or misconfigured DNS records; hosts legitimately dual-homed onto private ranges.","commonSituations":"Attack or misconfiguration where a public domain points at 169.254.169.254 (metadata SSRF attempt); internal services whose DNS returns private IPs to the guarded client; VPN/split-horizon setups where the same name yields private answers internally.","solutions":["Read the reason in the error to see which restricted class fired, then verify the DNS records for the host and remove any pointing at private/loopback/metadata space.","If the host is legitimately internal and trusted, add it to the SSRF whitelist so dial-time IP checks are bypassed for it.","If you suspect rebinding/abuse, treat it as a security event: audit who configured the record and block the domain.","Dial the specific public IP literal if you know the correct address and it passes isRestrictedIP."],"exampleFix":"// before\n// evil.example.com resolves to 169.254.169.254\nconn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", \"evil.example.com:80\")\n\n// after\n// correct the DNS record to a public IP, then dial\nconn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", \"api.example.com:443\") // or whitelist trusted internal host","handlingStrategy":"validation","validationCode":"ips, _ := net.LookupIP(host)\nfor _, ip := range ips {\n    if restricted, reason := utils.IsRestrictedIPCheck(ip); restricted { // or reuse library's isRestrictedIP via exported helper\n        return fmt.Errorf(\"%s resolves to restricted IP (%s); fix DNS or whitelist host\", host, reason)\n    }\n}","typeGuard":null,"tryCatchPattern":"conn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", addr)\nif err != nil && strings.Contains(err.Error(), \"resolves to restricted IP\") {\n    // do NOT retry — treat as either misconfigured DNS or an SSRF attempt; alert and fail closed\n    return nil, fmt.Errorf(\"possible DNS rebinding or internal-target attempt: %w\", err)\n}","preventionTips":["Treat this error as a potential security event, not a transient failure — never auto-retry.","Audit DNS records for hosts that mix public names with private answers.","Whitelist genuinely internal, trusted hosts instead of weakening checks.","Keep the restricted-IP lists updated with new cloud metadata ranges."],"tags":["ssrf","dns-rebinding","restricted-ip","security"],"backgroundTag":"ssrf-request-blocked","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}