{"record":{"id":"33e36e2b9c39c08b","repo":"kubernetes/kops","slug":"unable-to-build-client-cert-ca-pools","errorCode":null,"errorMessage":"unable to build client-cert CA pools","messagePattern":"unable to build client-cert CA pools","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/bootstrap/challenge_server.go","lineNumber":73,"sourceCode":"func NewChallengeServer(clusterName string, caBundle []byte) (*ChallengeServer, error) {\n\tserverCertificate, err := BuildChallengeServerCertificate(clusterName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig := &tls.Config{\n\t\tCertificates: []tls.Certificate{*serverCertificate},\n\t}\n\n\tvar servingCA bytes.Buffer\n\tfor _, cert := range serverCertificate.Certificate {\n\t\tif err := pem.Encode(&servingCA, &pem.Block{Type: \"CERTIFICATE\", Bytes: cert}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tclientCAs := x509.NewCertPool()\n\tif !clientCAs.AppendCertsFromPEM(caBundle) {\n\t\treturn nil, fmt.Errorf(\"unable to build client-cert CA pools\")\n\t}\n\ttlsConfig.ClientCAs = clientCAs\n\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\n\treturn &ChallengeServer{\n\t\tRequiredSubject: challengeKopsControllerSubject(clusterName),\n\t\ttlsConfig:       tlsConfig,\n\t\tservingCA:       servingCA.Bytes(),\n\t}, nil\n}\n\ntype Challenge struct {\n\tChallengeID     string\n\tChallengeSecret []byte\n}\n\nfunc (s *ChallengeServer) createChallenge() *Challenge {\n\tc := &Challenge{}","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/bootstrap/challenge_server.go#L55-L91","documentation":"NewChallengeServer parses the CA bundle PEM into an x509 pool used to verify client certificates (mTLS). If AppendCertsFromPEM cannot parse any certificate from the bundle, server construction fails with this error. It means the caBundle bytes are not valid PEM certificates.","triggerScenarios":"Calling NewChallengeServer (directly or via getNodeConfigFromServers/Run) with a caBundle that is empty of parseable certs: DER data, a private key, truncated PEM, or wrong file content.","commonSituations":"Pointing the server at the wrong PEM file (e.g. server cert chain without CAs); base64 not decoded before passing; templating artifact truncating the bundle; using the node CA instead of the cluster CA that signed client certs.","solutions":["Pass the full cluster CA PEM bundle (-----BEGIN CERTIFICATE----- blocks) as caBundle","Decode base64 wrapping if the source stores it encoded","Verify each PEM block parses (openssl x509 -in / -text)","Re-export the CA bundle from the cluster pki directory"],"exampleFix":"// before\ncaBundle, _ := os.ReadFile(\"tls.key\") // wrong file\nsrv, err := NewChallengeServer(..., caBundle, ...)\n// after\ncaBundle, _ := os.ReadFile(\"ca.crt\")\nsrv, err := NewChallengeServer(..., caBundle, ...)","handlingStrategy":"validation","validationCode":"pool := x509.NewCertPool()\nif !pool.AppendCertsFromPEM(caBundle) {\n\treturn fmt.Errorf(\"caBundle contains no parseable PEM certificates\")\n}","typeGuard":null,"tryCatchPattern":"srv, err := NewChallengeServer(...)\nif err != nil && strings.Contains(err.Error(), \"unable to build client-cert CA pools\") {\n\t// check the CA bundle file path and PEM contents before retrying\n\treturn err\n}","preventionTips":["Point the server at the cluster CA bundle file, not server certs or keys","Validate the bundle with openssl at config load time","Keep CA bundle generation in a single tested code path"],"tags":["tls","x509","mtls","ca-bundle","server"],"backgroundTag":"invalid-ca-certificate","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}