k3s-io/k3s · error
new CA bundle contains only a single certificate but should
Error message
new CA bundle contains only a single certificate but should include root or intermediate CA certificates
What it means
During CA rotation (caCertReplace in pkg/server/handlers/cert.go), k3s loads the submitted new CA bundle and rejects it if it contains exactly one certificate. The rotation logic rebuilds root and intermediate pools and verifies the serving/requesting certs against the combined old+new chains, so a single-cert bundle cannot express the root-plus-intermediate (or old-plus-new) chain it needs.
Source
Thrown at pkg/server/handlers/cert.go:189
func validateCA(oldCAPath, newCAPath string) error {
// Skip validation if old values are being reused
if oldCAPath == newCAPath {
return nil
}
oldCerts, err := certutil.CertsFromFile(oldCAPath)
if err != nil {
return err
}
newCerts, err := certutil.CertsFromFile(newCAPath)
if err != nil {
return err
}
if len(newCerts) == 1 {
return errors.New("new CA bundle contains only a single certificate but should include root or intermediate CA certificates")
}
roots := x509.NewCertPool()
intermediates := x509.NewCertPool()
// Load all certs from the old bundle
for _, cert := range oldCerts {
if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
roots.AddCert(cert)
} else {
intermediates.AddCert(cert)
}
}
// Include any intermediates from the new bundle, in case they're cross-signed by a cert in the old bundle
for i, cert := range newCerts {
if i > 0 {
if len(cert.AuthorityKeyId) > 0 {View on GitHub (pinned to 6ba341e396)
Solutions
- Build a full chain bundle: concatenate the new root plus any intermediates (and keep the old root during transition) into one PEM file, e.g. 'cat old-root.crt new-intermediate.crt new-root.crt > bundle.crt'.
- Verify the bundle before submitting: 'grep -c "BEGIN CERTIFICATE" bundle.crt' must be greater than 1, and 'openssl crl2pkcs7 -nocrl -certfile bundle.crt | openssl pkcs7 -print_certs' shows all chain members.
- Re-run rotate-ca with the corrected --cacert file.
Example fix
# before: single cert -> error cat new-root.crt > bundle.crt k3s certificate rotate-ca --cacert=bundle.crt ... # after: chain bundle (intermediate + root, old root kept during rotation) cat old-root.crt new-intermediate.crt new-root.crt > bundle.crt k3s certificate rotate-ca --cacert=bundle.crt --cakey=new-root.key
Defensive patterns
Strategy: validation
Validate before calling
// Count certs in the bundle before submitting rotate-ca
func bundleCertCount(pemPath string) (int, error) {
b, err := os.ReadFile(pemPath)
if err != nil {
return 0, err
}
return bytes.Count(b, []byte("BEGIN CERTIFICATE")), nil
}
// require count > 1 Try / catch
if err != nil && strings.Contains(err.Error(), "single certificate") {
// rebuild bundle as root+intermediates chain and resubmit
} Prevention
- Assemble rotate-ca bundles as concatenated PEM chains (old root + intermediates + new root).
- Validate with 'openssl crl2pkcs7 -nocrl -certfile bundle | openssl pkcs7 -print_certs' before rotating.
- Never hand-rotate with a single PEM block.
When it happens
Trigger: 'k3s certificate rotate-ca' (HTTP PUT /v1-k3s/cacert) with a --cacert file containing only a leaf-equivalent single CA cert - e.g. just the new root, or just an intermediate, without the rest of the chain.
Common situations: Bundles assembled from a single PEM block; copy/paste of only the intermediate from a provider; chain files truncated during transfer.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- method not allowed
- old ServiceAccount signing key not in new ServiceAccount key
- service %s is not recognized
- critical configuration value mismatch between servers
- %s is not a recognized service
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/2372a2a4e56d5bb7.
Report an issue: GitHub.