crowdsecurity/crowdsec · critical

empty master secret

Error message

empty master secret

What it means

ParseConfiguredSecret parses the configured appsec master secret, accepting either a hex string or a raw passphrase, and requires a non-empty value yielding at least minSecretBytes. An empty configuration string cannot derive any key material, so it is rejected immediately with this error. It is called by BuildOptions when constructing the challenge runtime.

Source

Thrown at pkg/appsec/challenge/secret.go:38

// generateRandomSecret returns a fresh 32-byte secret suitable for use when
// no master_secret is configured. Single-instance deployments are fine with
// this; distributed deployments MUST configure a shared secret because each
// instance generates an independent random one here.
func generateRandomSecret() ([]byte, error) {
	buf := make([]byte, 32)
	if _, err := crand.Read(buf); err != nil {
		return nil, fmt.Errorf("generate random master secret: %w", err)
	}
	return buf, nil
}

// ParseConfiguredSecret accepts a configured master secret as either a hex
// string (preferred — encodes raw bytes unambiguously) or a raw passphrase
// (fallback for human-edited configs). The result is at least minSecretBytes.
func ParseConfiguredSecret(value string) ([]byte, error) {
	if value == "" {
		return nil, errors.New("empty master secret")
	}

	// Hex form: even length, hex digits only.
	if isHex(value) {
		raw, err := hex.DecodeString(value)
		if err == nil {
			if len(raw) < minSecretBytes {
				return nil, fmt.Errorf("hex master secret decodes to %d bytes; minimum is %d", len(raw), minSecretBytes)
			}
			return raw, nil
		}
		// Fall through to passphrase handling on hex parse failure — defensive.
	}

	if len(value) < minSecretBytes {
		return nil, fmt.Errorf("passphrase master secret is %d bytes; minimum is %d", len(value), minSecretBytes)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Set a non-empty master secret in the appsec configuration (hex string preferred, e.g. `openssl rand -hex 32`).
  2. If using a passphrase, ensure it is at least minSecretBytes long.
  3. Check env-var interpolation isn't producing an empty value (e.g. ${SECRET} unset).
  4. Re-generate from the config template so the secret field is populated, then restart crowdsec.

Example fix

// before: empty value in appsec config
api:
  appsec:
    secret: ""
// after
api:
  appsec:
    secret: "6f1b...generated-hex..."
# or: openssl rand -hex 32
Defensive patterns

Strategy: validation

Validate before calling

// before calling BuildOptions/ParseConfiguredSecret
if secret == "" {
    return errors.New("appsec master secret is not configured")
}
if _, err := challenge.ParseConfiguredSecret(secret); err != nil {
    return fmt.Errorf("invalid master secret: %w", err)
}

Try / catch

key, err := challenge.ParseConfiguredSecret(cfg.AppsecSecret)
if err != nil {
    return fmt.Errorf("appsec secret config: %w", err) // includes "empty master secret"
}

Prevention

When it happens

Trigger: BuildOptions (or tests TestParseConfiguredSecret_*) passes "" as the configured secret value to ParseConfiguredSecret (secret.go:38).

Common situations: appsec config YAML has an empty secret/secret_file value; the config key was never set on a fresh install; an env var interpolated to empty at runtime; a config template left the field blank.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/159b3c6bb095a067. Report an issue: GitHub.