{"record":{"id":"03dd1ba00b7a07fc","repo":"MHSanaei/3x-ui","slug":"blocked-private-internal-address-s-03dd1b","errorCode":null,"errorMessage":"blocked private/internal address %s","messagePattern":"blocked private/internal address (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/web/service/url_safety.go","lineNumber":67,"sourceCode":"\tif allowPrivate {\n\t\treturn clean, nil\n\t}\n\tu, err := url.Parse(clean)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\tif err := rejectPrivateHost(ctx, u.Hostname()); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn clean, nil\n}\n\nfunc rejectPrivateHost(ctx context.Context, hostname string) error {\n\tif ip := net.ParseIP(hostname); ip != nil {\n\t\tif isBlockedIP(ip) {\n\t\t\treturn fmt.Errorf(\"blocked private/internal address %s\", ip.String())\n\t\t}\n\t\treturn nil\n\t}\n\tips, err := net.DefaultResolver.LookupIPAddr(ctx, hostname)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot resolve host %s: %w\", hostname, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn fmt.Errorf(\"host %s has no IP addresses\", hostname)\n\t}\n\tfor _, ipAddr := range ips {\n\t\tif isBlockedIP(ipAddr.IP) {\n\t\t\treturn fmt.Errorf(\"host %s resolves to blocked private/internal address %s\", hostname, ipAddr.IP.String())\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/url_safety.go#L49-L85","documentation":"Thrown by rejectPrivateHost (reached via SanitizePublicHTTPURL, the variant that enforces SSRF rules at request time) when the URL's hostname is a literal IP that falls in a blocked range. netsafe.IsBlockedIP blocks loopback, RFC1918/private, link-local, multicast, unspecified, and typically IPv6 ULA/link-local ranges, so the panel never makes outbound requests into its own network stack.","triggerScenarios":"Passing 'http://127.0.0.1:port/...', 'http://10.0.0.5/...', 'http://192.168.1.1/', 'http://[::1]/...', or 'http://169.254.169.254/' (cloud metadata) as an outbound URL to any feature that routes through SanitizePublicHTTPURL.","commonSituations":"Admins pointing a health-check/URL-test at a local reverse proxy or another service on the same box; attempts (accidental or malicious) to reach Docker's 172.17.x.x bridge or the cloud metadata IP; localhost shortcuts copied from dev configs into production panel settings.","solutions":["Point the URL at a genuinely public host; if the target is an internal service, do not route it through the SSRF-checked outbound path — expose it via a purpose-built config (e.g. a local inbound) instead.","If the destination is public but accessed via a private NAT address, use its public DNS name (note: the DNS-resolved form is also checked, see the 'resolves to blocked' sibling error).","Never try to bypass with decimal/octal IP encodings — url parsing normalizes most, and the resolve-time check catches the rest; treat a genuine need for private targets as a design change, not a workaround."],"exampleFix":"// before\nu := \"http://127.0.0.1:9090/health\" // health-check to a local sidecar\n\n// after — use the service's public name, or move the check off the SSRF-guarded path\nu := \"https://sidecar.example.com/health\"","handlingStrategy":"validation","validationCode":"// Reject private IP literals before they ever reach the outbound path\nfunc isSafeOutboundHost(host string) bool {\n    if ip := net.ParseIP(host); ip != nil {\n        return !netsafe.IsBlockedIP(ip)\n    }\n    return true // names get the resolve-time check\n}","typeGuard":"null","tryCatchPattern":"if _, err := service.SanitizePublicHTTPURL(u); err != nil {\n    if strings.Contains(err.Error(), \"blocked private/internal address\") {\n        // configuration error: switch to a public target; never retry as-is\n    }\n}","preventionTips":["Keep all health-check/URL-test targets on public DNS names by convention.","Never point outbound-checked URLs at 169.254.169.254 or RFC1918 space; use dedicated internal config paths instead."],"tags":["ssrf","network","security","url"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}