{"record":{"id":"ff658a82c9f40f45","repo":"cloudflare/cloudflared","slug":"error-appending-custom-ca-to-cert-pool","errorCode":null,"errorMessage":"error appending custom CA to cert pool","messagePattern":"error appending custom CA to cert pool","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tlsconfig/origin_ca.go","lineNumber":71,"sourceCode":"\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"could not append Cloudflare Root CAs to cloudflared certificate pool\")\n\t}\n\tfor _, cert := range cfRootCA {\n\t\tcertPool.AddCert(cert)\n\t}\n\n\tif originCAFilename == \"\" {\n\t\treturn certPool, nil\n\t}\n\n\t// nolint: gosec\n\tcustomOriginCA, err := os.ReadFile(originCAFilename)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, fmt.Sprintf(\"unable to read the file %s\", originCAFilename))\n\t}\n\n\tif !certPool.AppendCertsFromPEM(customOriginCA) {\n\t\treturn nil, fmt.Errorf(\"error appending custom CA to cert pool\")\n\t}\n\treturn certPool, nil\n}\n\nfunc CreateTunnelConfig(caCert string, serverName string) (*tls.Config, error) {\n\ttlsConfig := &tls.Config{ServerName: serverName}\n\tif caCert != \"\" {\n\t\tcaCertPEM, err := os.ReadFile(caCert) //nolint:gosec\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read CA certificate %s: %w\", caCert, err)\n\t\t}\n\n\t\trootCAPool := x509.NewCertPool()\n\t\tif !rootCAPool.AppendCertsFromPEM(caCertPEM) {\n\t\t\treturn nil, fmt.Errorf(\"parse CA certificate %s\", caCert)\n\t\t}\n\t\ttlsConfig.RootCAs = rootCAPool\n\t}","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/tlsconfig/origin_ca.go#L53-L89","documentation":"LoadCustomOriginCA reads a user-supplied CA PEM file (via --origin-ca-pool) and adds it to an x509 CertPool. If crypto/x509's AppendCertsFromPEM returns false — the file parsed to zero valid PEM certificates — cloudflared returns this error because the origin CA pool would otherwise silently trust nothing, breaking origin TLS validation.","triggerScenarios":"--origin-ca-pool points to a file whose content is not PEM (DER-encoded cert, private key, HTML error page, empty file), contains certificates in an unsupported format, or the file read succeeded but every PEM block failed to parse as a certificate.","commonSituations":"Exporting a cert from a browser/Windows store as DER instead of PEM; pointing the flag at a private key or bundle of CSRs; config copied from docs with a placeholder path; a proxy download saving an HTML login page as ca.pem.","solutions":["Convert the certificate to PEM format: `openssl x509 -inform DER -in ca.der -out ca.pem` and pass the PEM file.","Verify the file actually contains certificates: `openssl x509 -in ca.pem -noout -subject` should print a subject.","Check you are not passing a private key or CSR file; the pool needs CA certificates.","Confirm the path via --origin-ca-pool is correct and the file is non-empty (`ls -l`, `head -1` should show -----BEGIN CERTIFICATE-----).","Concatenate intermediate + root CAs into one PEM bundle if the chain is split across files."],"exampleFix":"// before (DER file passed directly)\noriginServerName: \"example.com\"\noriginCA: \"/etc/ssl/certs/ca.der\"\n// after (converted to PEM)\n# openssl x509 -inform DER -in /etc/ssl/certs/ca.der -out /etc/ssl/certs/ca.pem\noriginServerName: \"example.com\"\noriginCA: \"/etc/ssl/certs/ca.pem\"","handlingStrategy":"validation","validationCode":"// guard before invoking the flag path\npemBytes, err := os.ReadFile(caPath)\nif err != nil {\n\treturn err\n}\nif !bytes.Contains(pemBytes, []byte(\"-----BEGIN CERTIFICATE-----\")) {\n\treturn fmt.Errorf(\"%s is not a PEM certificate bundle\", caPath)\n}\nblock, _ := pem.Decode(pemBytes)\nif block == nil || block.Type != \"CERTIFICATE\" {\n\treturn fmt.Errorf(\"%s has no PEM CERTIFICATE block\", caPath)\n}\nif _, err := x509.ParseCertificate(block.Bytes); err != nil {\n\treturn fmt.Errorf(\"%s contains an unparseable certificate: %v\", caPath, err)\n}","typeGuard":"func isPEMCertBundle(data []byte) bool {\n\tblock, _ := pem.Decode(data)\n\treturn block != nil && block.Type == \"CERTIFICATE\"\n}","tryCatchPattern":"pool, err := tlsconfig.LoadCustomOriginCA(caPath)\nif err != nil {\n\tif strings.Contains(err.Error(), \"appending custom CA\") {\n\t\treturn fmt.Errorf(\"CA pool file %s is not valid PEM: %w\", caPath, err)\n\t}\n\treturn err\n}","preventionTips":["Always supply PEM (BEGIN CERTIFICATE) files to --origin-ca-pool; convert DER with openssl.","Verify the file with `openssl x509 -in ca.pem -noout -subject` before deploying.","Include full chain (root + intermediates) in the bundle; never point the flag at private keys."],"tags":["tls","pem","ca-pool","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}