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

     1  /*
     2  Copyright 2018 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 certs
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/x509"
    22  	"fmt"
    23  	"io"
    24  	"path/filepath"
    25  	"time"
    26  
    27  	"github.com/pkg/errors"
    28  
    29  	certutil "k8s.io/client-go/util/cert"
    30  	"k8s.io/klog/v2"
    31  
    32  	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
    33  	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
    34  	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
    35  	"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
    36  )
    37  
    38  const (
    39  	errInvalid = "invalid argument"
    40  	errExist   = "file already exists"
    41  )
    42  
    43  type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *pkiutil.CertConfig) error
    44  
    45  // KubeadmCert represents a certificate that Kubeadm will create to function properly.
    46  type KubeadmCert struct {
    47  	Name     string
    48  	LongName string
    49  	BaseName string
    50  	CAName   string
    51  	// Some attributes will depend on the InitConfiguration, only known at runtime.
    52  	// These functions will be run in series, passed both the InitConfiguration and a cert Config.
    53  	configMutators []configMutatorsFunc
    54  	config         pkiutil.CertConfig
    55  	// Used for unit tests.
    56  	creationTime time.Time
    57  }
    58  
    59  // GetConfig returns the definition for the given cert given the provided InitConfiguration
    60  func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*pkiutil.CertConfig, error) {
    61  	for _, f := range k.configMutators {
    62  		if err := f(ic, &k.config); err != nil {
    63  			return nil, err
    64  		}
    65  	}
    66  
    67  	// creationTime should be set only during unit tests, otherwise the kubeadm start time
    68  	// should be
    69  	if k.creationTime.IsZero() {
    70  		k.creationTime = kubeadmutil.StartTimeUTC()
    71  	}
    72  
    73  	// Backdate certificate to allow small time jumps.
    74  	k.config.NotBefore = k.creationTime.Add(-kubeadmconstants.CertificateBackdate)
    75  
    76  	// Use the validity periods defined in the ClusterConfiguration.
    77  	// If CAName is empty this is a CA cert.
    78  	if len(k.CAName) != 0 {
    79  		if ic.ClusterConfiguration.CertificateValidityPeriod != nil {
    80  			k.config.NotAfter = k.creationTime.
    81  				Add(ic.ClusterConfiguration.CertificateValidityPeriod.Duration)
    82  		}
    83  	} else {
    84  		if ic.ClusterConfiguration.CACertificateValidityPeriod != nil {
    85  			k.config.NotAfter = k.creationTime.
    86  				Add(ic.ClusterConfiguration.CACertificateValidityPeriod.Duration)
    87  		}
    88  	}
    89  
    90  	// Use the encryption algorithm from ClusterConfiguration.
    91  	k.config.EncryptionAlgorithm = ic.ClusterConfiguration.EncryptionAlgorithmType()
    92  	return &k.config, nil
    93  }
    94  
    95  // CreateFromCA makes and writes a certificate using the given CA cert and key.
    96  func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey crypto.Signer) error {
    97  	cfg, err := k.GetConfig(ic)
    98  	if err != nil {
    99  		return errors.Wrapf(err, "couldn't create %q certificate", k.Name)
   100  	}
   101  	cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, cfg)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	err = writeCertificateFilesIfNotExist(
   106  		ic.CertificatesDir,
   107  		k.BaseName,
   108  		caCert,
   109  		cert,
   110  		key,
   111  		cfg,
   112  	)
   113  
   114  	if err != nil {
   115  		return errors.Wrapf(err, "failed to write or validate certificate %q", k.Name)
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // CreateAsCA creates a certificate authority, writing the files to disk and also returning the created CA so it can be used to sign child certs.
   122  func (k *KubeadmCert) CreateAsCA(ic *kubeadmapi.InitConfiguration) (*x509.Certificate, crypto.Signer, error) {
   123  	cfg, err := k.GetConfig(ic)
   124  	if err != nil {
   125  		return nil, nil, errors.Wrapf(err, "couldn't get configuration for %q CA certificate", k.Name)
   126  	}
   127  	caCert, caKey, err := pkiutil.NewCertificateAuthority(cfg)
   128  	if err != nil {
   129  		return nil, nil, errors.Wrapf(err, "couldn't generate %q CA certificate", k.Name)
   130  	}
   131  
   132  	err = writeCertificateAuthorityFilesIfNotExist(
   133  		ic.CertificatesDir,
   134  		k.BaseName,
   135  		caCert,
   136  		caKey,
   137  	)
   138  	if err != nil {
   139  		return nil, nil, errors.Wrapf(err, "couldn't write out %q CA certificate", k.Name)
   140  	}
   141  
   142  	return caCert, caKey, nil
   143  }
   144  
   145  // CertificateTree is represents a one-level-deep tree, mapping a CA to the certs that depend on it.
   146  type CertificateTree map[*KubeadmCert]Certificates
   147  
   148  // CreateTree creates the CAs, certs signed by the CAs, and writes them all to disk.
   149  func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
   150  	for ca, leaves := range t {
   151  		cfg, err := ca.GetConfig(ic)
   152  		if err != nil {
   153  			return err
   154  		}
   155  
   156  		var caKey crypto.Signer
   157  
   158  		caCert, err := pkiutil.TryLoadCertFromDisk(ic.CertificatesDir, ca.BaseName)
   159  		if err == nil {
   160  			// Validate period
   161  			CheckCertificatePeriodValidity(ca.BaseName, caCert)
   162  
   163  			// Cert exists already, make sure it's valid
   164  			if !caCert.IsCA {
   165  				return errors.Errorf("certificate %q is not a CA", ca.Name)
   166  			}
   167  			// Try and load a CA Key
   168  			caKey, err = pkiutil.TryLoadKeyFromDisk(ic.CertificatesDir, ca.BaseName)
   169  			if err != nil {
   170  				// If there's no CA key, make sure every certificate exists.
   171  				for _, leaf := range leaves {
   172  					cl := certKeyLocation{
   173  						pkiDir:   ic.CertificatesDir,
   174  						baseName: leaf.BaseName,
   175  						uxName:   leaf.Name,
   176  					}
   177  					if err := validateSignedCertWithCA(cl, caCert); err != nil {
   178  						return errors.Wrapf(err, "could not load expected certificate %q or validate the existence of key %q for it", leaf.Name, ca.Name)
   179  					}
   180  				}
   181  				continue
   182  			}
   183  			// CA key exists; just use that to create new certificates.
   184  			klog.V(1).Infof("[certs] Using the existing CA certificate %q and key %q\n", filepath.Join(ic.CertificatesDir, fmt.Sprintf("%s.crt", ca.BaseName)), filepath.Join(ic.CertificatesDir, fmt.Sprintf("%s.key", ca.BaseName)))
   185  		} else {
   186  			// CACert doesn't already exist, create a new cert and key.
   187  			caCert, caKey, err = pkiutil.NewCertificateAuthority(cfg)
   188  			if err != nil {
   189  				return err
   190  			}
   191  
   192  			err = writeCertificateAuthorityFilesIfNotExist(
   193  				ic.CertificatesDir,
   194  				ca.BaseName,
   195  				caCert,
   196  				caKey,
   197  			)
   198  			if err != nil {
   199  				return err
   200  			}
   201  		}
   202  
   203  		for _, leaf := range leaves {
   204  			if err := leaf.CreateFromCA(ic, caCert, caKey); err != nil {
   205  				return err
   206  			}
   207  		}
   208  	}
   209  	return nil
   210  }
   211  
   212  // CertificateMap is a flat map of certificates, keyed by Name.
   213  type CertificateMap map[string]*KubeadmCert
   214  
   215  // CertTree returns a one-level-deep tree, mapping a CA cert to an array of certificates that should be signed by it.
   216  func (m CertificateMap) CertTree() (CertificateTree, error) {
   217  	caMap := make(CertificateTree)
   218  
   219  	for _, cert := range m {
   220  		if cert.CAName == "" {
   221  			if _, ok := caMap[cert]; !ok {
   222  				caMap[cert] = []*KubeadmCert{}
   223  			}
   224  		} else {
   225  			ca, ok := m[cert.CAName]
   226  			if !ok {
   227  				return nil, errors.Errorf("certificate %q references unknown CA %q", cert.Name, cert.CAName)
   228  			}
   229  			caMap[ca] = append(caMap[ca], cert)
   230  		}
   231  	}
   232  
   233  	return caMap, nil
   234  }
   235  
   236  // Certificates is a list of Certificates that Kubeadm should create.
   237  type Certificates []*KubeadmCert
   238  
   239  // AsMap returns the list of certificates as a map, keyed by name.
   240  func (c Certificates) AsMap() CertificateMap {
   241  	certMap := make(map[string]*KubeadmCert)
   242  	for _, cert := range c {
   243  		certMap[cert.Name] = cert
   244  	}
   245  
   246  	return certMap
   247  }
   248  
   249  // GetDefaultCertList returns  all of the certificates kubeadm requires to function.
   250  func GetDefaultCertList() Certificates {
   251  	return Certificates{
   252  		KubeadmCertRootCA(),
   253  		KubeadmCertAPIServer(),
   254  		KubeadmCertKubeletClient(),
   255  		// Front Proxy certs
   256  		KubeadmCertFrontProxyCA(),
   257  		KubeadmCertFrontProxyClient(),
   258  		// etcd certs
   259  		KubeadmCertEtcdCA(),
   260  		KubeadmCertEtcdServer(),
   261  		KubeadmCertEtcdPeer(),
   262  		KubeadmCertEtcdHealthcheck(),
   263  		KubeadmCertEtcdAPIClient(),
   264  	}
   265  }
   266  
   267  // GetCertsWithoutEtcd returns all of the certificates kubeadm needs when etcd is hosted externally.
   268  func GetCertsWithoutEtcd() Certificates {
   269  	return Certificates{
   270  		KubeadmCertRootCA(),
   271  		KubeadmCertAPIServer(),
   272  		KubeadmCertKubeletClient(),
   273  		// Front Proxy certs
   274  		KubeadmCertFrontProxyCA(),
   275  		KubeadmCertFrontProxyClient(),
   276  	}
   277  }
   278  
   279  // KubeadmCertRootCA is the definition of the Kubernetes Root CA for the API Server and kubelet.
   280  func KubeadmCertRootCA() *KubeadmCert {
   281  	return &KubeadmCert{
   282  		Name:     "ca",
   283  		LongName: "self-signed Kubernetes CA to provision identities for other Kubernetes components",
   284  		BaseName: kubeadmconstants.CACertAndKeyBaseName,
   285  		config: pkiutil.CertConfig{
   286  			Config: certutil.Config{
   287  				CommonName: "kubernetes",
   288  			},
   289  		},
   290  	}
   291  }
   292  
   293  // KubeadmCertAPIServer is the definition of the cert used to serve the Kubernetes API.
   294  func KubeadmCertAPIServer() *KubeadmCert {
   295  	return &KubeadmCert{
   296  		Name:     "apiserver",
   297  		LongName: "certificate for serving the Kubernetes API",
   298  		BaseName: kubeadmconstants.APIServerCertAndKeyBaseName,
   299  		CAName:   "ca",
   300  		config: pkiutil.CertConfig{
   301  			Config: certutil.Config{
   302  				CommonName: kubeadmconstants.APIServerCertCommonName,
   303  				Usages:     []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   304  			},
   305  		},
   306  		configMutators: []configMutatorsFunc{
   307  			makeAltNamesMutator(pkiutil.GetAPIServerAltNames),
   308  		},
   309  	}
   310  }
   311  
   312  // KubeadmCertKubeletClient is the definition of the cert used by the API server to access the kubelet.
   313  func KubeadmCertKubeletClient() *KubeadmCert {
   314  	return &KubeadmCert{
   315  		Name:     "apiserver-kubelet-client",
   316  		LongName: "certificate for the API server to connect to kubelet",
   317  		BaseName: kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName,
   318  		CAName:   "ca",
   319  		config: pkiutil.CertConfig{
   320  			Config: certutil.Config{
   321  				CommonName:   kubeadmconstants.APIServerKubeletClientCertCommonName,
   322  				Organization: []string{kubeadmconstants.ClusterAdminsGroupAndClusterRoleBinding},
   323  				Usages:       []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
   324  			},
   325  		},
   326  	}
   327  }
   328  
   329  // KubeadmCertFrontProxyCA is the definition of the CA used for the front end proxy.
   330  func KubeadmCertFrontProxyCA() *KubeadmCert {
   331  	return &KubeadmCert{
   332  		Name:     "front-proxy-ca",
   333  		LongName: "self-signed CA to provision identities for front proxy",
   334  		BaseName: kubeadmconstants.FrontProxyCACertAndKeyBaseName,
   335  		config: pkiutil.CertConfig{
   336  			Config: certutil.Config{
   337  				CommonName: "front-proxy-ca",
   338  			},
   339  		},
   340  	}
   341  }
   342  
   343  // KubeadmCertFrontProxyClient is the definition of the cert used by the API server to access the front proxy.
   344  func KubeadmCertFrontProxyClient() *KubeadmCert {
   345  	return &KubeadmCert{
   346  		Name:     "front-proxy-client",
   347  		BaseName: kubeadmconstants.FrontProxyClientCertAndKeyBaseName,
   348  		LongName: "certificate for the front proxy client",
   349  		CAName:   "front-proxy-ca",
   350  		config: pkiutil.CertConfig{
   351  			Config: certutil.Config{
   352  				CommonName: kubeadmconstants.FrontProxyClientCertCommonName,
   353  				Usages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
   354  			},
   355  		},
   356  	}
   357  }
   358  
   359  // KubeadmCertEtcdCA is the definition of the root CA used by the hosted etcd server.
   360  func KubeadmCertEtcdCA() *KubeadmCert {
   361  	return &KubeadmCert{
   362  		Name:     "etcd-ca",
   363  		LongName: "self-signed CA to provision identities for etcd",
   364  		BaseName: kubeadmconstants.EtcdCACertAndKeyBaseName,
   365  		config: pkiutil.CertConfig{
   366  			Config: certutil.Config{
   367  				CommonName: "etcd-ca",
   368  			},
   369  		},
   370  	}
   371  }
   372  
   373  // KubeadmCertEtcdServer is the definition of the cert used to serve etcd to clients.
   374  func KubeadmCertEtcdServer() *KubeadmCert {
   375  	return &KubeadmCert{
   376  		Name:     "etcd-server",
   377  		LongName: "certificate for serving etcd",
   378  		BaseName: kubeadmconstants.EtcdServerCertAndKeyBaseName,
   379  		CAName:   "etcd-ca",
   380  		config: pkiutil.CertConfig{
   381  			Config: certutil.Config{
   382  				// TODO: etcd 3.2 introduced an undocumented requirement for ClientAuth usage on the
   383  				// server cert: https://github.com/etcd-io/etcd/issues/9785#issuecomment-396715692
   384  				// Once the upstream issue is resolved, this should be returned to only allowing
   385  				// ServerAuth usage.
   386  				Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   387  			},
   388  		},
   389  		configMutators: []configMutatorsFunc{
   390  			makeAltNamesMutator(pkiutil.GetEtcdAltNames),
   391  			setCommonNameToNodeName(),
   392  		},
   393  	}
   394  }
   395  
   396  // KubeadmCertEtcdPeer is the definition of the cert used by etcd peers to access each other.
   397  func KubeadmCertEtcdPeer() *KubeadmCert {
   398  	return &KubeadmCert{
   399  		Name:     "etcd-peer",
   400  		LongName: "certificate for etcd nodes to communicate with each other",
   401  		BaseName: kubeadmconstants.EtcdPeerCertAndKeyBaseName,
   402  		CAName:   "etcd-ca",
   403  		config: pkiutil.CertConfig{
   404  			Config: certutil.Config{
   405  				Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   406  			},
   407  		},
   408  		configMutators: []configMutatorsFunc{
   409  			makeAltNamesMutator(pkiutil.GetEtcdPeerAltNames),
   410  			setCommonNameToNodeName(),
   411  		},
   412  	}
   413  }
   414  
   415  // KubeadmCertEtcdHealthcheck is the definition of the cert used by Kubernetes to check the health of the etcd server.
   416  func KubeadmCertEtcdHealthcheck() *KubeadmCert {
   417  	return &KubeadmCert{
   418  		Name:     "etcd-healthcheck-client",
   419  		LongName: "certificate for liveness probes to healthcheck etcd",
   420  		BaseName: kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName,
   421  		CAName:   "etcd-ca",
   422  		config: pkiutil.CertConfig{
   423  			Config: certutil.Config{
   424  				CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName,
   425  				Usages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
   426  			},
   427  		},
   428  	}
   429  }
   430  
   431  // KubeadmCertEtcdAPIClient is the definition of the cert used by the API server to access etcd.
   432  func KubeadmCertEtcdAPIClient() *KubeadmCert {
   433  	return &KubeadmCert{
   434  		Name:     "apiserver-etcd-client",
   435  		LongName: "certificate the apiserver uses to access etcd",
   436  		BaseName: kubeadmconstants.APIServerEtcdClientCertAndKeyBaseName,
   437  		CAName:   "etcd-ca",
   438  		config: pkiutil.CertConfig{
   439  			Config: certutil.Config{
   440  				CommonName: kubeadmconstants.APIServerEtcdClientCertCommonName,
   441  				Usages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
   442  			},
   443  		},
   444  	}
   445  }
   446  
   447  func makeAltNamesMutator(f func(*kubeadmapi.InitConfiguration) (*certutil.AltNames, error)) configMutatorsFunc {
   448  	return func(mc *kubeadmapi.InitConfiguration, cc *pkiutil.CertConfig) error {
   449  		altNames, err := f(mc)
   450  		if err != nil {
   451  			return err
   452  		}
   453  		cc.AltNames = *altNames
   454  		return nil
   455  	}
   456  }
   457  
   458  func setCommonNameToNodeName() configMutatorsFunc {
   459  	return func(mc *kubeadmapi.InitConfiguration, cc *pkiutil.CertConfig) error {
   460  		cc.CommonName = mc.NodeRegistration.Name
   461  		return nil
   462  	}
   463  }
   464  
   465  // leafCertificates returns non-CA certificates from the supplied Certificates.
   466  func leafCertificates(c Certificates) (Certificates, error) {
   467  	certTree, err := c.AsMap().CertTree()
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  
   472  	var out Certificates
   473  	for _, leafCertificates := range certTree {
   474  		out = append(out, leafCertificates...)
   475  	}
   476  	return out, nil
   477  }
   478  
   479  func createKeyAndCSR(kubeadmConfig *kubeadmapi.InitConfiguration, cert *KubeadmCert) error {
   480  	if kubeadmConfig == nil {
   481  		return errors.Errorf("%s: kubeadmConfig was nil", errInvalid)
   482  	}
   483  	if cert == nil {
   484  		return errors.Errorf("%s: cert was nil", errInvalid)
   485  	}
   486  	certDir := kubeadmConfig.CertificatesDir
   487  	name := cert.BaseName
   488  	if pkiutil.CSROrKeyExist(certDir, name) {
   489  		return errors.Errorf("%s: key or CSR %s/%s", errExist, certDir, name)
   490  	}
   491  	cfg, err := cert.GetConfig(kubeadmConfig)
   492  	if err != nil {
   493  		return err
   494  	}
   495  	csr, key, err := pkiutil.NewCSRAndKey(cfg)
   496  	if err != nil {
   497  		return err
   498  	}
   499  	err = pkiutil.WriteKey(certDir, name, key)
   500  	if err != nil {
   501  		return err
   502  	}
   503  	return pkiutil.WriteCSR(certDir, name, csr)
   504  }
   505  
   506  // CreateDefaultKeysAndCSRFiles is used in ExternalCA mode to create key files
   507  // and adjacent CSR files.
   508  func CreateDefaultKeysAndCSRFiles(out io.Writer, config *kubeadmapi.InitConfiguration) error {
   509  	certificates, err := leafCertificates(GetDefaultCertList())
   510  	if err != nil {
   511  		return err
   512  	}
   513  	if out != nil {
   514  		fmt.Fprintf(out, "generating keys and CSRs in %s\n", config.CertificatesDir)
   515  	}
   516  	for _, cert := range certificates {
   517  		if err := createKeyAndCSR(config, cert); err != nil {
   518  			return err
   519  		}
   520  		if out != nil {
   521  			fmt.Fprintf(out, "  %s\n", cert.BaseName)
   522  		}
   523  	}
   524  	return nil
   525  }