{"record":{"id":"a50a0b67b27838b3","repo":"juanfont/headscale","slug":"generating-random-ip-w","errorCode":null,"errorMessage":"generating random IP: %w","messagePattern":"generating random IP: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/ip.go","lineNumber":266,"sourceCode":"\tvar from, to big.Int\n\n\tfrom.SetBytes(fromIP.AsSlice())\n\tto.SetBytes(toIP.AsSlice())\n\n\t// Find the max, this is how we can do \"random range\",\n\t// get the \"max\" as 0 -> to - from and then add back from\n\t// after.\n\ttempMax := big.NewInt(0).Sub(&to, &from)\n\n\t// A single-address prefix (/32 or /128) has from == to, so tempMax is 0 and\n\t// rand.Int would panic on a non-positive bound. Return the sole address.\n\tif tempMax.Sign() <= 0 {\n\t\treturn fromIP, nil\n\t}\n\n\tout, err := rand.Int(rand.Reader, tempMax)\n\tif err != nil {\n\t\treturn netip.Addr{}, fmt.Errorf(\"generating random IP: %w\", err)\n\t}\n\n\tvalInRange := big.NewInt(0).Add(&from, out)\n\n\t// big.Int.Bytes() strips leading zero bytes, so a value with a zero high\n\t// byte yields a too-short slice that AddrFromSlice rejects. Pad to the\n\t// prefix's address width.\n\tip, ok := netip.AddrFromSlice(valInRange.FillBytes(make([]byte, len(fromIP.AsSlice()))))\n\tif !ok {\n\t\treturn netip.Addr{}, errGeneratedIPBytesInvalid\n\t}\n\n\tif !pfx.Contains(ip) {\n\t\treturn netip.Addr{}, fmt.Errorf(\n\t\t\t\"%w: ip(%s) not in prefix(%s)\",\n\t\t\terrGeneratedIPNotInPrefix,\n\t\t\tip.String(),\n\t\t\tpfx.String(),","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/ip.go#L248-L284","documentation":"randomNext picks a uniform random offset within the prefix using crypto/rand's rand.Int over the range size. This error means the system entropy source returned an error — the read from rand.Reader failed. Single-address prefixes (from == to) are short-circuited before this call, so this specifically signals an entropy/reader fault.","triggerScenarios":"rand.Reader read failure: blocked /dev/random in a restricted sandbox, a broken getrandom(2) under a strict seccomp/apparmor profile, or kernel RNG not yet initialized early in boot (rare on modern Linux).","commonSituations":"Minimal Docker images with restrictive default seccomp denying getrandom; freshly booted VMs; nested virtualization environments; never on correctly configured modern hosts.","solutions":["Use sequential allocation strategy to remove the dependency on randomness.","Loosen the container security profile to allow getrandom(2) (Docker's default seccomp already does).","Ensure the host kernel RNG is initialized before headscale starts (check `cat /proc/sys/kernel/random/entropy_avail`)."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import \"crypto/rand\"\n\nfunc entropyHealthy() error {\n    b := make([]byte, 16)\n    if _, err := rand.Read(b); err != nil {\n        return fmt.Errorf(\"entropy source unavailable: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// errors.Is on the wrapped crypto/rand error; fall back to sequential\n// allocation — never to math/rand for address selection in security-\n// relevant deployments, though for pure uniqueness sequential is fine.","preventionTips":["Allow getrandom(2) in container seccomp profiles.","Wait for kernel RNG initialization before starting services at early boot."],"tags":["crypto-rand","entropy","sandbox","ip-allocation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}