{"record":{"id":"39f5be0f71680fad","repo":"cilium/cilium","slug":"found-additional-non-ca-certificate-in-chain","errorCode":null,"errorMessage":"found additional non-CA certificate in chain","messagePattern":"found additional non-CA certificate in chain","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/mutual_authhandler.go","lineNumber":293,"sourceCode":"\tfor _, chain := range certChains {\n\t\topts := x509.VerifyOptions{\n\t\t\tRoots:         caBundle,\n\t\t\tIntermediates: x509.NewCertPool(),\n\t\t}\n\n\t\tif len(chain) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"no certificate chains found\")\n\t\t}\n\t\tleaf := chain[0]\n\t\tif leaf.IsCA {\n\t\t\treturn nil, fmt.Errorf(\"leaf certificate cannot be a CA\")\n\t\t}\n\t\tfor i := 1; i < len(chain); i++ {\n\t\t\tcert := chain[i]\n\t\t\tif cert.IsCA {\n\t\t\t\topts.Intermediates.AddCert(cert)\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"found additional non-CA certificate in chain\")\n\t\t\t}\n\t\t}\n\t\tif leaf == nil {\n\t\t\treturn nil, fmt.Errorf(\"no leaf certificate found\")\n\t\t}\n\t\tif _, err := leaf.Verify(opts); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to verify certificate: %w\", err)\n\t\t}\n\n\t\tif id != nil { // this will be empty in the peer connection\n\t\t\tm.log.Debug(\"Validating Server SNI\", logfields.SNIID, id)\n\t\t\tif valid, err := m.cert.ValidateIdentity(*id, leaf); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to validate SAN: %w\", err)\n\t\t\t} else if !valid {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to validate SAN\")\n\t\t\t}\n\t\t}\n","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/auth/mutual_authhandler.go#L275-L311","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: two leaves concatenated\n// cert.pem = leaf1 + leaf2 + intermediate\n// after: single leaf + intermediates\n// cert.pem = leaf1 + intermediate","handlingStrategy":"validation","validationCode":"// Reject bundles containing more than one end-entity cert\nfunc validateBundle(pemBytes []byte) error {\n    leaves := 0\n    for _, c := range parseCerts(pemBytes) {\n        if !c.IsCA { leaves++ }\n    }\n    if leaves > 1 { return fmt.Errorf(\"bundle has %d leaf certs; keep exactly one\", leaves) }\n    return nil\n}","typeGuard":"func isLeafFirstChain(chain []*x509.Certificate) bool {\n    return len(chain) > 0 && !chain[0].IsCA &&\n        func() bool { for _, c := range chain[1:] { if !c.IsCA { return false } }; return true }()\n}","tryCatchPattern":"exp, err := handler.verifyPeerCertificate(id, caBundle, chains)\nif err != nil {\n    if strings.Contains(err.Error(), \"additional non-CA certificate\") {\n        return nil, fmt.Errorf(\"peer's cert bundle contains multiple leaves; rebuild it: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["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"],"tags":["tls","x509","certificate-chain","chain-structure","cilium"],"backgroundTag":"malformed-certificate-chain","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}