1
16
17 package certs
18
19 import (
20 "crypto"
21 "crypto/x509"
22 "fmt"
23 "os"
24 "path/filepath"
25 "sync"
26
27 "github.com/pkg/errors"
28
29 utilerrors "k8s.io/apimachinery/pkg/util/errors"
30 "k8s.io/client-go/util/keyutil"
31 "k8s.io/klog/v2"
32
33 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
34 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
35 "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
36 )
37
38 var (
39
40 certPeriodValidationMutex sync.Mutex
41 certPeriodValidation = map[string]struct{}{}
42 )
43
44
45
46 func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
47 klog.V(1).Infoln("creating PKI assets")
48
49
50
51
52 var certList Certificates
53
54 if cfg.Etcd.Local == nil {
55 certList = GetCertsWithoutEtcd()
56 } else {
57 certList = GetDefaultCertList()
58 }
59
60 certTree, err := certList.AsMap().CertTree()
61 if err != nil {
62 return err
63 }
64
65 if err := certTree.CreateTree(cfg); err != nil {
66 return errors.Wrap(err, "error creating PKI assets")
67 }
68
69 fmt.Printf("[certs] Valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
70
71
72 return CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir, cfg.ClusterConfiguration.EncryptionAlgorithmType())
73 }
74
75
76
77 func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string, keyType kubeadmapi.EncryptionAlgorithmType) error {
78 klog.V(1).Infoln("creating new public/private key files for signing service account users")
79 _, err := keyutil.PrivateKeyFromFile(filepath.Join(certsDir, kubeadmconstants.ServiceAccountPrivateKeyName))
80 if err == nil {
81
82
83
84 fmt.Printf("[certs] Using the existing %q key\n", kubeadmconstants.ServiceAccountKeyBaseName)
85 return nil
86 } else if !os.IsNotExist(err) {
87 return errors.Wrapf(err, "file %s existed but it could not be loaded properly", kubeadmconstants.ServiceAccountPrivateKeyName)
88 }
89
90
91 key, err := pkiutil.NewPrivateKey(keyType)
92 if err != nil {
93 return err
94 }
95
96
97 fmt.Printf("[certs] Generating %q key and public key\n", kubeadmconstants.ServiceAccountKeyBaseName)
98
99 if err := pkiutil.WriteKey(certsDir, kubeadmconstants.ServiceAccountKeyBaseName, key); err != nil {
100 return err
101 }
102
103 return pkiutil.WritePublicKey(certsDir, kubeadmconstants.ServiceAccountKeyBaseName, key.Public())
104 }
105
106
107
108 func CreateCACertAndKeyFiles(certSpec *KubeadmCert, cfg *kubeadmapi.InitConfiguration) error {
109 if certSpec.CAName != "" {
110 return errors.Errorf("this function should only be used for CAs, but cert %s has CA %s", certSpec.Name, certSpec.CAName)
111 }
112 klog.V(1).Infof("creating a new certificate authority for %s", certSpec.Name)
113
114 certConfig, err := certSpec.GetConfig(cfg)
115 if err != nil {
116 return err
117 }
118
119 caCert, caKey, err := pkiutil.NewCertificateAuthority(certConfig)
120 if err != nil {
121 return err
122 }
123
124 return writeCertificateAuthorityFilesIfNotExist(
125 cfg.CertificatesDir,
126 certSpec.BaseName,
127 caCert,
128 caKey,
129 )
130 }
131
132
133 func NewCSR(certSpec *KubeadmCert, cfg *kubeadmapi.InitConfiguration) (*x509.CertificateRequest, crypto.Signer, error) {
134 certConfig, err := certSpec.GetConfig(cfg)
135 if err != nil {
136 return nil, nil, errors.Wrap(err, "failed to retrieve cert configuration")
137 }
138
139 return pkiutil.NewCSRAndKey(certConfig)
140 }
141
142
143
144 func CreateCertAndKeyFilesWithCA(certSpec *KubeadmCert, caCertSpec *KubeadmCert, cfg *kubeadmapi.InitConfiguration) error {
145 if certSpec.CAName != caCertSpec.Name {
146 return errors.Errorf("expected CAname for %s to be %q, but was %s", certSpec.Name, certSpec.CAName, caCertSpec.Name)
147 }
148
149 caCert, caKey, err := LoadCertificateAuthority(cfg.CertificatesDir, caCertSpec.BaseName)
150 if err != nil {
151 return errors.Wrapf(err, "couldn't load CA certificate %s", caCertSpec.Name)
152 }
153
154 return certSpec.CreateFromCA(cfg, caCert, caKey)
155 }
156
157
158 func LoadCertificateAuthority(pkiDir string, baseName string) (*x509.Certificate, crypto.Signer, error) {
159
160 if !pkiutil.CertOrKeyExist(pkiDir, baseName) {
161 return nil, nil, errors.Errorf("couldn't load %s certificate authority from %s", baseName, pkiDir)
162 }
163
164
165 caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, baseName)
166 if err != nil {
167 return nil, nil, errors.Wrapf(err, "failure loading %s certificate authority", baseName)
168 }
169
170 CheckCertificatePeriodValidity(baseName, caCert)
171
172
173 if !caCert.IsCA {
174 return nil, nil, errors.Errorf("%s certificate is not a certificate authority", baseName)
175 }
176
177 return caCert, caKey, nil
178 }
179
180
181
182
183
184 func writeCertificateAuthorityFilesIfNotExist(pkiDir string, baseName string, caCert *x509.Certificate, caKey crypto.Signer) error {
185
186
187 if pkiutil.CertOrKeyExist(pkiDir, baseName) {
188
189
190 caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, baseName)
191 if err != nil {
192 return errors.Wrapf(err, "failure loading %s certificate", baseName)
193 }
194
195 CheckCertificatePeriodValidity(baseName, caCert)
196
197
198 if !caCert.IsCA {
199 return errors.Errorf("certificate %s is not a CA", baseName)
200 }
201
202
203
204
205 fmt.Printf("[certs] Using the existing %q certificate and key\n", baseName)
206 } else {
207
208 fmt.Printf("[certs] Generating %q certificate and key\n", baseName)
209
210 if err := pkiutil.WriteCertAndKey(pkiDir, baseName, caCert, caKey); err != nil {
211 return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
212 }
213 }
214 return nil
215 }
216
217
218
219
220
221 func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert *x509.Certificate, cert *x509.Certificate, key crypto.Signer, cfg *pkiutil.CertConfig) error {
222
223
224 if pkiutil.CertOrKeyExist(pkiDir, baseName) {
225
226 _, err := pkiutil.TryLoadKeyFromDisk(pkiDir, baseName)
227 if err != nil {
228 return errors.Wrapf(err, "failure loading %s key", baseName)
229 }
230
231
232 signedCert, intermediates, err := pkiutil.TryLoadCertChainFromDisk(pkiDir, baseName)
233 if err != nil {
234 return errors.Wrapf(err, "failure loading %s certificate", baseName)
235 }
236
237 CheckCertificatePeriodValidity(baseName, signedCert)
238
239
240 if err := pkiutil.VerifyCertChain(signedCert, intermediates, signingCert); err != nil {
241 return errors.Errorf("certificate %s is not signed by corresponding CA", baseName)
242 }
243
244
245 if err := validateCertificateWithConfig(signedCert, baseName, cfg); err != nil {
246 return err
247 }
248
249 fmt.Printf("[certs] Using the existing %q certificate and key\n", baseName)
250 } else {
251
252 fmt.Printf("[certs] Generating %q certificate and key\n", baseName)
253
254 if err := pkiutil.WriteCertAndKey(pkiDir, baseName, cert, key); err != nil {
255 return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
256 }
257 if pkiutil.HasServerAuth(cert) {
258 fmt.Printf("[certs] %s serving cert is signed for DNS names %v and IPs %v\n", baseName, cert.DNSNames, cert.IPAddresses)
259 }
260 }
261
262 return nil
263 }
264
265 type certKeyLocation struct {
266 pkiDir string
267 caBaseName string
268 baseName string
269 uxName string
270 }
271
272
273
274
275 func SharedCertificateExists(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
276 var errs []error
277 if err := validateCACertAndKey(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil {
278 errs = append(errs, err)
279 }
280
281 if err := validatePrivatePublicKey(certKeyLocation{cfg.CertificatesDir, "", kubeadmconstants.ServiceAccountKeyBaseName, "service account"}); err != nil {
282 errs = append(errs, err)
283 }
284
285 if err := validateCACertAndKey(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, "", "front-proxy CA"}); err != nil {
286 errs = append(errs, err)
287 }
288
289
290 if cfg.Etcd.External == nil {
291 if err := validateCACertAndKey(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, "", "etcd CA"}); err != nil {
292 errs = append(errs, err)
293 }
294 }
295 if len(errs) != 0 {
296 return false, utilerrors.NewAggregate(errs)
297 }
298 return true, nil
299 }
300
301
302
303
304
305 func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
306
307 if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil {
308 return false, err
309 }
310
311 caKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)
312 if _, err := os.Stat(caKeyPath); !os.IsNotExist(err) {
313 return false, nil
314 }
315
316 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerCertAndKeyBaseName, "API server"}); err != nil {
317 return true, err
318 }
319
320 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName, "API server kubelet client"}); err != nil {
321 return true, err
322 }
323
324 return true, nil
325 }
326
327
328
329
330 func UsingExternalFrontProxyCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
331
332 if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, "", "front-proxy CA"}); err != nil {
333 return false, err
334 }
335
336 frontProxyCAKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName)
337 if _, err := os.Stat(frontProxyCAKeyPath); !os.IsNotExist(err) {
338 return false, nil
339 }
340
341 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, kubeadmconstants.FrontProxyClientCertAndKeyBaseName, "front-proxy client"}); err != nil {
342 return true, err
343 }
344
345 return true, nil
346 }
347
348
349
350
351 func UsingExternalEtcdCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
352 if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, "", "etcd CA"}); err != nil {
353 return false, err
354 }
355
356 path := filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCAKeyName)
357 if _, err := os.Stat(path); !os.IsNotExist(err) {
358 return false, nil
359 }
360
361 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.APIServerEtcdClientCertAndKeyBaseName, "apiserver etcd client"}); err != nil {
362 return true, err
363 }
364
365 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdServerCertAndKeyBaseName, "etcd server"}); err != nil {
366 return true, err
367 }
368
369 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdPeerCertAndKeyBaseName, "etcd peer"}); err != nil {
370 return true, err
371 }
372
373 if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName, kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName, "etcd health-check client"}); err != nil {
374 return true, err
375 }
376
377 return true, nil
378 }
379
380
381 func validateCACert(l certKeyLocation) error {
382
383 caCert, err := pkiutil.TryLoadCertFromDisk(l.pkiDir, l.caBaseName)
384 if err != nil {
385 return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
386 }
387
388 CheckCertificatePeriodValidity(l.uxName, caCert)
389
390
391 if !caCert.IsCA {
392 return errors.Errorf("certificate %s is not a CA", l.uxName)
393 }
394 return nil
395 }
396
397
398
399 func validateCACertAndKey(l certKeyLocation) error {
400 if err := validateCACert(l); err != nil {
401 return err
402 }
403
404 _, err := pkiutil.TryLoadKeyFromDisk(l.pkiDir, l.caBaseName)
405 if err != nil {
406 klog.Warningf("assuming external key for %s: %v", l.uxName, err)
407 }
408 return nil
409 }
410
411
412
413 func validateSignedCert(l certKeyLocation) error {
414
415 caCert, err := pkiutil.TryLoadCertFromDisk(l.pkiDir, l.caBaseName)
416 if err != nil {
417 return errors.Wrapf(err, "failure loading certificate authority for %s", l.uxName)
418 }
419
420 CheckCertificatePeriodValidity(l.uxName, caCert)
421
422 return validateSignedCertWithCA(l, caCert)
423 }
424
425
426
427 func validateSignedCertWithCA(l certKeyLocation, caCert *x509.Certificate) error {
428
429 _, err := pkiutil.TryLoadKeyFromDisk(l.pkiDir, l.baseName)
430 if err != nil {
431 return errors.Wrapf(err, "failure loading key for %s", l.baseName)
432 }
433
434
435 signedCert, intermediates, err := pkiutil.TryLoadCertChainFromDisk(l.pkiDir, l.baseName)
436 if err != nil {
437 return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
438 }
439
440 CheckCertificatePeriodValidity(l.uxName, signedCert)
441
442
443 if err := pkiutil.VerifyCertChain(signedCert, intermediates, caCert); err != nil {
444 return errors.Wrapf(err, "certificate %s is not signed by corresponding CA", l.uxName)
445 }
446 return nil
447 }
448
449
450 func validatePrivatePublicKey(l certKeyLocation) error {
451
452 _, _, err := pkiutil.TryLoadPrivatePublicKeyFromDisk(l.pkiDir, l.baseName)
453 return errors.Wrapf(err, "failure loading key for %s", l.uxName)
454 }
455
456
457
458 func validateCertificateWithConfig(cert *x509.Certificate, baseName string, cfg *pkiutil.CertConfig) error {
459 for _, dnsName := range cfg.AltNames.DNSNames {
460 if err := cert.VerifyHostname(dnsName); err != nil {
461 return errors.Wrapf(err, "certificate %s is invalid", baseName)
462 }
463 }
464 for _, ipAddress := range cfg.AltNames.IPs {
465 if err := cert.VerifyHostname(ipAddress.String()); err != nil {
466 return errors.Wrapf(err, "certificate %s is invalid", baseName)
467 }
468 }
469 return nil
470 }
471
472
473
474
475 func CheckCertificatePeriodValidity(baseName string, cert *x509.Certificate) {
476 certPeriodValidationMutex.Lock()
477 defer certPeriodValidationMutex.Unlock()
478 if _, exists := certPeriodValidation[baseName]; exists {
479 return
480 }
481 certPeriodValidation[baseName] = struct{}{}
482
483 klog.V(5).Infof("validating certificate period for %s certificate", baseName)
484 if err := pkiutil.ValidateCertPeriod(cert, 0); err != nil {
485 klog.Warningf("WARNING: could not validate bounds for certificate %s: %v", baseName, err)
486 }
487 }
488
View as plain text