From f7a5be39426c70d4a776d4e9375629ec7ebd55d0 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Nov 2018 15:57:13 -0800 Subject: [PATCH 1/3] Force the renew of the CA server. --- ca/ca.go | 3 ++- ca/renew.go | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/ca/ca.go b/ca/ca.go index e5a281e9..8f72984f 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -121,6 +121,7 @@ func (ca *CA) Run() error { // Stop stops the CA calling to the server Shutdown method. func (ca *CA) Stop() error { + ca.renewer.Stop() return ca.srv.Shutdown() } @@ -185,7 +186,7 @@ func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, error) { // empty we are implicitly forcing GetCertificate to be the only mechanism // by which the server can find it's own leaf Certificate. tlsConfig.Certificates = []tls.Certificate{} - tlsConfig.GetCertificate = ca.renewer.GetCertificate + tlsConfig.GetCertificate = ca.renewer.GetCertificateForCA // Add support for mutual tls to renew certificates tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven diff --git a/ca/renew.go b/ca/renew.go index c138cb5c..f18d8d11 100644 --- a/ca/renew.go +++ b/ca/renew.go @@ -22,6 +22,7 @@ type TLSRenewer struct { timer *time.Timer renewBefore time.Duration renewJitter time.Duration + certNotAfter time.Time } type tlsRenewerOptions func(r *TLSRenewer) error @@ -91,7 +92,10 @@ func (r *TLSRenewer) RunContext(ctx context.Context) { // Stop prevents the renew timer from firing. func (r *TLSRenewer) Stop() bool { - return r.timer.Stop() + if r.timer != nil { + return r.timer.Stop() + } + return true } // GetCertificate returns the current server certificate. @@ -101,6 +105,15 @@ func (r *TLSRenewer) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Cert return r.getCertificate(), nil } +// GetCertificateForCA returns the current server certificate. It can only be +// used if the renew function creates the new certificate and do not uses a TLS +// request. It's intended to be use by the certificate authority server. +// +// This method is set in the tls.Config GetCertificate property. +func (r *TLSRenewer) GetCertificateForCA(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { + return r.getCertificateForCA(), nil +} + // GetClientCertificate returns the current client certificate. // // This method is set in the tls.Config GetClientCertificate property. @@ -109,6 +122,9 @@ func (r *TLSRenewer) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Cer } // getCertificate returns the certificate using a read-only lock. +// +// Known issue: It will not attempt to renew the certificate if its expired as +// the renew request with mTLS will fail. func (r *TLSRenewer) getCertificate() *tls.Certificate { r.RLock() cert := r.cert @@ -116,10 +132,29 @@ func (r *TLSRenewer) getCertificate() *tls.Certificate { return cert } -// setCertificate updates the certificate using a read-write lock. +// getCertificateForCA returns the certificate using a read-only lock. It will +// automatically renew the certificate if it's expired. +func (r *TLSRenewer) getCertificateForCA() *tls.Certificate { + r.RLock() + // Force certificate renewal if the timer didn't run. + // This is an special case that can happen after a computer sleep. + if time.Now().After(r.certNotAfter) { + r.RUnlock() + r.renewCertificate() + r.RLock() + } + cert := r.cert + r.RUnlock() + return cert +} + +// setCertificate updates the certificate using a read-write lock. It also +// updates certNotAfter with 1m of delta, this will force the renew of the +// certificate if it's expired or about to expire. func (r *TLSRenewer) setCertificate(cert *tls.Certificate) { r.Lock() r.cert = cert + r.certNotAfter = cert.Leaf.NotAfter.Add(-1 * time.Minute) r.Unlock() } @@ -133,7 +168,9 @@ func (r *TLSRenewer) renewCertificate() { r.setCertificate(cert) next = r.nextRenewDuration(cert.Leaf.NotAfter) } - r.timer = time.AfterFunc(next, r.renewCertificate) + r.Lock() + r.timer.Reset(next) + r.Unlock() } func (r *TLSRenewer) nextRenewDuration(notAfter time.Time) time.Duration { From c0107ab5b9bf4ae44e0d982d35a778a824b2f8d7 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 27 Nov 2018 16:25:01 -0800 Subject: [PATCH 2/3] Fix ca renew documentation --- ca/renew.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ca/renew.go b/ca/renew.go index f18d8d11..b7d0fe2b 100644 --- a/ca/renew.go +++ b/ca/renew.go @@ -14,7 +14,7 @@ import ( // certificate. type RenewFunc func() (*tls.Certificate, error) -// TLSRenewer renews automatically a tls certificate with a given function. +// TLSRenewer automatically renews a tls certificate using a give RenewFunc. type TLSRenewer struct { sync.RWMutex RenewCertificate RenewFunc @@ -44,7 +44,7 @@ func WithRenewJitter(j time.Duration) func(r *TLSRenewer) error { } // NewTLSRenewer creates a TLSRenewer for the given cert. It will use the given -// function to get a new certificate when required. +// RenewFunc to get a new certificate when required. func NewTLSRenewer(cert *tls.Certificate, fn RenewFunc, opts ...tlsRenewerOptions) (*TLSRenewer, error) { r := &TLSRenewer{ RenewCertificate: fn, @@ -123,8 +123,10 @@ func (r *TLSRenewer) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Cer // getCertificate returns the certificate using a read-only lock. // -// Known issue: It will not attempt to renew the certificate if its expired as -// the renew request with mTLS will fail. +// Known issue: It cannot renew an expired certificate because the /renew +// endpoint requires a valid client certificate. The certificate can expire +// if the timer does not fire e.g. when the CA is run from a laptop that +// enters sleep mode. func (r *TLSRenewer) getCertificate() *tls.Certificate { r.RLock() cert := r.cert @@ -133,7 +135,7 @@ func (r *TLSRenewer) getCertificate() *tls.Certificate { } // getCertificateForCA returns the certificate using a read-only lock. It will -// automatically renew the certificate if it's expired. +// automatically renew the certificate if it has expired. func (r *TLSRenewer) getCertificateForCA() *tls.Certificate { r.RLock() // Force certificate renewal if the timer didn't run. @@ -149,8 +151,8 @@ func (r *TLSRenewer) getCertificateForCA() *tls.Certificate { } // setCertificate updates the certificate using a read-write lock. It also -// updates certNotAfter with 1m of delta, this will force the renew of the -// certificate if it's expired or about to expire. +// updates certNotAfter with 1m of delta; this will force the renewal of the +// certificate if it is about to expire. func (r *TLSRenewer) setCertificate(cert *tls.Certificate) { r.Lock() r.cert = cert From 7e2f80ac300a2dc9e0346283d92f75e6876bfca6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Nov 2018 16:29:14 -0800 Subject: [PATCH 3/3] Fix grammar error --- ca/renew.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ca/renew.go b/ca/renew.go index b7d0fe2b..aee9dd13 100644 --- a/ca/renew.go +++ b/ca/renew.go @@ -14,7 +14,7 @@ import ( // certificate. type RenewFunc func() (*tls.Certificate, error) -// TLSRenewer automatically renews a tls certificate using a give RenewFunc. +// TLSRenewer automatically renews a tls certificate using a RenewFunc. type TLSRenewer struct { sync.RWMutex RenewCertificate RenewFunc