crowdsecurity/crowdsec · error
client certificate OU %v doesn't match expected OU %v
Error message
client certificate OU %v doesn't match expected OU %v
What it means
The TLS client-auth middleware validates the presented client certificate's Organizational Unit (OU) subject field against the configured allow-list (AllowedOUs, e.g. crowdsec_lapi cert OUs like 'crowdsec-agents' or 'crowdsec-bouncers'). If none of the OUs in the client cert's subject appears in the allow-list, the certificate is refused even though it chains to a trusted CA. This enforces role separation between agents, bouncers and admins at the TLS layer.
Source
Thrown at pkg/apiserver/middlewares/v1/tls_auth.go:98
continue
}
uniqueOUs[ou] = struct{}{}
ta.AllowedOUs = append(ta.AllowedOUs, ou)
}
return nil
}
func (ta *TLSAuth) checkAllowedOU(ous []string) error {
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]
View on GitHub (pinned to 909b515798)
Solutions
- Inspect the client cert: `openssl x509 -in cert.pem -noout -subject` and note the OU.
- Compare with the allowed OUs configured for the endpoint (api.server.tls.allowed_ous / LAPI ca config).
- Reissue the client certificate with the correct OU (e.g. `cscli bouncers`/CA workflow or the cert-generation script using the expected profile).
- Alternatively add the cert's actual OU to the allowed_ous list and restart crowdsec — only if the client's role is genuinely permitted.
Example fix
// before: bouncer cert issued with OU=crowdsec-bouncers, endpoint allows only crowdsec-agents openssl req -new -subj "/CN=bouncer1/OU=crowdsec-agents" ... // after: issue cert with the OU expected for its role, or extend config: // api: // server: // tls: // allowed_ous: [crowdsec-agents, crowdsec-bouncers]
Defensive patterns
Strategy: validation
Validate before calling
subject, _ := exec.Command("openssl", "x509", "-in", certPath, "-noout", "-subject").Output()
// extract OU= from subject and compare against the allowed_ous list before deploying the cert Try / catch
if _, err := tlsAuth.ValidateCert(c); err != nil {
if strings.Contains(err.Error(), "OU") {
// log cert subject vs allowed OUs; fail the request with 403
}
} Prevention
- Issue client certs through the same CA profile script that sets the role OU (crowdsec-agents / crowdsec-bouncers).
- Before rollout, verify each cert: `openssl x509 -noout -subject -in cert.pem`.
- Keep allowed_ous in config in sync with your cert-generation profiles; change both together.
- Separate CA/profiles per component role to prevent cert reuse across roles.
When it happens
Trigger: ValidateCert extracts the leaf cert (VerifiedChains[0][0]) and calls checkAllowedOU with leaf.Subject.OrganizationalUnit; the connection is rejected when the cert was issued with an OU not listed in the endpoint's allowed_ous/LAPI TLS config — e.g. a bouncer presenting a 'crowdsec-agents' cert, or a cert issued from a different CA profile.
Common situations: Certificate generated with the wrong CA profile or for the wrong component role; allowed_ous edited in config.yaml without reissuing certs; older certs minted before an OU naming convention change; copying a client cert between hosts with different roles.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- user/password authentication and TLS authentication are mutu
- while opening cert file: %w
- basic_auth is selected, but basic_auth is not provided
- basic_auth is selected, but username is not provided
- basic_auth is selected, but password is not provided
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/518f7c3e9396d541.
Report an issue: GitHub.