Skip to content

Commit 6045694

Browse files
authored
Merge pull request kubernetes#94504 from neolit123/1.20-warning-cert-bounds-client-side
kubeadm: print warnings on invalid cert period instead of erroring out
2 parents 55be3c0 + b5b9698 commit 6045694

File tree

6 files changed

+74
-11
lines changed

6 files changed

+74
-11
lines changed

cmd/kubeadm/app/cmd/phases/init/certs.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/pkg/errors"
2424
"github.com/spf13/pflag"
25+
2526
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
2627
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
2728
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
@@ -228,7 +229,9 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
228229
return nil
229230
}
230231

231-
if _, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), ca.BaseName); err == nil {
232+
if cert, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), ca.BaseName); err == nil {
233+
certsphase.CheckCertificatePeriodValidity(ca.BaseName, cert)
234+
232235
if _, err := pkiutil.TryLoadKeyFromDisk(data.CertificateDir(), ca.BaseName); err == nil {
233236
fmt.Printf("[certs] Using existing %s certificate authority\n", ca.BaseName)
234237
return nil
@@ -261,11 +264,15 @@ func runCertPhase(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert)
261264
}
262265

263266
if certData, _, err := pkiutil.TryLoadCertAndKeyFromDisk(data.CertificateDir(), cert.BaseName); err == nil {
267+
certsphase.CheckCertificatePeriodValidity(cert.BaseName, certData)
268+
264269
caCertData, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), caCert.BaseName)
265270
if err != nil {
266271
return errors.Wrapf(err, "couldn't load CA certificate %s", caCert.Name)
267272
}
268273

274+
certsphase.CheckCertificatePeriodValidity(caCert.BaseName, caCertData)
275+
269276
if err := certData.CheckSignatureFrom(caCertData); err != nil {
270277
return errors.Wrapf(err, "[certs] certificate %s not signed by CA certificate %s", cert.BaseName, caCert.BaseName)
271278
}

cmd/kubeadm/app/phases/certs/certlist.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
124124

125125
caCert, err := pkiutil.TryLoadCertFromDisk(ic.CertificatesDir, ca.BaseName)
126126
if err == nil {
127+
// Validate period
128+
CheckCertificatePeriodValidity(ca.BaseName, caCert)
129+
127130
// Cert exists already, make sure it's valid
128131
if !caCert.IsCA {
129132
return errors.Errorf("certificate %q is not a CA", ca.Name)

cmd/kubeadm/app/phases/certs/certs.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"os"
2424
"path/filepath"
25+
"sync"
2526

2627
"github.com/pkg/errors"
2728
"k8s.io/client-go/util/keyutil"
@@ -32,6 +33,12 @@ import (
3233
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
3334
)
3435

36+
var (
37+
// certPeriodValidation is used to store if period validation was done for a certificate
38+
certPeriodValidationMutex sync.Mutex
39+
certPeriodValidation = map[string]struct{}{}
40+
)
41+
3542
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
3643
// If the PKI assets already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned.
3744
func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
@@ -166,6 +173,8 @@ func LoadCertificateAuthority(pkiDir string, baseName string) (*x509.Certificate
166173
if err != nil {
167174
return nil, nil, errors.Wrapf(err, "failure loading %s certificate authority", baseName)
168175
}
176+
// Validate period
177+
CheckCertificatePeriodValidity(baseName, caCert)
169178

170179
// Make sure the loaded CA cert actually is a CA
171180
if !caCert.IsCA {
@@ -189,6 +198,8 @@ func writeCertificateAuthorityFilesIfNotExist(pkiDir string, baseName string, ca
189198
if err != nil {
190199
return errors.Wrapf(err, "failure loading %s certificate", baseName)
191200
}
201+
// Validate period
202+
CheckCertificatePeriodValidity(baseName, caCert)
192203

193204
// Check if the existing cert is a CA
194205
if !caCert.IsCA {
@@ -223,6 +234,8 @@ func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert
223234
if err != nil {
224235
return errors.Wrapf(err, "failure loading %s certificate", baseName)
225236
}
237+
// Validate period
238+
CheckCertificatePeriodValidity(baseName, signedCert)
226239

227240
// Check if the existing cert is signed by the given CA
228241
if err := signedCert.CheckSignatureFrom(signingCert); err != nil {
@@ -365,6 +378,8 @@ func validateCACert(l certKeyLocation) error {
365378
if err != nil {
366379
return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
367380
}
381+
// Validate period
382+
CheckCertificatePeriodValidity(l.uxName, caCert)
368383

369384
// Check if cert is a CA
370385
if !caCert.IsCA {
@@ -395,6 +410,8 @@ func validateSignedCert(l certKeyLocation) error {
395410
if err != nil {
396411
return errors.Wrapf(err, "failure loading certificate authority for %s", l.uxName)
397412
}
413+
// Validate period
414+
CheckCertificatePeriodValidity(l.uxName, caCert)
398415

399416
return validateSignedCertWithCA(l, caCert)
400417
}
@@ -406,6 +423,8 @@ func validateSignedCertWithCA(l certKeyLocation, caCert *x509.Certificate) error
406423
if err != nil {
407424
return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
408425
}
426+
// Validate period
427+
CheckCertificatePeriodValidity(l.uxName, signedCert)
409428

410429
// Check if the cert is signed by the CA
411430
if err := signedCert.CheckSignatureFrom(caCert); err != nil {
@@ -439,3 +458,21 @@ func validateCertificateWithConfig(cert *x509.Certificate, baseName string, cfg
439458
}
440459
return nil
441460
}
461+
462+
// CheckCertificatePeriodValidity takes a certificate and prints a warning if its period
463+
// is not valid related to the current time. It does so only if the certificate was not validated already
464+
// by keeping track with a cache.
465+
func CheckCertificatePeriodValidity(baseName string, cert *x509.Certificate) {
466+
certPeriodValidationMutex.Lock()
467+
if _, exists := certPeriodValidation[baseName]; exists {
468+
certPeriodValidationMutex.Unlock()
469+
return
470+
}
471+
certPeriodValidation[baseName] = struct{}{}
472+
certPeriodValidationMutex.Unlock()
473+
474+
klog.V(5).Infof("validating certificate period for %s certificate", baseName)
475+
if err := pkiutil.ValidateCertPeriod(cert, 0); err != nil {
476+
klog.Warningf("WARNING: could not validate bounds for certificate %s: %v", baseName, err)
477+
}
478+
}

cmd/kubeadm/app/phases/kubeconfig/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ go_library(
1616
deps = [
1717
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
1818
"//cmd/kubeadm/app/constants:go_default_library",
19+
"//cmd/kubeadm/app/phases/certs:go_default_library",
1920
"//cmd/kubeadm/app/util:go_default_library",
2021
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
2122
"//cmd/kubeadm/app/util/pkiutil:go_default_library",

cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"k8s.io/klog/v2"
3535
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
3636
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
37+
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
3738
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
3839
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
3940
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
@@ -140,6 +141,9 @@ func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConf
140141
if err != nil {
141142
return nil, errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
142143
}
144+
// Validate period
145+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
146+
143147
configs, err := getKubeConfigSpecsBase(cfg)
144148
if err != nil {
145149
return nil, err
@@ -282,6 +286,8 @@ func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfigurat
282286
if err != nil {
283287
return errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
284288
}
289+
// Validate period
290+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
285291

286292
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
287293
if err != nil {
@@ -309,6 +315,8 @@ func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration,
309315
if err != nil {
310316
return errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
311317
}
318+
// Validate period
319+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
312320

313321
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
314322
if err != nil {
@@ -354,6 +362,8 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
354362
if err != nil {
355363
return errors.Wrapf(err, "the CA file couldn't be loaded")
356364
}
365+
// Validate period
366+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
357367

358368
// validate user provided kubeconfig files for the scheduler and controller-manager
359369
localAPIEndpoint, err := kubeadmutil.GetLocalAPIEndpoint(&cfg.LocalAPIEndpoint)

cmd/kubeadm/app/util/pkiutil/pki_helpers.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, crypto.
240240
return cert, key, nil
241241
}
242242

243-
// TryLoadCertFromDisk tries to load the cert from the disk and validates that it is valid
243+
// TryLoadCertFromDisk tries to load the cert from the disk
244244
func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
245245
certificatePath := pathForCert(pkiPath, name)
246246

@@ -253,15 +253,6 @@ func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
253253
// TODO: Support multiple certs here in order to be able to rotate certs
254254
cert := certs[0]
255255

256-
// Check so that the certificate is valid now
257-
now := time.Now()
258-
if now.Before(cert.NotBefore) {
259-
return nil, errors.New("the certificate is not valid yet")
260-
}
261-
if now.After(cert.NotAfter) {
262-
return nil, errors.New("the certificate has expired")
263-
}
264-
265256
return cert, nil
266257
}
267258

@@ -619,3 +610,17 @@ func RemoveDuplicateAltNames(altNames *certutil.AltNames) {
619610
}
620611
altNames.IPs = ips
621612
}
613+
614+
// ValidateCertPeriod checks if the certificate is valid relative to the current time
615+
// (+/- offset)
616+
func ValidateCertPeriod(cert *x509.Certificate, offset time.Duration) error {
617+
period := fmt.Sprintf("NotBefore: %v, NotAfter: %v", cert.NotBefore, cert.NotAfter)
618+
now := time.Now().Add(offset)
619+
if now.Before(cert.NotBefore) {
620+
return errors.Errorf("the certificate is not valid yet: %s", period)
621+
}
622+
if now.After(cert.NotAfter) {
623+
return errors.Errorf("the certificate has expired: %s", period)
624+
}
625+
return nil
626+
}

0 commit comments

Comments
 (0)