{"record":{"id":"d92b2b56368a21c8","repo":"k3s-io/k3s","slug":"no-certificates-loaded-from-etcd-s3-endpoint-ca","errorCode":null,"errorMessage":"no certificates loaded from etcd-s3-endpoint-ca","messagePattern":"no certificates loaded from etcd-s3-endpoint-ca","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"pkg/etcd/s3/s3.go","lineNumber":587,"sourceCode":"\t\t// contains multiline, ascii-armored, base64-encoded certificate data - as would be produced\n\t\t// by `base64 --wrap=0 /path/to/cert.pem`. If this fails, assume the value is the path to a\n\t\t// file on disk, and try to read that.  This is backwards compatible with RKE1.\n\t\tcaData, err := base64.StdEncoding.DecodeString(ca)\n\t\tif err != nil {\n\t\t\tcaData, err = os.ReadFile(ca)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif certPool.AppendCertsFromPEM(caData) {\n\t\t\tloaded = true\n\t\t}\n\t}\n\n\tif loaded {\n\t\treturn &tls.Config{RootCAs: certPool}, nil\n\t}\n\treturn nil, errors.New(\"no certificates loaded from etcd-s3-endpoint-ca\")\n}\n\nfunc bucketLookupType(endpoint, lookupType string) minio.BucketLookupType {\n\tswitch strings.ToLower(lookupType) {\n\tcase \"dns\":\n\t\treturn minio.BucketLookupDNS\n\tcase \"path\":\n\t\treturn minio.BucketLookupPath\n\t}\n\n\tif strings.Contains(endpoint, \"aliyun\") { // backwards compatible with RKE1\n\t\treturn minio.BucketLookupDNS\n\t}\n\treturn minio.BucketLookupAuto\n}\n","sourceCodeStart":569,"sourceCodeEnd":603,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/etcd/s3/s3.go#L569-L603","documentation":"The S3 TLS setup builds a cert pool from the etcd-s3-endpoint-ca sources (file on disk and/or data from the config secret) and sets 'loaded' only when AppendCertsFromPEM accepts at least one PEM block. If every source is empty, missing, or not parseable PEM, the pool stays empty and this error is returned.","triggerScenarios":"Setting --etcd-s3-endpoint-ca to a path whose file is empty/truncated/not PEM, or an etcd-s3-config secret whose CA field contains base64 garbage or a DER-encoded (binary) certificate instead of PEM.","commonSituations":"Fetched the cert with openssl s_client without -showcerts and got a partial chain; copied the server cert instead of the CA; trailing junk before the BEGIN CERTIFICATE line; envs where the CA file is mounted empty until an init job runs.","solutions":["Verify the file is valid PEM: openssl x509 -in ca.pem -noout -text (for bundles, check every block with awk).","Ensure the value is the CA certificate (PEM, possibly a bundle), not DER and not the leaf server cert alone.","If the CA comes from the config secret, confirm the secret data key and that it is mounted/decoded correctly (no double base64)."],"exampleFix":"# before: DER-encoded or truncated CA\n--etcd-s3-endpoint-ca=/etc/ssl/ca.der\n# after: PEM bundle, verified first\nopenssl x509 -in /etc/ssl/ca.pem -noout || exit 1\n--etcd-s3-endpoint-ca=/etc/ssl/ca.pem","handlingStrategy":"validation","validationCode":"func validPEM(data []byte) bool {\n\trest := data\n\tfound := false\n\tvar block *pem.Block\n\tfor {\n\t\tblock, rest = pem.Decode(rest)\n\t\tif block == nil {\n\t\t\treturn found\n\t\t}\n\t\tif block.Type == \"CERTIFICATE\" {\n\t\t\tfound = true\n\t\t}\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate CA bundles with openssl x509 -noout in CI before mounting them.","Store bundles as PEM; never DER; watch for truncated multi-cert chains."],"tags":["s3","tls","certificates","configuration"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}