...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal/manager.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal

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

View as plain text