{"record":{"id":"6308f21c01854ade","repo":"crowdsecurity/crowdsec","slug":"generate-random-master-secret-w","errorCode":null,"errorMessage":"generate random master secret: %w","messagePattern":"generate random master secret: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/appsec/challenge/secret.go","lineNumber":28,"sourceCode":"\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n)\n\n// minSecretBytes is the smallest accepted master-secret length. 32 bytes is\n// the natural minimum for an HMAC-SHA256 key with full security margin; we\n// reject anything shorter as a configuration error rather than silently\n// padding it.\nconst minSecretBytes = 32\n\n// generateRandomSecret returns a fresh 32-byte secret suitable for use when\n// no master_secret is configured. Single-instance deployments are fine with\n// this; distributed deployments MUST configure a shared secret because each\n// instance generates an independent random one here.\nfunc generateRandomSecret() ([]byte, error) {\n\tbuf := make([]byte, 32)\n\tif _, err := crand.Read(buf); err != nil {\n\t\treturn nil, fmt.Errorf(\"generate random master secret: %w\", err)\n\t}\n\treturn buf, nil\n}\n\n// ParseConfiguredSecret accepts a configured master secret as either a hex\n// string (preferred — encodes raw bytes unambiguously) or a raw passphrase\n// (fallback for human-edited configs). The result is at least minSecretBytes.\nfunc ParseConfiguredSecret(value string) ([]byte, error) {\n\tif value == \"\" {\n\t\treturn nil, errors.New(\"empty master secret\")\n\t}\n\n\t// Hex form: even length, hex digits only.\n\tif isHex(value) {\n\t\traw, err := hex.DecodeString(value)\n\t\tif err == nil {\n\t\t\tif len(raw) < minSecretBytes {\n\t\t\t\treturn nil, fmt.Errorf(\"hex master secret decodes to %d bytes; minimum is %d\", len(raw), minSecretBytes)","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/secret.go#L10-L46","documentation":"generateRandomSecret wraps a failure from crypto/rand.Read when generating the default 32-byte master secret (used when no master_secret is configured). crypto/rand essentially never fails on normal systems; failure indicates the OS entropy source is unavailable, so no secure key can be produced and startup must abort.","triggerScenarios":"NewChallengeRuntime is built without a configured master_secret and crypto/rand.Read fails — e.g. a container/sandbox where /dev/urandom or getrandom(2) is unavailable or blocked.","commonSituations":"Restricted containers or seccomp profiles blocking getrandom; embedded/minimal systems lacking an entropy device; heavily sandboxed CI environments.","solutions":["Fix the host entropy source: ensure /dev/urandom exists and getrandom(2) is not blocked by seccomp/AppArmor","Configure an explicit master_secret so the random generator path is not used at all","Check container runtime settings — run with a normal /dev mount rather than a stripped one","Retry startup; if the failure is transient (early-boot entropy), it may succeed once the system is up"],"exampleFix":"// before: no secret configured, relying on random generation\nopts := challenge.BuildOptions(cfg)\n// after: explicit secret avoids the failing path\nsecret, _ := challenge.ParseConfiguredSecret(\"<64-hex-chars>\")\nopts := challenge.BuildOptions(cfg, challenge.WithMasterSecret(secret))","handlingStrategy":"try-catch","validationCode":"// pre-check is not practical; ensure host entropy availability and/or configure a secret\nif os.Getenv(\"CS_CHALLENGE_MASTER_SECRET\") == \"\" { log.Warn(\"no master_secret configured; using random (single-instance only)\") }","typeGuard":null,"tryCatchPattern":"secret, err := challenge.ParseConfiguredSecret(cfgValue)\nif err != nil {\n    secret, err = generateSecret() // surfaces the wrapped crypto/rand failure\n    if err != nil { return fmt.Errorf(\"challenge runtime init: %w\", err) }\n}","preventionTips":["Configure an explicit master_secret in production, especially distributed deployments","Verify containers allow getrandom(2) and expose /dev/urandom","Test startup in the same sandbox profile used in production","Fail fast with a clear log line when random generation fails"],"tags":["crypto","entropy","startup"],"backgroundTag":"missing-env-var","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}