cilium/cilium · error
found additional non-CA certificate in chain
Error message
found additional non-CA certificate in chain
What it means
All certificates after the leaf are expected to be CA (intermediate) certificates. If any certificate at position >= 1 is a non-CA end-entity certificate, the chain structure is invalid — a leaf cannot appear mid-chain — so verification aborts rather than attempting a nonsensical path build.
Source
Thrown at pkg/auth/mutual_authhandler.go:293
for _, chain := range certChains {
opts := x509.VerifyOptions{
Roots: caBundle,
Intermediates: x509.NewCertPool(),
}
if len(chain) == 0 {
return nil, fmt.Errorf("no certificate chains found")
}
leaf := chain[0]
if leaf.IsCA {
return nil, fmt.Errorf("leaf certificate cannot be a CA")
}
for i := 1; i < len(chain); i++ {
cert := chain[i]
if cert.IsCA {
opts.Intermediates.AddCert(cert)
} else {
return nil, fmt.Errorf("found additional non-CA certificate in chain")
}
}
if leaf == nil {
return nil, fmt.Errorf("no leaf certificate found")
}
if _, err := leaf.Verify(opts); err != nil {
return nil, fmt.Errorf("failed to verify certificate: %w", err)
}
if id != nil { // this will be empty in the peer connection
m.log.Debug("Validating Server SNI", logfields.SNIID, id)
if valid, err := m.cert.ValidateIdentity(*id, leaf); err != nil {
return nil, fmt.Errorf("failed to validate SAN: %w", err)
} else if !valid {
return nil, fmt.Errorf("unable to validate SAN")
}
}
View on GitHub (pinned to ac7b90affa)
Solutions
- Rebuild the certificate bundle so it contains only the leaf followed by its CA intermediates
- Remove any duplicate or stale leaf certificates from the mounted secret/PEM file
- Check the cert-issuing tooling (e.g. cert-manager, mkcert) configuration to see why an extra end-entity cert is included
- Restart/redeploy the peer so it picks up the corrected bundle
Example fix
// before: two leaves concatenated // cert.pem = leaf1 + leaf2 + intermediate // after: single leaf + intermediates // cert.pem = leaf1 + intermediate
Defensive patterns
Strategy: validation
Validate before calling
// Reject bundles containing more than one end-entity cert
func validateBundle(pemBytes []byte) error {
leaves := 0
for _, c := range parseCerts(pemBytes) {
if !c.IsCA { leaves++ }
}
if leaves > 1 { return fmt.Errorf("bundle has %d leaf certs; keep exactly one", leaves) }
return nil
} Type guard
func isLeafFirstChain(chain []*x509.Certificate) bool {
return len(chain) > 0 && !chain[0].IsCA &&
func() bool { for _, c := range chain[1:] { if !c.IsCA { return false } }; return true }()
} Try / catch
exp, err := handler.verifyPeerCertificate(id, caBundle, chains)
if err != nil {
if strings.Contains(err.Error(), "additional non-CA certificate") {
return nil, fmt.Errorf("peer's cert bundle contains multiple leaves; rebuild it: %w", err)
}
return nil, err
} Prevention
- Build PEM bundles with exactly one leaf plus its intermediates
- Avoid blind `cat *.crt` when assembling cert files
- Re-validate bundles after cert rotation
- Use cert-manager issuance rather than manual assembly
When it happens
Trigger: The peer concatenated multiple leaf certificates into one chain file (e.g. two serving certs in the same PEM), or rotation tooling appended the old leaf after the new one instead of intermediates.
Common situations: cert-manager or manual `cat` of certs producing a PEM with two end-entity certs; misordered renewal bundles; copy/paste assembly of cert files; upstream cert provider returning extra leaf certs.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- no certificate chains found
- failed to parse certificate: %w
- leaf certificate cannot be a CA
- no leaf certificate found
- failed to verify certificate: %w
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/39f5be0f71680fad.
Report an issue: GitHub.