Skip to content

Commit b5b9698

Browse files
committed
kubeadm: print warnings on invalid cert period instead of erroring out
Client side period validation of certificates should not be fatal, as local clock skews are not so uncommon. The validation should be left to the running servers. - Remove this validation from TryLoadCertFromDisk(). - Add a new function ValidateCertPeriod(), that can be used for this purpose on demand. - In phases/certs add a new function CheckCertificatePeriodValidity() that will print warnings if a certificate does not pass period validation, and caches certificates that were already checked. - Use the function in a number of places where certificates are loaded from disk.
1 parent 9d9d305 commit b5b9698

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 {
@@ -364,6 +377,8 @@ func validateCACert(l certKeyLocation) error {
364377
if err != nil {
365378
return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
366379
}
380+
// Validate period
381+
CheckCertificatePeriodValidity(l.uxName, caCert)
367382

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

398415
return validateSignedCertWithCA(l, caCert)
399416
}
@@ -405,6 +422,8 @@ func validateSignedCertWithCA(l certKeyLocation, caCert *x509.Certificate) error
405422
if err != nil {
406423
return errors.Wrapf(err, "failure loading certificate for %s", l.uxName)
407424
}
425+
// Validate period
426+
CheckCertificatePeriodValidity(l.uxName, signedCert)
408427

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

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"
@@ -124,6 +125,9 @@ func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConf
124125
if err != nil {
125126
return nil, errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
126127
}
128+
// Validate period
129+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
130+
127131
configs, err := getKubeConfigSpecsBase(cfg)
128132
if err != nil {
129133
return nil, err
@@ -265,6 +269,8 @@ func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfigurat
265269
if err != nil {
266270
return errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
267271
}
272+
// Validate period
273+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
268274

269275
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
270276
if err != nil {
@@ -292,6 +298,8 @@ func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration,
292298
if err != nil {
293299
return errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
294300
}
301+
// Validate period
302+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
295303

296304
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
297305
if err != nil {
@@ -344,6 +352,8 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
344352
if err != nil {
345353
return errors.Wrapf(err, "the CA file couldn't be loaded")
346354
}
355+
// Validate period
356+
certsphase.CheckCertificatePeriodValidity(kubeadmconstants.CACertAndKeyBaseName, caCert)
347357

348358
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
349359
if err != nil {

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)