crowdsecurity/crowdsec · error

no verified cert in request

Error message

no verified cert in request

What it means

Even when a peer certificate was presented, ValidateCert requires the TLS handshake to have completed full verification: tls.ConnectionState.VerifiedChains must be non-empty. An empty VerifiedChains means the certificate chain was never validated, so the cert cannot be trusted and auth fails.

Source

Thrown at pkg/apiserver/middlewares/v1/tls_auth.go:110

	for _, ou := range ous {
		if slices.Contains(ta.AllowedOUs, ou) {
			return nil
		}
	}

	return fmt.Errorf("client certificate OU %v doesn't match expected OU %v", ous, ta.AllowedOUs)
}

func (ta *TLSAuth) ValidateCert(c *gin.Context) (string, error) {
	// Checks cert validity, Returns true + CN if client cert matches requested OU
	var leaf *x509.Certificate

	if c.Request.TLS == nil || len(c.Request.TLS.PeerCertificates) == 0 {
		return "", errors.New("no certificate in request")
	}

	if len(c.Request.TLS.VerifiedChains) == 0 {
		return "", errors.New("no verified cert in request")
	}

	// although there can be multiple chains, the leaf certificate is the same
	// we take the first one
	leaf = c.Request.TLS.VerifiedChains[0][0]

	if err := ta.checkAllowedOU(leaf.Subject.OrganizationalUnit); err != nil {
		return "", err
	}

	if ta.isExpired(leaf) {
		return "", errors.New("client certificate is expired")
	}

	if validErr, cached := ta.revocationCache.Get(leaf); cached {
		if validErr != nil {
			return "", fmt.Errorf("(cache) %w", validErr)
		}

View on GitHub (pinned to 909b515798)

Solutions

  1. Set the server's TLS ClientAuth mode to tls.RequireAndVerifyClientCert
  2. Ensure the server trusts the CA that signed client certificates (client_ca certificate loaded correctly)
  3. Restart the API server and reconnect with the client cert

Example fix

// before
tlsConfig.ClientAuth = tls.RequestClientCert
// after
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
tlsConfig.ClientCAs = caPool
Defensive patterns

Strategy: type-guard

Validate before calling

if len(req.TLS.VerifiedChains) == 0 {
    return errors.New("client cert chain was not verified")
}

Type guard

func hasVerifiedChain(req *http.Request) bool {
    return req.TLS != nil && len(req.TLS.VerifiedChains) > 0 && len(req.TLS.VerifiedChains[0]) > 0
}

Try / catch

cn, err := ta.ValidateCert(c)
if err != nil {
    log.Debugf("cert auth rejected: %v", err)
    c.AbortWithStatus(http.StatusUnauthorized)
    return
}

Prevention

When it happens

Trigger: ValidateCert sees len(c.Request.TLS.VerifiedChains) == 0 — typically because the server accepted the connection with client auth set to RequestClientCert (or VerifyClientCertIfGiven without verification succeeding at handshake level), so certs are present but not verified.

Common situations: Go TLS config with ClientAuth: tls.RequestClientCert instead of RequireAndVerifyClientCert; handshake verification errors tolerated; custom TLS wrapper that drops VerifiedChains.

Related errors


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