k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/phases/certs/renewal/manager.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package renewal
    18  
    19  import (
    20  	"crypto/x509"
    21  	"sort"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	certutil "k8s.io/client-go/util/cert"
    26  
    27  	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
    28  	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
    29  	certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
    30  	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
    31  	"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
    32  )
    33  
    34  // Manager can be used to coordinate certificate renewal and related processes,
    35  // like CSR generation or checking certificate expiration
    36  type Manager struct {
    37  	// cfg holds the kubeadm ClusterConfiguration
    38  	cfg *kubeadmapi.ClusterConfiguration
    39  
    40  	// kubernetesDir holds the directory where kubeConfig files are stored
    41  	kubernetesDir string
    42  
    43  	// certificates contains the certificateRenewHandler controlled by this manager
    44  	certificates map[string]*CertificateRenewHandler
    45  
    46  	// cas contains the CAExpirationHandler related to the certificates that are controlled by this manager
    47  	cas map[string]*CAExpirationHandler
    48  }
    49  
    50  type certConfigMutatorFunc func(*certutil.Config) error
    51  
    52  // CertificateRenewHandler defines required info for renewing a certificate
    53  type CertificateRenewHandler struct {
    54  	// Name of the certificate to be used for UX.
    55  	// This value can be used to trigger operations on this certificate
    56  	Name string
    57  
    58  	// LongName of the certificate to be used for UX
    59  	LongName string
    60  
    61  	// FileName defines the name (or the BaseName) of the certificate file
    62  	FileName string
    63  
    64  	// CAName defines the name for the CA on which this certificate depends
    65  	CAName string
    66  
    67  	// CABaseName defines the base name for the CA that should be used for certificate renewal
    68  	CABaseName string
    69  
    70  	// readwriter defines a CertificateReadWriter to be used for certificate renewal
    71  	readwriter certificateReadWriter
    72  
    73  	// certConfigMutators holds the mutator functions that can be applied to the input cert config object
    74  	// These functions will be run in series.
    75  	certConfigMutators []certConfigMutatorFunc
    76  }
    77  
    78  // CAExpirationHandler defines required info for CA expiration check
    79  type CAExpirationHandler struct {
    80  	// Name of the CA to be used for UX.
    81  	// This value can be used to trigger operations on this CA
    82  	Name string
    83  
    84  	// LongName of the CA to be used for UX
    85  	LongName string
    86  
    87  	// FileName defines the name (or the BaseName) of the CA file
    88  	FileName string
    89  
    90  	// readwriter defines a CertificateReadWriter to be used for CA expiration check
    91  	readwriter certificateReadWriter
    92  }
    93  
    94  // NewManager return a new certificate renewal manager ready for handling certificates in the cluster
    95  func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Manager, error) {
    96  	rm := &Manager{
    97  		cfg:           cfg,
    98  		kubernetesDir: kubernetesDir,
    99  		certificates:  map[string]*CertificateRenewHandler{},
   100  		cas:           map[string]*CAExpirationHandler{},
   101  	}
   102  
   103  	// gets the list of certificates that are expected according to the current cluster configuration
   104  	certListFunc := certsphase.GetDefaultCertList
   105  	if cfg.Etcd.External != nil {
   106  		certListFunc = certsphase.GetCertsWithoutEtcd
   107  	}
   108  	certTree, err := certListFunc().AsMap().CertTree()
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	// create a CertificateRenewHandler for each signed certificate in the certificate tree;
   114  	// NB. we are not offering support for renewing CAs; this would cause serious consequences
   115  	for ca, certs := range certTree {
   116  		for _, cert := range certs {
   117  			// create a ReadWriter for certificates stored in the K8s local PKI
   118  			pkiReadWriter := newPKICertificateReadWriter(rm.cfg.CertificatesDir, cert.BaseName)
   119  			certConfigMutators := loadCertConfigMutators(cert.BaseName)
   120  
   121  			// adds the certificateRenewHandler.
   122  			// PKI certificates are indexed by name, that is a well know constant defined
   123  			// in the certsphase package and that can be reused across all the kubeadm codebase
   124  			rm.certificates[cert.Name] = &CertificateRenewHandler{
   125  				Name:               cert.Name,
   126  				LongName:           cert.LongName,
   127  				FileName:           cert.BaseName,
   128  				CAName:             ca.Name,
   129  				CABaseName:         ca.BaseName, // Nb. this is a path for etcd certs (they are stored in a subfolder)
   130  				readwriter:         pkiReadWriter,
   131  				certConfigMutators: certConfigMutators,
   132  			}
   133  		}
   134  
   135  		pkiReadWriter := newPKICertificateReadWriter(rm.cfg.CertificatesDir, ca.BaseName)
   136  		rm.cas[ca.Name] = &CAExpirationHandler{
   137  			Name:       ca.Name,
   138  			LongName:   ca.LongName,
   139  			FileName:   ca.BaseName,
   140  			readwriter: pkiReadWriter,
   141  		}
   142  	}
   143  
   144  	// gets the list of certificates that should be considered for renewal
   145  	kubeConfigs := []struct {
   146  		longName string
   147  		fileName string
   148  	}{
   149  		{
   150  			longName: "certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself",
   151  			fileName: kubeadmconstants.AdminKubeConfigFileName,
   152  		},
   153  		{
   154  			longName: "certificate embedded in the kubeconfig file for the super-admin",
   155  			fileName: kubeadmconstants.SuperAdminKubeConfigFileName,
   156  		},
   157  		{
   158  			longName: "certificate embedded in the kubeconfig file for the controller manager to use",
   159  			fileName: kubeadmconstants.ControllerManagerKubeConfigFileName,
   160  		},
   161  		{
   162  			longName: "certificate embedded in the kubeconfig file for the scheduler manager to use",
   163  			fileName: kubeadmconstants.SchedulerKubeConfigFileName,
   164  		},
   165  		//NB. we are excluding KubeletKubeConfig from renewal because management of this certificate is delegated to kubelet
   166  	}
   167  
   168  	// create a CertificateRenewHandler for each kubeConfig file
   169  	for _, kubeConfig := range kubeConfigs {
   170  		// create a ReadWriter for certificates embedded in kubeConfig files
   171  		kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName,
   172  			rm.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
   173  
   174  		// adds the certificateRenewHandler.
   175  		// Certificates embedded kubeConfig files in are indexed by fileName, that is a well know constant defined
   176  		// in the kubeadm constants package and that can be reused across all the kubeadm codebase
   177  		rm.certificates[kubeConfig.fileName] = &CertificateRenewHandler{
   178  			Name:       kubeConfig.fileName, // we are using fileName as name, because there is nothing similar outside
   179  			LongName:   kubeConfig.longName,
   180  			FileName:   kubeConfig.fileName,
   181  			CABaseName: kubeadmconstants.CACertAndKeyBaseName, // all certificates in kubeConfig files are signed by the Kubernetes CA
   182  			CAName:     kubeadmconstants.CACertAndKeyBaseName,
   183  			readwriter: kubeConfigReadWriter,
   184  		}
   185  	}
   186  
   187  	return rm, nil
   188  }
   189  
   190  // Certificates returns the list of certificates controlled by this Manager
   191  func (rm *Manager) Certificates() []*CertificateRenewHandler {
   192  	certificates := []*CertificateRenewHandler{}
   193  	for _, h := range rm.certificates {
   194  		certificates = append(certificates, h)
   195  	}
   196  
   197  	sort.Slice(certificates, func(i, j int) bool { return certificates[i].Name < certificates[j].Name })
   198  
   199  	return certificates
   200  }
   201  
   202  // CAs returns the list of CAs related to the certificates that are controlled by this manager
   203  func (rm *Manager) CAs() []*CAExpirationHandler {
   204  	cas := []*CAExpirationHandler{}
   205  	for _, h := range rm.cas {
   206  		cas = append(cas, h)
   207  	}
   208  
   209  	sort.Slice(cas, func(i, j int) bool { return cas[i].Name < cas[j].Name })
   210  
   211  	return cas
   212  }
   213  
   214  // RenewUsingLocalCA executes certificate renewal using local certificate authorities for generating new certs.
   215  // For PKI certificates, use the name defined in the certsphase package, while for certificates
   216  // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
   217  // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
   218  func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) {
   219  	handler, ok := rm.certificates[name]
   220  	if !ok {
   221  		return false, errors.Errorf("%s is not a valid certificate for this cluster", name)
   222  	}
   223  
   224  	// checks if the certificate is externally managed (CA certificate provided without the certificate key)
   225  	externallyManaged, err := rm.IsExternallyManaged(handler.CABaseName)
   226  	if err != nil {
   227  		return false, err
   228  	}
   229  
   230  	// in case of external CA it is not possible to renew certificates, then return early
   231  	if externallyManaged {
   232  		return false, nil
   233  	}
   234  
   235  	// reads the current certificate
   236  	cert, err := handler.readwriter.Read()
   237  	if err != nil {
   238  		return false, err
   239  	}
   240  
   241  	// extract the certificate config
   242  	certConfig := certToConfig(cert)
   243  	for _, f := range handler.certConfigMutators {
   244  		if err := f(&certConfig); err != nil {
   245  			return false, err
   246  		}
   247  	}
   248  
   249  	cfg := &pkiutil.CertConfig{
   250  		Config:              certConfig,
   251  		EncryptionAlgorithm: rm.cfg.EncryptionAlgorithmType(),
   252  	}
   253  
   254  	startTime := kubeadmutil.StartTimeUTC()
   255  
   256  	// Backdate certificate to allow small time jumps.
   257  	cfg.NotBefore = startTime.Add(-kubeadmconstants.CertificateBackdate)
   258  
   259  	// Use the validity periods defined in the ClusterConfiguration.
   260  	// Only use CertificateValidityPeriod as CA renewal is not supported.
   261  	if rm.cfg.CertificateValidityPeriod != nil {
   262  		cfg.NotAfter = startTime.Add(rm.cfg.CertificateValidityPeriod.Duration)
   263  	}
   264  
   265  	// reads the CA
   266  	caCert, caKey, err := certsphase.LoadCertificateAuthority(rm.cfg.CertificatesDir, handler.CABaseName)
   267  	if err != nil {
   268  		return false, err
   269  	}
   270  
   271  	// create a new certificate with the same config
   272  	newCert, newKey, err := NewFileRenewer(caCert, caKey).Renew(cfg)
   273  	if err != nil {
   274  		return false, errors.Wrapf(err, "failed to renew certificate %s", name)
   275  	}
   276  
   277  	// writes the new certificate to disk
   278  	err = handler.readwriter.Write(newCert, newKey)
   279  	if err != nil {
   280  		return false, err
   281  	}
   282  
   283  	return true, nil
   284  }
   285  
   286  // CreateRenewCSR generates CSR request for certificate renewal.
   287  // For PKI certificates, use the name defined in the certsphase package, while for certificates
   288  // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
   289  // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
   290  func (rm *Manager) CreateRenewCSR(name, outdir string) error {
   291  	handler, ok := rm.certificates[name]
   292  	if !ok {
   293  		return errors.Errorf("%s is not a known certificate", name)
   294  	}
   295  
   296  	// reads the current certificate
   297  	cert, err := handler.readwriter.Read()
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	// extracts the certificate config
   303  	certConfig := certToConfig(cert)
   304  	for _, f := range handler.certConfigMutators {
   305  		if err := f(&certConfig); err != nil {
   306  			return err
   307  		}
   308  	}
   309  	cfg := &pkiutil.CertConfig{
   310  		Config:              certConfig,
   311  		EncryptionAlgorithm: rm.cfg.EncryptionAlgorithmType(),
   312  	}
   313  
   314  	// generates the CSR request and save it
   315  	csr, key, err := pkiutil.NewCSRAndKey(cfg)
   316  	if err != nil {
   317  		return errors.Wrapf(err, "failure while generating %s CSR and key", name)
   318  	}
   319  	if err := pkiutil.WriteKey(outdir, name, key); err != nil {
   320  		return errors.Wrapf(err, "failure while saving %s key", name)
   321  	}
   322  
   323  	if err := pkiutil.WriteCSR(outdir, name, csr); err != nil {
   324  		return errors.Wrapf(err, "failure while saving %s CSR", name)
   325  	}
   326  
   327  	return nil
   328  }
   329  
   330  // CertificateExists returns true if a certificate exists.
   331  func (rm *Manager) CertificateExists(name string) (bool, error) {
   332  	handler, ok := rm.certificates[name]
   333  	if !ok {
   334  		return false, errors.Errorf("%s is not a known certificate", name)
   335  	}
   336  
   337  	return handler.readwriter.Exists()
   338  }
   339  
   340  // GetCertificateExpirationInfo returns certificate expiration info.
   341  // For PKI certificates, use the name defined in the certsphase package, while for certificates
   342  // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
   343  // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
   344  func (rm *Manager) GetCertificateExpirationInfo(name string) (*ExpirationInfo, error) {
   345  	handler, ok := rm.certificates[name]
   346  	if !ok {
   347  		return nil, errors.Errorf("%s is not a known certificate", name)
   348  	}
   349  
   350  	// checks if the certificate is externally managed (CA certificate provided without the certificate key)
   351  	externallyManaged, err := rm.IsExternallyManaged(handler.CABaseName)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	// reads the current certificate
   357  	cert, err := handler.readwriter.Read()
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	// returns the certificate expiration info
   363  	return newExpirationInfo(name, cert, externallyManaged), nil
   364  }
   365  
   366  // CAExists returns true if a certificate authority exists.
   367  func (rm *Manager) CAExists(name string) (bool, error) {
   368  	handler, ok := rm.cas[name]
   369  	if !ok {
   370  		return false, errors.Errorf("%s is not a known certificate", name)
   371  	}
   372  
   373  	return handler.readwriter.Exists()
   374  }
   375  
   376  // GetCAExpirationInfo returns CA expiration info.
   377  func (rm *Manager) GetCAExpirationInfo(name string) (*ExpirationInfo, error) {
   378  	handler, ok := rm.cas[name]
   379  	if !ok {
   380  		return nil, errors.Errorf("%s is not a known CA", name)
   381  	}
   382  
   383  	// checks if the CA is externally managed (CA certificate provided without the certificate key)
   384  	externallyManaged, err := rm.IsExternallyManaged(handler.FileName)
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  
   389  	// reads the current CA
   390  	ca, err := handler.readwriter.Read()
   391  	if err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	// returns the CA expiration info
   396  	return newExpirationInfo(name, ca, externallyManaged), nil
   397  }
   398  
   399  // IsExternallyManaged checks if we are in the external CA case (CA certificate provided without the certificate key)
   400  func (rm *Manager) IsExternallyManaged(caBaseName string) (bool, error) {
   401  	switch caBaseName {
   402  	case kubeadmconstants.CACertAndKeyBaseName:
   403  		externallyManaged, err := certsphase.UsingExternalCA(rm.cfg)
   404  		if err != nil {
   405  			return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName)
   406  		}
   407  		return externallyManaged, nil
   408  	case kubeadmconstants.FrontProxyCACertAndKeyBaseName:
   409  		externallyManaged, err := certsphase.UsingExternalFrontProxyCA(rm.cfg)
   410  		if err != nil {
   411  			return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName)
   412  		}
   413  		return externallyManaged, nil
   414  	case kubeadmconstants.EtcdCACertAndKeyBaseName:
   415  		externallyManaged, err := certsphase.UsingExternalEtcdCA(rm.cfg)
   416  		if err != nil {
   417  			return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", caBaseName)
   418  		}
   419  		return externallyManaged, nil
   420  	default:
   421  		return false, errors.Errorf("unknown certificate authority %s", caBaseName)
   422  	}
   423  }
   424  
   425  func certToConfig(cert *x509.Certificate) certutil.Config {
   426  	return certutil.Config{
   427  		CommonName:   cert.Subject.CommonName,
   428  		Organization: cert.Subject.Organization,
   429  		AltNames: certutil.AltNames{
   430  			IPs:      cert.IPAddresses,
   431  			DNSNames: cert.DNSNames,
   432  		},
   433  		Usages: cert.ExtKeyUsage,
   434  	}
   435  }
   436  
   437  func loadCertConfigMutators(certBaseName string) []certConfigMutatorFunc {
   438  	return nil
   439  }