From 68f7c2ade971b5b07af9bcce75f4445f6eb8916f Mon Sep 17 00:00:00 2001 From: SataQiu Date: Wed, 16 Oct 2019 22:52:01 +0800 Subject: [PATCH] kubeadm: enhance certs check-expiration to show the expiration info of related CAs --- cmd/kubeadm/app/cmd/alpha/certs.go | 22 ++++- .../app/phases/certs/renewal/manager.go | 91 ++++++++++++++++--- 2 files changed, 99 insertions(+), 14 deletions(-) diff --git a/cmd/kubeadm/app/cmd/alpha/certs.go b/cmd/kubeadm/app/cmd/alpha/certs.go index f282b7efac8..3f2614a3b5e 100644 --- a/cmd/kubeadm/app/cmd/alpha/certs.go +++ b/cmd/kubeadm/app/cmd/alpha/certs.go @@ -281,9 +281,27 @@ func newCmdCertsExpiration(out io.Writer, kdir string) *cobra.Command { return "no" } w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0) - fmt.Fprintln(w, "CERTIFICATE\tEXPIRES\tRESIDUAL TIME\tEXTERNALLY MANAGED") + fmt.Fprintln(w, "CERTIFICATE\tEXPIRES\tRESIDUAL TIME\tCERTIFICATE AUTHORITY\tEXTERNALLY MANAGED") for _, handler := range rm.Certificates() { - e, err := rm.GetExpirationInfo(handler.Name) + e, err := rm.GetCertificateExpirationInfo(handler.Name) + if err != nil { + return err + } + + s := fmt.Sprintf("%s\t%s\t%s\t%s\t%-8v", + e.Name, + e.ExpirationDate.Format("Jan 02, 2006 15:04 MST"), + duration.ShortHumanDuration(e.ResidualTime()), + handler.CAName, + yesNo(e.ExternallyManaged), + ) + + fmt.Fprintln(w, s) + } + fmt.Fprintln(w) + fmt.Fprintln(w, "CERTIFICATE AUTHORITY\tEXPIRES\tRESIDUAL TIME\tEXTERNALLY MANAGED") + for _, handler := range rm.CAs() { + e, err := rm.GetCAExpirationInfo(handler.Name) if err != nil { return err } diff --git a/cmd/kubeadm/app/phases/certs/renewal/manager.go b/cmd/kubeadm/app/phases/certs/renewal/manager.go index dabd3616036..d675d10c402 100644 --- a/cmd/kubeadm/app/phases/certs/renewal/manager.go +++ b/cmd/kubeadm/app/phases/certs/renewal/manager.go @@ -40,6 +40,9 @@ type Manager struct { // certificates contains the certificateRenewHandler controlled by this manager certificates map[string]*CertificateRenewHandler + + // cas contains the CAExpirationHandler related to the certificates that are controlled by this manager + cas map[string]*CAExpirationHandler } // CertificateRenewHandler defines required info for renewing a certificate @@ -54,10 +57,29 @@ type CertificateRenewHandler struct { // FileName defines the name (or the BaseName) of the certificate file FileName string - // CABaseName define the base name for the CA that should be used for certificate renewal + // CAName defines the name for the CA on which this certificate depends + CAName string + + // CABaseName defines the base name for the CA that should be used for certificate renewal CABaseName string - // readwriter define a CertificateReadWriter to be used for certificate renewal + // readwriter defines a CertificateReadWriter to be used for certificate renewal + readwriter certificateReadWriter +} + +// CAExpirationHandler defines required info for CA expiration check +type CAExpirationHandler struct { + // Name of the CA to be used for UX. + // This value can be used to trigger operations on this CA + Name string + + // LongName of the CA to be used for UX + LongName string + + // FileName defines the name (or the BaseName) of the CA file + FileName string + + // readwriter defines a CertificateReadWriter to be used for CA expiration check readwriter certificateReadWriter } @@ -67,6 +89,7 @@ func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Ma cfg: cfg, kubernetesDir: kubernetesDir, certificates: map[string]*CertificateRenewHandler{}, + cas: map[string]*CAExpirationHandler{}, } // gets the list of certificates that are expected according to the current cluster configuration @@ -93,10 +116,19 @@ func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Ma Name: cert.Name, LongName: cert.LongName, FileName: cert.BaseName, + CAName: ca.Name, CABaseName: ca.BaseName, //Nb. this is a path for etcd certs (they are stored in a subfolder) readwriter: pkiReadWriter, } } + + pkiReadWriter := newPKICertificateReadWriter(rm.cfg.CertificatesDir, ca.BaseName) + rm.cas[ca.Name] = &CAExpirationHandler{ + Name: ca.Name, + LongName: ca.LongName, + FileName: ca.BaseName, + readwriter: pkiReadWriter, + } } // gets the list of certificates that should be considered for renewal @@ -139,7 +171,7 @@ func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Ma return rm, nil } -// Certificates return the list of certificates controlled by this Manager +// Certificates returns the list of certificates controlled by this Manager func (rm *Manager) Certificates() []*CertificateRenewHandler { certificates := []*CertificateRenewHandler{} for _, h := range rm.certificates { @@ -151,6 +183,18 @@ func (rm *Manager) Certificates() []*CertificateRenewHandler { return certificates } +// CAs returns the list of CAs related to the certificates that are controlled by this manager +func (rm *Manager) CAs() []*CAExpirationHandler { + cas := []*CAExpirationHandler{} + for _, h := range rm.cas { + cas = append(cas, h) + } + + sort.Slice(cas, func(i, j int) bool { return cas[i].Name < cas[j].Name }) + + return cas +} + // RenewUsingLocalCA executes certificate renewal using local certificate authorities for generating new certs. // For PKI certificates, use the name defined in the certsphase package, while for certificates // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package. @@ -162,7 +206,7 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) { } // checks if the certificate is externally managed (CA certificate provided without the certificate key) - externallyManaged, err := rm.IsExternallyManaged(handler) + externallyManaged, err := rm.IsExternallyManaged(handler.CABaseName) if err != nil { return false, err } @@ -271,18 +315,18 @@ func (rm *Manager) CreateRenewCSR(name, outdir string) error { return nil } -// GetExpirationInfo returns certificate expiration info. +// GetCertificateExpirationInfo returns certificate expiration info. // For PKI certificates, use the name defined in the certsphase package, while for certificates // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package. // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value. -func (rm *Manager) GetExpirationInfo(name string) (*ExpirationInfo, error) { +func (rm *Manager) GetCertificateExpirationInfo(name string) (*ExpirationInfo, error) { handler, ok := rm.certificates[name] if !ok { return nil, errors.Errorf("%s is not a known certificate", name) } // checks if the certificate is externally managed (CA certificate provided without the certificate key) - externallyManaged, err := rm.IsExternallyManaged(handler) + externallyManaged, err := rm.IsExternallyManaged(handler.CABaseName) if err != nil { return nil, err } @@ -297,25 +341,48 @@ func (rm *Manager) GetExpirationInfo(name string) (*ExpirationInfo, error) { return newExpirationInfo(name, cert, externallyManaged), nil } +// GetCAExpirationInfo returns CA expiration info. +func (rm *Manager) GetCAExpirationInfo(name string) (*ExpirationInfo, error) { + handler, ok := rm.cas[name] + if !ok { + return nil, errors.Errorf("%s is not a known CA", name) + } + + // checks if the CA is externally managed (CA certificate provided without the certificate key) + externallyManaged, err := rm.IsExternallyManaged(handler.FileName) + if err != nil { + return nil, err + } + + // reads the current CA + ca, err := handler.readwriter.Read() + if err != nil { + return nil, err + } + + // returns the CA expiration info + return newExpirationInfo(name, ca, externallyManaged), nil +} + // IsExternallyManaged checks if we are in the external CA case (CA certificate provided without the certificate key) -func (rm *Manager) IsExternallyManaged(h *CertificateRenewHandler) (bool, error) { - switch h.CABaseName { +func (rm *Manager) IsExternallyManaged(caBaseName string) (bool, error) { + switch caBaseName { case kubeadmconstants.CACertAndKeyBaseName: externallyManaged, err := certsphase.UsingExternalCA(rm.cfg) if err != nil { - return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", h.CABaseName) + return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName) } return externallyManaged, nil case kubeadmconstants.FrontProxyCACertAndKeyBaseName: externallyManaged, err := certsphase.UsingExternalFrontProxyCA(rm.cfg) if err != nil { - return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", h.CABaseName) + return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName) } return externallyManaged, nil case kubeadmconstants.EtcdCACertAndKeyBaseName: return false, nil default: - return false, errors.Errorf("unknown certificate authority %s", h.CABaseName) + return false, errors.Errorf("unknown certificate authority %s", caBaseName) } }