crowdsecurity/crowdsec · error

keyring master secret is %d bytes; minimum is %d

Error message

keyring master secret is %d bytes; minimum is %d

What it means

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.

Source

Thrown at pkg/appsec/challenge/keyring.go:100

	logger *log.Entry

	mu    sync.RWMutex
	cache map[int64][]byte // epoch -> per-epoch sign key
}

// log returns the component logger (never nil; see the logger field).
func (k *KeyRing) log() *log.Entry {
	return k.logger
}

// NewKeyRing constructs a KeyRing. masterSecret must be at least minSecretBytes
// long (callers should already have validated this via WithMasterSecret); the
// rotation interval must be at least keyringMinRotation. maxLive defaults to
// keyringDefaultMaxLive when zero.
func NewKeyRing(masterSecret []byte, rotationInterval time.Duration, maxLive int) (*KeyRing, error) {
	if len(masterSecret) < minSecretBytes {
		return nil, fmt.Errorf("keyring master secret is %d bytes; minimum is %d", len(masterSecret), minSecretBytes)
	}
	if rotationInterval < keyringMinRotation {
		return nil, fmt.Errorf("keyring rotation interval %s is below the floor %s", rotationInterval, keyringMinRotation)
	}
	if maxLive <= 0 {
		maxLive = keyringDefaultMaxLive
	}

	return &KeyRing{
		masterSecret:     masterSecret,
		rotationInterval: rotationInterval,
		maxLive:          maxLive,
		clockSkew:        keyringClockSkew,
		masterCookieKey:  deriveMasterCookieKey(masterSecret),
		logger:           log.StandardLogger().WithField("module", "challenge"),
		now:              time.Now,
		cache:            make(map[int64][]byte),
	}, nil

View on GitHub (pinned to 909b515798)

Solutions

  1. Set a master_secret of at least minSecretBytes — prefer a 32-byte hex value (64 hex chars), e.g. `openssl rand -hex 32`
  2. If using a passphrase, ensure it is at least minSecretBytes bytes of actual text
  3. Run the value through challenge.ParseConfiguredSecret first to get a precise error before constructing the keyring
  4. On distributed deployments, ensure every instance uses the same sufficiently long shared secret

Example fix

// before
kr, err := challenge.NewKeyRing([]byte("short"), time.Hour, 0)
// after
secret, err := challenge.ParseConfiguredSecret(os.Getenv("CS_CHALLENGE_SECRET"))
if err != nil { return err }
kr, err := challenge.NewKeyRing(secret, time.Hour, 0)
Defensive patterns

Strategy: validation

Validate before calling

if len(secret) < minSecretBytes { return fmt.Errorf("master secret must be at least %d bytes", minSecretBytes) }
kr, err := challenge.NewKeyRing(secret, rotation, maxLive)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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