{"record":{"id":"f62c2a47ddff19d8","repo":"knadh/listmonk","slug":"error-generating-altcha-hmac-key-v","errorCode":null,"errorMessage":"error generating Altcha HMAC key: %v","messagePattern":"error generating Altcha HMAC key: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/captcha/captcha.go","lineNumber":86,"sourceCode":"\t\t\tTimeout: timeout,\n\t\t\tTransport: &http.Transport{\n\t\t\t\tMaxIdleConnsPerHost:   10,\n\t\t\t\tMaxConnsPerHost:       100,\n\t\t\t\tResponseHeaderTimeout: timeout,\n\t\t\t\tIdleConnTimeout:       timeout,\n\t\t\t},\n\t\t},\n\t}\n\n\t// Determine which provider is enabled\n\tif o.Altcha.Enabled {\n\t\tc.provider = ProviderAltcha\n\n\t\t// Generate an random HMAC key for Altcha.\n\t\tb := make([]byte, 24) // 24 bytes will give 32 characters when base64 encoded\n\t\t_, err := rand.Read(b)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"error generating Altcha HMAC key: %v\", err))\n\t\t}\n\t\thmacKey := base64.URLEncoding.EncodeToString(b)[:32]\n\n\t\tc.altcha = altchaOpt{\n\t\t\tComplexity: o.Altcha.Complexity,\n\t\t\tHMACKey:    hmacKey,\n\t\t}\n\t} else if o.HCaptcha.Enabled {\n\t\tc.provider = ProviderHCaptcha\n\t\tc.hCaptcha = hCaptchaOpt{\n\t\t\tSecret: o.HCaptcha.Secret,\n\t\t}\n\t}\n\n\treturn c\n}\n\n// IsEnabled returns true if any captcha provider is enabled.","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/captcha/captcha.go#L68-L104","documentation":"During New(), when the captcha provider is ALTCHA, a random 24-byte HMAC key is generated with crypto/rand and base64-encoded. If rand.Read fails (the OS cryptographic entropy source is unavailable), the code panics because it cannot construct a secure Altcha provider without a key.","triggerScenarios":"Calling New (via initCaptcha) with ProviderAltcha configured while crypto/rand.Read returns an error — typically when the OS entropy pool is exhausted or /dev/urandom is unavailable in a restricted container.","commonSituations":"Highly restricted Docker/container sandboxes without proper /dev/urandom access, extremely low-entropy embedded systems or VMs at boot, or seccomp policies blocking the getrandom syscall.","solutions":["Ensure the container/host exposes /dev/urandom and does not block the getrandom syscall (fix seccomp/AppArmor profile)","Restart the process — crypto/rand failures are usually transient entropy exhaustion","Check kernel entropy levels (cat /proc/sys/kernel/random/entropy_avail) and add a hardware RNG or haveged/jitterentropy if persistently low","Remove the custom RNG redirection if crypto/rand was stubbed (e.g. in tests)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Health-check entropy source availability before app start\nif _, err := os.Stat(\"/dev/urandom\"); err != nil {\n    panic(\"crypto entropy source unavailable: \" + err.Error())\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"captcha init failed: %v\", r) // New panics on rand failure\n    }\n}()\ncaptcha, _ := captcha.New(cfg)","preventionTips":["Ensure containers expose /dev/urandom and don't block the getrandom syscall via seccomp","Verify kernel entropy (cat /proc/sys/kernel/random/entropy_avail) on low-resource VMs","Wrap initCaptcha/New in a recover to convert the panic into a controlled startup failure","Test startup in the production container image, not just on dev machines"],"tags":["captcha","altcha","crypto","entropy","panic","startup"],"backgroundTag":"crypto-rand-unavailable","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}