Add fault tolerance against clock skew accross system on TLS certificates.

This commit is contained in:
Mariano Cano 2020-01-02 17:48:28 -08:00 committed by max furman
parent e6cafb89b6
commit 5565d61bf3
3 changed files with 48 additions and 13 deletions

View file

@ -28,6 +28,7 @@ var (
MaxVersion: 1.2, MaxVersion: 1.2,
Renegotiation: false, Renegotiation: false,
} }
defaultBackdate = time.Minute
defaultDisableRenewal = false defaultDisableRenewal = false
defaultEnableSSHCA = false defaultEnableSSHCA = false
globalProvisionerClaims = provisioner.Claims{ globalProvisionerClaims = provisioner.Claims{
@ -69,6 +70,7 @@ type AuthConfig struct {
Template *x509util.ASN1DN `json:"template,omitempty"` Template *x509util.ASN1DN `json:"template,omitempty"`
Claims *provisioner.Claims `json:"claims,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"`
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
Backdate *provisioner.Duration `json:"backdate,omitempty"`
} }
// Validate validates the authority configuration. // Validate validates the authority configuration.
@ -91,6 +93,17 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
if c.Template == nil { if c.Template == nil {
c.Template = &x509util.ASN1DN{} c.Template = &x509util.ASN1DN{}
} }
if c.Backdate != nil {
if c.Backdate.Duration < 0 {
return errors.New("authority.backdate cannot be less than 0")
}
} else {
c.Backdate = &provisioner.Duration{
Duration: defaultBackdate,
}
}
return nil return nil
} }

View file

@ -15,10 +15,12 @@ import (
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
) )
// Options contains the options that can be passed to the Sign method. // Options contains the options that can be passed to the Sign method. Backdate
// is automatically filled and can only be configured in the CA.
type Options struct { type Options struct {
NotAfter TimeDuration `json:"notAfter"` NotAfter TimeDuration `json:"notAfter"`
NotBefore TimeDuration `json:"notBefore"` NotBefore TimeDuration `json:"notBefore"`
Backdate time.Duration `json:"-"`
} }
// SignOption is the interface used to collect all extra options used in the // SignOption is the interface used to collect all extra options used in the
@ -189,12 +191,22 @@ func (v emailAddressesValidator) Valid(req *x509.CertificateRequest) error {
type profileDefaultDuration time.Duration type profileDefaultDuration time.Duration
func (v profileDefaultDuration) Option(so Options) x509util.WithOption { func (v profileDefaultDuration) Option(so Options) x509util.WithOption {
var backdate time.Duration
notBefore := so.NotBefore.Time() notBefore := so.NotBefore.Time()
if notBefore.IsZero() { if notBefore.IsZero() {
notBefore = time.Now() notBefore = time.Now()
backdate = -1 * so.Backdate
} }
notAfter := so.NotAfter.RelativeTime(notBefore) notAfter := so.NotAfter.RelativeTime(notBefore)
return x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v)) return func(p x509util.Profile) error {
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v))
if err := fn(p); err != nil {
return err
}
crt := p.Subject()
crt.NotBefore = crt.NotBefore.Add(backdate)
return nil
}
} }
// profileLimitDuration is an x509 profile option that modifies an x509 validity // profileLimitDuration is an x509 profile option that modifies an x509 validity
@ -208,10 +220,12 @@ type profileLimitDuration struct {
// certificate to one that is superficially imposed. // certificate to one that is superficially imposed.
func (v profileLimitDuration) Option(so Options) x509util.WithOption { func (v profileLimitDuration) Option(so Options) x509util.WithOption {
return func(p x509util.Profile) error { return func(p x509util.Profile) error {
var backdate time.Duration
n := now() n := now()
notBefore := so.NotBefore.Time() notBefore := so.NotBefore.Time()
if notBefore.IsZero() { if notBefore.IsZero() {
notBefore = n notBefore = n
backdate = -1 * so.Backdate
} }
if notBefore.After(v.notAfter) { if notBefore.After(v.notAfter) {
return errors.Errorf("provisioning credential expiration (%s) is before "+ return errors.Errorf("provisioning credential expiration (%s) is before "+
@ -232,7 +246,7 @@ func (v profileLimitDuration) Option(so Options) x509util.WithOption {
} }
} }
crt := p.Subject() crt := p.Subject()
crt.NotBefore = notBefore crt.NotBefore = notBefore.Add(backdate)
crt.NotAfter = notAfter crt.NotAfter = notAfter
return nil return nil
} }
@ -255,9 +269,10 @@ func (v *validityValidator) Valid(crt *x509.Certificate) error {
var ( var (
na = crt.NotAfter na = crt.NotAfter
nb = crt.NotBefore nb = crt.NotBefore
d = na.Sub(nb)
now = time.Now() now = time.Now()
) )
// Get duration from to not take into account the backdate.
var d = na.Sub(now)
if na.Before(now) { if na.Before(now) {
return errors.Errorf("NotAfter: %v cannot be in the past", na) return errors.Errorf("NotAfter: %v cannot be in the past", na)

View file

@ -65,6 +65,10 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
certValidators = []provisioner.CertificateValidator{} certValidators = []provisioner.CertificateValidator{}
issIdentity = a.intermediateIdentity issIdentity = a.intermediateIdentity
) )
// Set backdate with the configured value
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
for _, op := range extraOpts { for _, op := range extraOpts {
switch k := op.(type) { switch k := op.(type) {
case provisioner.CertificateValidator: case provisioner.CertificateValidator:
@ -136,14 +140,17 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error
// Issuer // Issuer
issIdentity := a.intermediateIdentity issIdentity := a.intermediateIdentity
now := time.Now().UTC() // Durations
backdate := a.config.AuthorityConfig.Backdate.Duration
duration := oldCert.NotAfter.Sub(oldCert.NotBefore) duration := oldCert.NotAfter.Sub(oldCert.NotBefore)
now := time.Now().UTC()
newCert := &x509.Certificate{ newCert := &x509.Certificate{
PublicKey: oldCert.PublicKey, PublicKey: oldCert.PublicKey,
Issuer: issIdentity.Crt.Subject, Issuer: issIdentity.Crt.Subject,
Subject: oldCert.Subject, Subject: oldCert.Subject,
NotBefore: now, NotBefore: now.Add(-1 * backdate),
NotAfter: now.Add(duration), NotAfter: now.Add(duration - backdate),
KeyUsage: oldCert.KeyUsage, KeyUsage: oldCert.KeyUsage,
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions, UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
ExtKeyUsage: oldCert.ExtKeyUsage, ExtKeyUsage: oldCert.ExtKeyUsage,