Commit f24ecee6 authored by Matthew Holt's avatar Matthew Holt

letsencrypt: Basic renewal failover and better error handling

parent c5635f21
...@@ -17,8 +17,10 @@ import ( ...@@ -17,8 +17,10 @@ import (
func keepCertificatesRenewed(configs []server.Config) { func keepCertificatesRenewed(configs []server.Config) {
ticker := time.Tick(renewInterval) ticker := time.Tick(renewInterval)
for range ticker { for range ticker {
if err := processCertificateRenewal(configs); err != nil { if errs := processCertificateRenewal(configs); len(errs) > 0 {
log.Printf("[ERROR] cert renewal: %v", err) for _, err := range errs {
log.Printf("[ERROR] cert renewal: %v\n", err)
}
} }
} }
} }
...@@ -26,11 +28,12 @@ func keepCertificatesRenewed(configs []server.Config) { ...@@ -26,11 +28,12 @@ func keepCertificatesRenewed(configs []server.Config) {
// checkCertificateRenewal loops through all configured // checkCertificateRenewal loops through all configured
// sites and looks for certificates to renew. Nothing is mutated // sites and looks for certificates to renew. Nothing is mutated
// through this function. The changes happen directly on disk. // through this function. The changes happen directly on disk.
func processCertificateRenewal(configs []server.Config) error { func processCertificateRenewal(configs []server.Config) []error {
var errs []error
log.Print("[INFO] Processing certificate renewals...") log.Print("[INFO] Processing certificate renewals...")
for _, cfg := range configs { for _, cfg := range configs {
// Check if this entry is TLS enabled and managed by LE // Host must be TLS-enabled and have assets managed by LE
if !cfg.TLS.Enabled || !existingCertAndKey(cfg.Host) { if !cfg.TLS.Enabled || !existingCertAndKey(cfg.Host) {
continue continue
} }
...@@ -38,11 +41,13 @@ func processCertificateRenewal(configs []server.Config) error { ...@@ -38,11 +41,13 @@ func processCertificateRenewal(configs []server.Config) error {
// Read the certificate and get the NotAfter time. // Read the certificate and get the NotAfter time.
certBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host)) certBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host))
if err != nil { if err != nil {
return err errs = append(errs, err)
continue // still have to check other certificates
} }
expTime, err := acme.GetPEMCertExpiration(certBytes) expTime, err := acme.GetPEMCertExpiration(certBytes)
if err != nil { if err != nil {
return err errs = append(errs, err)
continue
} }
// The time returned from the certificate is always in UTC. // The time returned from the certificate is always in UTC.
...@@ -50,23 +55,26 @@ func processCertificateRenewal(configs []server.Config) error { ...@@ -50,23 +55,26 @@ func processCertificateRenewal(configs []server.Config) error {
// Directly convert it to days for the following checks. // Directly convert it to days for the following checks.
daysLeft := int(expTime.Sub(time.Now().UTC()).Hours() / 24) daysLeft := int(expTime.Sub(time.Now().UTC()).Hours() / 24)
// Renew on two or less days remaining. // Renew with a week or less remaining.
if daysLeft <= 2 { if daysLeft <= 7 {
log.Printf("[WARN] There are %d days left on the certificate of %s. Trying to renew now.", daysLeft, cfg.Host) log.Printf("[INFO] There are %d days left on the certificate of %s. Trying to renew now.", daysLeft, cfg.Host)
client, err := newClient(getEmail(cfg)) client, err := newClient(getEmail(cfg))
if err != nil { if err != nil {
return err errs = append(errs, err)
continue
} }
// Read metadata // Read metadata
metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(cfg.Host)) metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(cfg.Host))
if err != nil { if err != nil {
return err errs = append(errs, err)
continue
} }
privBytes, err := ioutil.ReadFile(storage.SiteKeyFile(cfg.Host)) privBytes, err := ioutil.ReadFile(storage.SiteKeyFile(cfg.Host))
if err != nil { if err != nil {
return err errs = append(errs, err)
continue
} }
var certMeta acme.CertificateResource var certMeta acme.CertificateResource
...@@ -78,17 +86,20 @@ func processCertificateRenewal(configs []server.Config) error { ...@@ -78,17 +86,20 @@ func processCertificateRenewal(configs []server.Config) error {
// TODO: revokeOld should be an option in the caddyfile // TODO: revokeOld should be an option in the caddyfile
newCertMeta, err := client.RenewCertificate(certMeta, true) newCertMeta, err := client.RenewCertificate(certMeta, true)
if err != nil { if err != nil {
return err time.Sleep(10 * time.Second)
newCertMeta, err = client.RenewCertificate(certMeta, true)
if err != nil {
errs = append(errs, err)
continue
} }
saveCertsAndKeys([]acme.CertificateResource{newCertMeta})
} }
saveCertsAndKeys([]acme.CertificateResource{newCertMeta})
} else if daysLeft <= 14 {
// Warn on 14 days remaining // Warn on 14 days remaining
if daysLeft <= 14 { log.Printf("[WARN] There are %d days left on the certificate for %s. Will renew when 7 days remain.\n", daysLeft, cfg.Host)
log.Printf("[WARN] There are %d days left on the certificate of %s. Will renew on two days left.\n", daysLeft, cfg.Host)
} }
} }
return nil return errs
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment