...

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

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

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

View as plain text