{"record":{"id":"62b7d3f5044b7cc2","repo":"juanfont/headscale","slug":"errtrustedproxyzerorange","errorCode":"errTrustedProxyZeroRange","errorMessage":"trusted_proxies[%d] %q: 0.0.0.0/0 and ::/0 are not allowed","messagePattern":"trusted_proxies\\[(.+?)\\] %q: 0\\.0\\.0\\.0/0 and ::/0 are not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/types/config.go","lineNumber":1080,"sourceCode":"}\n\n// trustedProxies rejects 0.0.0.0/0 and ::/0 because they defeat the\n// peer-trust gate and almost always indicate misconfiguration.\nfunc trustedProxies() ([]netip.Prefix, error) {\n\traw := viper.GetStringSlice(\"trusted_proxies\")\n\tif len(raw) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tout := make([]netip.Prefix, 0, len(raw))\n\tfor i, s := range raw {\n\t\tp, err := netip.ParsePrefix(s)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"trusted_proxies[%d] %q: %w\", i, s, err)\n\t\t}\n\n\t\tif p.Bits() == 0 {\n\t\t\treturn nil, fmt.Errorf(\"trusted_proxies[%d] %q: %w\", i, s, errTrustedProxyZeroRange)\n\t\t}\n\n\t\tout = append(out, p.Masked())\n\t}\n\n\treturn out, nil\n}\n\n// LoadCLIConfig returns the needed configuration for the CLI client\n// of Headscale to connect to a Headscale server.\nfunc LoadCLIConfig() (*Config, error) {\n\tlogConfig := logConfig()\n\tzerolog.SetGlobalLevel(logConfig.Level)\n\n\treturn &Config{\n\t\tDisableUpdateCheck: viper.GetBool(\"disable_check_updates\"),\n\t\tUnixSocket:         viper.GetString(\"unix_socket\"),\n\t\tCLI: CLIConfig{","sourceCodeStart":1062,"sourceCodeEnd":1098,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/types/config.go#L1062-L1098","documentation":"Returned when a trusted_proxies entry is 0.0.0.0/0 or ::/0 (prefix length 0). Trusting every possible source defeats the peer-trust gate that validates X-Forwarded-For/X-Real-IP headers, so headscale rejects it as an almost-certain misconfiguration rather than silently trusting spoofable client IPs.","triggerScenarios":"Configuring trusted_proxies: [\"0.0.0.0/0\"] or [\"::/0\"] and loading the server configuration. Detected via p.Bits() == 0 after successful prefix parsing.","commonSituations":"Operators copy a permissive allow-all CIDR from other proxy configs (nginx set_real_ip_from style) or try to 'make it work' when their proxy's address keeps changing. It can enable IP spoofing against ACLs or rate limits keyed on client IP.","solutions":["Restrict trusted_proxies to the actual CIDR(s) your reverse proxy lives in, e.g. 172.16.0.0/12","If the proxy address is dynamic, put it in a dedicated Docker/Kubernetes network and trust that subnet","Remove trusted_proxies entirely and pass the real client IP out-of-band if no fixed proxy subnet exists"],"exampleFix":"# before\ntrusted_proxies:\n  - 0.0.0.0/0\n# after\ntrusted_proxies:\n  - 172.16.0.0/12","handlingStrategy":"validation","validationCode":"import \"net/netip\"\n\nfunc assertNoZeroRangeProxies(raw []string) error {\n\tfor i, s := range raw {\n\t\tp, err := netip.ParsePrefix(s)\n\t\tif err != nil {\n\t\t\tcontinue // parse error handled separately\n\t\t}\n\t\tif p.Bits() == 0 {\n\t\t\treturn fmt.Errorf(\"entry %d (%q) is an all-ips range; refuse to trust everyone\", i, s)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never carry allow-all CIDRs (0.0.0.0/0, ::/0) into trusted_proxies","Document the proxy network CIDR next to the config key so operators copy the right value","Add a config lint rule that fails CI on any /0 entry in trusted_proxies"],"tags":["config","security","proxy","startup"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}