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 }