Skip to content

Commit

Permalink
feat: add support for more dynamic renewal
Browse files Browse the repository at this point in the history
  • Loading branch information
soerenschneider committed Jul 9, 2023
1 parent 34b9cb8 commit 995437f
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 22 deletions.
32 changes: 10 additions & 22 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ import (
"github.com/soerenschneider/acmevault/internal/metrics"
"github.com/soerenschneider/acmevault/internal/server/acme"
"github.com/soerenschneider/acmevault/pkg/certstorage"
"time"
)

const (
// MinCertLifetime defines a certs minimum validity. If a certificate's lifetime is less than this threshold, it's
// being renewed.
MinCertLifetime = time.Duration(30*24) * time.Hour
)

type AcmeVaultServer struct {
Expand Down Expand Up @@ -71,26 +64,21 @@ func (c *AcmeVaultServer) obtainAndHandleCert(domain config.AcmeServerDomains) e
}

log.Info().Msgf("Successfully read cert data from storage for domain %s", domain)
expiry, err := read.GetExpiryTimestamp()
renewCert, err := read.NeedsRenewal()
if err != nil {
log.Info().Msgf("Could not determine cert lifetime for %s, probably the cert is broken", domain)
} else {
timeLeft := expiry.Sub(time.Now().UTC())
if timeLeft > MinCertLifetime {
metrics.CertServerExpiryTimestamp.WithLabelValues(domain.Domain).Set(float64(expiry.Unix()))
log.Info().Msgf("Not renewing cert for domain %s, still valid for %v", domain, timeLeft)
return nil
}
log.Info().Msgf("Cert for domain %s is only valid for %v, renewing...", domain, timeLeft)
}

renewed, err := c.acmeClient.RenewCert(read)
metrics.CertificatesRenewals.Inc()
if err != nil {
metrics.CertificatesRenewErrors.Inc()
return fmt.Errorf("renewing cert failed for domain %s: %v", domain, err)
if renewCert {
renewed, err := c.acmeClient.RenewCert(read)
metrics.CertificatesRenewals.Inc()
if err != nil {
metrics.CertificatesRenewErrors.Inc()
return fmt.Errorf("renewing cert failed for domain %s: %v", domain, err)
}
return handleReceivedCert(renewed, c.certStorage)
}
return handleReceivedCert(renewed, c.certStorage)
return nil
}

func handleReceivedCert(cert *certstorage.AcmeCertificate, storage certstorage.CertStorage) error {
Expand Down
29 changes: 29 additions & 0 deletions pkg/certstorage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ import (
"github.com/go-acme/lego/v4/registration"
"github.com/rs/zerolog/log"
"github.com/soerenschneider/acmevault/internal/metrics"
"math/rand"
"time"
)

const (
// MinCertLifetime defines a certs minimum validity. If a certificate's lifetime is less than this threshold, it's
// being renewed.
MinCertLifetime = time.Duration(24*30) * time.Hour
Skew = time.Duration(24*60) * time.Hour
)

type CertStorage interface {
// Authenticate authenticates against the storage subsystem and returns an error about the success of the operation.
Authenticate() error
Expand Down Expand Up @@ -55,6 +63,27 @@ type AcmeCertificate struct {
CSR []byte `json:"-"`
}

func (cert *AcmeCertificate) NeedsRenewal() (bool, error) {
expiry, err := cert.GetExpiryTimestamp()
if err != nil {
return false, fmt.Errorf("could not determine cert expiry for domain '%s': %v", cert.Domain, err)
}

metrics.CertServerExpiryTimestamp.WithLabelValues(cert.Domain).Set(float64(expiry.Unix()))
timeLeft := expiry.Sub(time.Now().UTC())
log.Info().Msgf("Not renewing cert for domain %s, still valid for %v", cert.Domain, timeLeft)

if timeLeft > MinCertLifetime && timeLeft <= Skew {
rand.Seed(time.Now().UnixNano())
if rand.Intn(100) >= 97 {
log.Info().Msgf("Earlier renewal of cert for domain %s to distribute cert expiries (%v)", cert.Domain, timeLeft)
return true, nil
}
}

return timeLeft <= MinCertLifetime, nil
}

func (cert *AcmeCertificate) GetExpiryTimestamp() (time.Time, error) {
block, _ := pem.Decode(cert.Certificate)
if block == nil {
Expand Down

0 comments on commit 995437f

Please sign in to comment.