crowdsecurity/crowdsec · critical

failed to generate nonce

Error message

failed to generate nonce: %w

What it means

sealCookieV0 generates the random GCM nonce with crypto/rand.Read. This error means the OS CSPRNG failed, so the cookie cannot be sealed safely — without a fresh random nonce, AES-GCM nonce reuse would catastrophically break confidentiality and authenticity. The library fails closed.

Solutions

  1. Inspect the wrapped OS error (errno) to identify the CSPRNG failure cause.
  2. Check the container/sandbox seccomp profile allows the getrandom syscall.
  3. Verify /dev/urandom is available and the host kernel is sane.
  4. Restart the process after fixing the environment; this is not retryable within the same call.

Example fix

// seccomp profile (docker/containerd): ensure getrandom is permitted
"syscalls": [
  {"names": ["getrandom"], "action": "SCMP_ACT_ALLOW"}
]
Defensive patterns

Strategy: retry

Try / catch

if err := errors.Is(err, randErr); err != nil {
    // log the OS-level cause; fix sandbox/CSPRNG; restart process
    return fmt.Errorf("cookie sealing unavailable, entropy failure: %w", err)
}

Prevention

When it happens

Trigger: Calling sealCookieV0 (via SealAllowlistCookie or ValidateChallengeResponse) when crypto/rand.Read returns an error — e.g. getrandom(2) blocked by a seccomp/sandbox profile, fd exhaustion, or a broken /dev/urandom in the container.

Common situations: Containers or sandboxes with restrictive seccomp profiles blocking getrandom; severely resource-starved hosts; unusual kernels or restricted environments (some hardened container runtimes).

Related errors


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

Appendix: source

Thrown at pkg/appsec/challenge/crypto.go:145

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create GCM: %w", err)
	}

	// Reject an over-limit envelope before marshaling it.
	maxPlaintext := maxCookieLen/4*3 - 1 - gcm.NonceSize() - gcm.Overhead()
	if plaintextLen := cookiePlaintextFixedHeaderLen + len(reason) + proto.Size(envelope); plaintextLen > maxPlaintext {
		return "", fmt.Errorf("%w: plaintext=%d > %d", ErrCookieTooLarge, plaintextLen, maxPlaintext)
	}

	envelopeBytes, err := proto.Marshal(envelope)
	if err != nil {
		return "", fmt.Errorf("failed to marshal challenge cookie proto: %w", err)
	}

	nonce := make([]byte, gcm.NonceSize())
	if _, err := rand.Read(nonce); err != nil {
		return "", fmt.Errorf("failed to generate nonce: %w", err)
	}

	// Build the plaintext: not_after_be8 || flags || reason_len_be || reason || envelope
	plaintext := make([]byte, 0, cookiePlaintextFixedHeaderLen+len(reason)+len(envelopeBytes))

	var notAfterBytes [8]byte
	binary.BigEndian.PutUint64(notAfterBytes[:], uint64(notAfter))
	plaintext = append(plaintext, notAfterBytes[:]...)

	plaintext = append(plaintext, flags)

	var reasonLenBytes [2]byte
	binary.BigEndian.PutUint16(reasonLenBytes[:], uint16(len(reason)))
	plaintext = append(plaintext, reasonLenBytes[:]...)

	plaintext = append(plaintext, []byte(reason)...)
	plaintext = append(plaintext, envelopeBytes...)

View on GitHub (pinned to 909b515798)