{"record":{"id":"4569602ea9aaa60a","repo":"crowdsecurity/crowdsec","slug":"cs-lapi-secret-not-strong-enough","errorCode":null,"errorMessage":"CS_LAPI_SECRET not strong enough","messagePattern":"CS_LAPI_SECRET not strong enough","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiserver/middlewares/v1/jwt.go","lineNumber":286,"sourceCode":"\t// Get secret from environment variable \"SECRET\"\n\tvar (\n\t\tsecret []byte\n\t\terr    error\n\t)\n\n\t// Please be aware that brute force HS256 is possible.\n\t// PLEASE choose a STRONG secret\n\tsecretString := os.Getenv(\"CS_LAPI_SECRET\")\n\tsecret = []byte(secretString)\n\n\tswitch l := len(secret); {\n\tcase l == 0:\n\t\tsecret, err = randomSecret()\n\t\tif err != nil {\n\t\t\treturn &JWT{}, err\n\t\t}\n\tcase l < 64:\n\t\treturn &JWT{}, errors.New(\"CS_LAPI_SECRET not strong enough\")\n\t}\n\n\tjwtMiddleware := &JWT{\n\t\tDbClient: dbClient,\n\t\tTlsAuth:  &TLSAuth{},\n\t}\n\n\tret, err := jwt.New(&jwt.GinJWTMiddleware{\n\t\tRealm:           \"Crowdsec API local\",\n\t\tKey:             secret,\n\t\tTimeout:         time.Hour,\n\t\tMaxRefresh:      time.Hour,\n\t\tIdentityKey:     MachineIDKey,\n\t\tPayloadFunc:     PayloadFunc,\n\t\tIdentityHandler: IdentityHandler,\n\t\tAuthenticator:   jwtMiddleware.Authenticator,\n\t\tAuthorizator:    Authorizator,\n\t\tUnauthorized:    Unauthorized,","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/middlewares/v1/jwt.go#L268-L304","documentation":"NewJWT validates CS_LAPI_SECRET, the HMAC secret used to sign LAPI JWTs. If the variable is set but shorter than 64 characters it returns \"CS_LAPI_SECRET not strong enough\" and the API server fails to start. The length floor prevents trivially brute-forceable token-signing keys.","triggerScenarios":"CS_LAPI_SECRET exported with a short value (0 < len < 64) before starting crowdsec/LAPI — e.g. a placeholder like \"changeme\", a truncated paste, or a deployment script writing a short default.","commonSituations":"Kubernetes/docker deployments templating the secret with a short default; ansible scripts generating too-short passwords; users following outdated docs that suggested short secrets.","solutions":["Generate a ≥64-character secret, e.g. `export CS_LAPI_SECRET=$(head -c 48 /dev/urandom | base64)`","Ensure all LAPI clients (agents, bouncers' registration flows) get the same secret when you change it","Fix the deployment template/script that produces the short value","Alternatively unset CS_LAPI_SECRET (length 0) to let crowdsec generate a random secret itself"],"exampleFix":"// before\nCS_LAPI_SECRET=secret123\n// after\nexport CS_LAPI_SECRET=$(head -c 48 /dev/urandom | base64 | tr -d '\\n')","handlingStrategy":"validation","validationCode":"if v := os.Getenv(\"CS_LAPI_SECRET\"); v != \"\" && len(v) < 64 {\n    return errors.New(\"CS_LAPI_SECRET must be at least 64 characters\")\n}","typeGuard":null,"tryCatchPattern":"jwt, err := NewJWT(dbClient)\nif err != nil {\n    if strings.Contains(err.Error(), \"not strong enough\") {\n        log.Fatal(\"regenerate CS_LAPI_SECRET with: head -c 48 /dev/urandom | base64\")\n    }\n    return err\n}","preventionTips":["Generate secrets with `head -c 48 /dev/urandom | base64` (64+ chars after encoding)","Fix deployment templates that default to short secrets like \"changeme\"","Add a pre-start check in your entrypoint script that validates the secret length","Rotate the secret across all LAPI clients whenever you change it"],"tags":["configuration","jwt","env-var","security"],"backgroundTag":"invalid-env-var-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"}