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 }