{"record":{"id":"026354273bf20fee","repo":"crowdsecurity/crowdsec","slug":"keyring-master-secret-is-d-bytes-minimum-is-d","errorCode":null,"errorMessage":"keyring master secret is %d bytes; minimum is %d","messagePattern":"keyring master secret is (.+?) bytes; minimum is (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/keyring.go","lineNumber":100,"sourceCode":"\n\tlogger *log.Entry\n\n\tmu    sync.RWMutex\n\tcache map[int64][]byte // epoch -> per-epoch sign key\n}\n\n// log returns the component logger (never nil; see the logger field).\nfunc (k *KeyRing) log() *log.Entry {\n\treturn k.logger\n}\n\n// NewKeyRing constructs a KeyRing. masterSecret must be at least minSecretBytes\n// long (callers should already have validated this via WithMasterSecret); the\n// rotation interval must be at least keyringMinRotation. maxLive defaults to\n// keyringDefaultMaxLive when zero.\nfunc NewKeyRing(masterSecret []byte, rotationInterval time.Duration, maxLive int) (*KeyRing, error) {\n\tif len(masterSecret) < minSecretBytes {\n\t\treturn nil, fmt.Errorf(\"keyring master secret is %d bytes; minimum is %d\", len(masterSecret), minSecretBytes)\n\t}\n\tif rotationInterval < keyringMinRotation {\n\t\treturn nil, fmt.Errorf(\"keyring rotation interval %s is below the floor %s\", rotationInterval, keyringMinRotation)\n\t}\n\tif maxLive <= 0 {\n\t\tmaxLive = keyringDefaultMaxLive\n\t}\n\n\treturn &KeyRing{\n\t\tmasterSecret:     masterSecret,\n\t\trotationInterval: rotationInterval,\n\t\tmaxLive:          maxLive,\n\t\tclockSkew:        keyringClockSkew,\n\t\tmasterCookieKey:  deriveMasterCookieKey(masterSecret),\n\t\tlogger:           log.StandardLogger().WithField(\"module\", \"challenge\"),\n\t\tnow:              time.Now,\n\t\tcache:            make(map[int64][]byte),\n\t}, nil","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/keyring.go#L82-L118","documentation":"NewKeyRing rejects a master secret shorter than minSecretBytes. The master secret is the root key material from which per-key cookie secrets are derived; a short secret is brute-forceable, so the constructor fails fast. Callers should validate earlier via WithMasterSecret/ParseConfiguredSecret, but NewKeyRing enforces it defensively.","triggerScenarios":"Calling NewKeyRing directly (or BuildOptions wiring a configured secret into it) with a masterSecret slice under minSecretBytes — e.g. a hex secret that decodes to too few bytes, or a short passphrase passed raw.","commonSituations":"An operator configured a short master_secret (e.g. 'abc123') in the AppSec challenge config; a hex string was entered without realizing it must decode to the minimum byte length; tests constructing a KeyRing with a stub secret.","solutions":["Set a master_secret of at least minSecretBytes — prefer a 32-byte hex value (64 hex chars), e.g. `openssl rand -hex 32`","If using a passphrase, ensure it is at least minSecretBytes bytes of actual text","Run the value through challenge.ParseConfiguredSecret first to get a precise error before constructing the keyring","On distributed deployments, ensure every instance uses the same sufficiently long shared secret"],"exampleFix":"// before\nkr, err := challenge.NewKeyRing([]byte(\"short\"), time.Hour, 0)\n// after\nsecret, err := challenge.ParseConfiguredSecret(os.Getenv(\"CS_CHALLENGE_SECRET\"))\nif err != nil { return err }\nkr, err := challenge.NewKeyRing(secret, time.Hour, 0)","handlingStrategy":"validation","validationCode":"if len(secret) < minSecretBytes { return fmt.Errorf(\"master secret must be at least %d bytes\", minSecretBytes) }\nkr, err := challenge.NewKeyRing(secret, rotation, maxLive)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate secrets with `openssl rand -hex 32`","Validate configuration with ParseConfiguredSecret before constructing a KeyRing","Enforce secret length checks in config-loading tests","Use the documented option helpers (WithMasterSecret) rather than calling NewKeyRing directly"],"tags":["crypto","keyring","configuration"],"backgroundTag":"invalid-config-value","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"}