diff --git a/pkg/daemons/control/deps/deps.go b/pkg/daemons/control/deps/deps.go index be10d9ebca5f..6753e6c792ed 100644 --- a/pkg/daemons/control/deps/deps.go +++ b/pkg/daemons/control/deps/deps.go @@ -24,6 +24,7 @@ import ( "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/passwd" "github.com/k3s-io/k3s/pkg/token" + "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/version" certutil "github.com/rancher/dynamiclistener/cert" "github.com/sirupsen/logrus" @@ -592,7 +593,7 @@ func createClientCertKey(regen bool, commonName string, organization []string, a return false, err } - caCert, err := certutil.CertsFromFile(caCertFile) + caCerts, err := certutil.CertsFromFile(caCertFile) if err != nil { return false, err } @@ -615,12 +616,12 @@ func createClientCertKey(regen bool, commonName string, organization []string, a if altNames != nil { cfg.AltNames = *altNames } - cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCert[0], caKey.(crypto.Signer)) + cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCerts[0], caKey.(crypto.Signer)) if err != nil { return false, err } - return true, certutil.WriteCert(certFile, append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + return true, certutil.WriteCert(certFile, util.EncodeCertsPEM(cert, caCerts)) } func exists(files ...string) bool { diff --git a/pkg/server/cert.go b/pkg/server/cert.go index e90e2c6c4e35..db26f632cd7b 100644 --- a/pkg/server/cert.go +++ b/pkg/server/cert.go @@ -143,10 +143,6 @@ func validateCA(oldCAPath, newCAPath string) error { return err } - if len(oldCerts) == 1 { - return errors.New("old CA is self-signed") - } - newCerts, err := certutil.CertsFromFile(newCAPath) if err != nil { return err @@ -158,16 +154,26 @@ func validateCA(oldCAPath, newCAPath string) error { roots := x509.NewCertPool() intermediates := x509.NewCertPool() - for i, cert := range oldCerts { + + // 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 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { - roots.AddCert(cert) - } else { + if len(cert.AuthorityKeyId) > 0 { intermediates.AddCert(cert) } } } + // Verify the first cert in the bundle, using the combined roots and intermediates _, err = newCerts[0].Verify(x509.VerifyOptions{Roots: roots, Intermediates: intermediates}) if err != nil { err = errors.Wrap(err, "new CA cert cannot be verified using old CA chain") diff --git a/pkg/server/router.go b/pkg/server/router.go index 37169e1fe1b1..435e6b7fdf45 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -219,7 +219,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo return } - caCert, caKey, key, err := getCACertAndKeys(server.Runtime.ServerCA, server.Runtime.ServerCAKey, server.Runtime.ServingKubeletKey) + caCerts, caKey, key, err := getCACertAndKeys(server.Runtime.ServerCA, server.Runtime.ServerCAKey, server.Runtime.ServingKubeletKey) if err != nil { sendError(err, resp) return @@ -245,7 +245,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo DNSNames: []string{nodeName, "localhost"}, IPs: ips, }, - }, key, caCert[0], caKey) + }, key, caCerts[0], caKey) if err != nil { sendError(err, resp) return @@ -257,7 +257,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo return } - resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + resp.Write(util.EncodeCertsPEM(cert, caCerts)) resp.Write(keyBytes) }) } @@ -275,7 +275,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot return } - caCert, caKey, key, err := getCACertAndKeys(server.Runtime.ClientCA, server.Runtime.ClientCAKey, server.Runtime.ClientKubeletKey) + caCerts, caKey, key, err := getCACertAndKeys(server.Runtime.ClientCA, server.Runtime.ClientCAKey, server.Runtime.ClientKubeletKey) if err != nil { sendError(err, resp) return @@ -285,7 +285,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot CommonName: "system:node:" + nodeName, Organization: []string{user.NodesGroup}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }, key, caCert[0], caKey) + }, key, caCerts[0], caKey) if err != nil { sendError(err, resp) return @@ -297,7 +297,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot return } - resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + resp.Write(util.EncodeCertsPEM(cert, caCerts)) resp.Write(keyBytes) }) } diff --git a/pkg/util/cert.go b/pkg/util/cert.go new file mode 100644 index 000000000000..bad59d8d9a4f --- /dev/null +++ b/pkg/util/cert.go @@ -0,0 +1,17 @@ +package util + +import ( + "crypto/x509" + + certutil "github.com/rancher/dynamiclistener/cert" +) + +// EncodeCertsPEM is a wrapper around the EncodeCertPEM function to return the +// PEM encoding of a cert and chain, instead of just a single cert. +func EncodeCertsPEM(cert *x509.Certificate, caCerts []*x509.Certificate) []byte { + pemBytes := certutil.EncodeCertPEM(cert) + for _, caCert := range caCerts { + pemBytes = append(pemBytes, certutil.EncodeCertPEM(caCert)...) + } + return pemBytes +}