From 6603cf6357470e72aa66ec25a93e538d447f6972 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Wed, 8 Dec 2021 20:37:58 +0200 Subject: [PATCH] kubeadm: validate local etcd certficates during expiration checks In case stacked etcd is used, the code that does expiration checks does not validate if the etcd CA is "external" (missing key) and if the etcd CA signed certificates are valid. Add a new function UsingExternalEtcdCA() similar to existing functions for the cluster CA and front-proxy CA, that performs the checks for missing etcd CA key and certificate validity. This function only runs for stacked etcd, since if etcd is external kubeadm does not track any certs signed by that etcd CA. This fixes a bug where the etcd CA will be reported as local even if the etcd/ca.key is missing during "certs check-expiration". --- cmd/kubeadm/app/phases/certs/certs.go | 32 +++++++++++++++++++ .../app/phases/certs/renewal/manager.go | 6 +++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go index 7e8a60bce91..738fccf1743 100644 --- a/cmd/kubeadm/app/phases/certs/certs.go +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -381,6 +381,38 @@ func UsingExternalFrontProxyCA(cfg *kubeadmapi.ClusterConfiguration) (bool, erro return true, nil } +// UsingExternalEtcdCA determines whether the user is relying on an external etcd CA. We currently implicitly determine this is the case +// when the etcd CA Cert is present but the etcd CA Key is not. +// In case we are using an external etcd CA, the function validates the certificates signed by etcd CA that should be provided by the user. +func UsingExternalEtcdCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) { + if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, "", "etcd CA"}); err != nil { + return false, err + } + + path := filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCAKeyName) + if _, err := os.Stat(path); !os.IsNotExist(err) { + return false, nil + } + + if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.APIServerEtcdClientCertAndKeyBaseName, "apiserver etcd client"}); err != nil { + return true, err + } + + if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdServerCertAndKeyBaseName, "etcd server"}); err != nil { + return true, err + } + + if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdPeerCertAndKeyBaseName, "etcd peer"}); err != nil { + return true, err + } + + if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName, "etcd health-check client"}); err != nil { + return true, err + } + + return true, nil +} + // validateCACert tries to load a x509 certificate from pkiDir and validates that it is a CA func validateCACert(l certKeyLocation) error { // Check CA Cert diff --git a/cmd/kubeadm/app/phases/certs/renewal/manager.go b/cmd/kubeadm/app/phases/certs/renewal/manager.go index 09421d7e816..9f5e0d158f9 100644 --- a/cmd/kubeadm/app/phases/certs/renewal/manager.go +++ b/cmd/kubeadm/app/phases/certs/renewal/manager.go @@ -374,7 +374,11 @@ func (rm *Manager) IsExternallyManaged(caBaseName string) (bool, error) { } return externallyManaged, nil case kubeadmconstants.EtcdCACertAndKeyBaseName: - return false, nil + externallyManaged, err := certsphase.UsingExternalEtcdCA(rm.cfg) + if err != nil { + return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName) + } + return externallyManaged, nil default: return false, errors.Errorf("unknown certificate authority %s", caBaseName) }