{"record":{"id":"1eb56f4852104abc","repo":"istio/istio","slug":"failed-to-parse-crl-w","errorCode":null,"errorMessage":"failed to parse CRL: %w","messagePattern":"failed to parse CRL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pilot/pkg/bootstrap/configcontroller.go","lineNumber":509,"sourceCode":"\t\t\t\t\tsanMatchFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !sanMatchFound {\n\t\t\treturn fmt.Errorf(\"no matching SAN found\")\n\t\t}\n\t}\n\n\tif len(tlsSettings.CaCrl) > 0 {\n\t\tcrlData := []byte(strings.TrimSpace(tlsSettings.CaCrl))\n\t\tblock, _ := pem.Decode(crlData)\n\t\tif block != nil {\n\t\t\tcrlData = block.Bytes\n\t\t}\n\t\tcrl, err := x509.ParseRevocationList(crlData)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to parse CRL: %w\", err)\n\t\t}\n\t\tfor _, revokedCert := range crl.RevokedCertificateEntries {\n\t\t\tif cert.SerialNumber.Cmp(revokedCert.SerialNumber) == 0 {\n\t\t\t\treturn fmt.Errorf(\"certificate is revoked\")\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// getRootCertFromSecret fetches a map of keys and values from a secret with name in namespace\nfunc (s *Server) getRootCertFromSecret(name, namespace string) (*istioCredentials.CertInfo, error) {\n\tsecret, err := s.kubeClient.Kube().CoreV1().Secrets(namespace).Get(context.Background(), name, v1.GetOptions{})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get credential with name %v: %v\", name, err)\n\t}\n\treturn kube.ExtractRoot(secret.Data)","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/istio/istio/blob/8dc789c5cf17517c64e3c36cb3288230f149dfae/pilot/pkg/bootstrap/configcontroller.go#L491-L527","documentation":"verifyCert applies certificate revocation when configSource.tlsSettings.caCrl is set: it trims the string, optionally unwraps a PEM block, and calls x509.ParseRevocationList. Failure means the content is neither valid PEM-encoded nor raw DER CRL. Because the code ignores pem.Decode errors and falls through to parsing the raw bytes, both a mangled PEM header and non-CRL DER produce this error. The handshake is rejected.","triggerScenarios":"caCrl populated with a certificate instead of a CRL; base64 blob with newlines stripped or added by YAML folding; CRL from an old openssl version the parser rejects; empty-ish whitespace content that survives the len>0 check; secret key holding ca.crt copied into the crl field.","commonSituations":"Hand-assembled tlsSettings referencing a CRL distributed via secret; CI templating that re-wraps long base64 lines; mixing up which secret key (crl vs crt) got pasted.","solutions":["Validate the file: openssl crl -in crl.pem -noout -text (add -inform DER if raw).","Re-encode as canonical PEM CRL and update the mesh config or secret.","If you did not intend revocation checking, remove caCrl entirely.","Regenerate a fresh CRL if the old one used deprecated structures."],"exampleFix":"# before: certificate pasted into caCrl -> x509.ParseRevocationList fails -> error 837\ntlsSettings:\n  caCrl: \"-----BEGIN CERTIFICATE-----...\"\n# after\ntlsSettings:\n  caCrl: \"-----BEGIN X509 CRL-----\\nMIIB...\\n-----END X509 CRL-----\\n\"","handlingStrategy":"validation","validationCode":"// validate CRL material before it enters tlsSettings\nfunc isParseableCRL(s string) bool {\n    data := []byte(strings.TrimSpace(s))\n    if b, _ := pem.Decode(data); b != nil { data = b.Bytes }\n    _, err := x509.ParseRevocationList(data)\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Distribute CRLs via the same secret mechanism as root certs; validate with 'openssl crl -noout' in CI.","Regenerate CRLs with modern openssl; avoid hand-editing base64."],"tags":["istiod","tls","crl","pki","revocation","pem"],"backgroundTag":null,"analyzedSha":"8dc789c5cf17517c64e3c36cb3288230f149dfae","analyzedAt":"2026-08-15T15:16:55.434Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}