func renew(c *cli.Context) { conf, _, client := setup(c) if len(c.GlobalStringSlice("domains")) <= 0 { logger().Fatal("Please specify at least one domain.") } domain := c.GlobalStringSlice("domains")[0] // load the cert resource from files. // We store the certificate, private key and metadata in different files // as web servers would not be able to work with a combined file. certPath := path.Join(conf.CertPath(), domain+".crt") privPath := path.Join(conf.CertPath(), domain+".key") metaPath := path.Join(conf.CertPath(), domain+".json") certBytes, err := ioutil.ReadFile(certPath) if err != nil { logger().Fatalf("Error while loading the certificate for domain %s\n\t%s", domain, err.Error()) } if c.IsSet("days") { expTime, err := acme.GetPEMCertExpiration(certBytes) if err != nil { logger().Printf("Could not get Certification expiration for domain %s", domain) } if int(expTime.Sub(time.Now()).Hours()/24.0) > c.Int("days") { return } } metaBytes, err := ioutil.ReadFile(metaPath) if err != nil { logger().Fatalf("Error while loading the meta data for domain %s\n\t%s", domain, err.Error()) } var certRes acme.CertificateResource err = json.Unmarshal(metaBytes, &certRes) if err != nil { logger().Fatalf("Error while marshalling the meta data for domain %s\n\t%s", domain, err.Error()) } if c.Bool("reuse-key") { keyBytes, err := ioutil.ReadFile(privPath) if err != nil { logger().Fatalf("Error while loading the private key for domain %s\n\t%s", domain, err.Error()) } certRes.PrivateKey = keyBytes } certRes.Certificate = certBytes newCert, err := client.RenewCertificate(certRes, true) if err != nil { logger().Fatalf("%s", err.Error()) } saveCertRes(newCert, conf) }
// Renew renews the managed certificate for name. Right now our storage // mechanism only supports one name per certificate, so this function only // accepts one domain as input. It can be easily modified to support SAN // certificates if, one day, they become desperately needed enough that our // storage mechanism is upgraded to be more complex to support SAN certs. // // Anyway, this function is safe for concurrent use. func (c *ACMEClient) Renew(name string) error { // Get access to ACME storage storage, err := StorageFor(c.config.CAUrl) if err != nil { return err } // Prepare for renewal (load PEM cert, key, and meta) certBytes, err := ioutil.ReadFile(storage.SiteCertFile(name)) if err != nil { return err } keyBytes, err := ioutil.ReadFile(storage.SiteKeyFile(name)) if err != nil { return err } metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(name)) if err != nil { return err } var certMeta acme.CertificateResource err = json.Unmarshal(metaBytes, &certMeta) certMeta.Certificate = certBytes certMeta.PrivateKey = keyBytes // Perform renewal and retry if necessary, but not too many times. var newCertMeta acme.CertificateResource var success bool for attempts := 0; attempts < 2; attempts++ { acmeMu.Lock() newCertMeta, err = c.RenewCertificate(certMeta, true) acmeMu.Unlock() if err == nil { success = true break } // If the legal terms changed and need to be agreed to again, // we can handle that. if _, ok := err.(acme.TOSError); ok { err := c.AgreeToTOS() if err != nil { return err } continue } // For any other kind of error, wait 10s and try again. wait := 10 * time.Second log.Printf("[ERROR] Renewing: %v; trying again in %s", err, wait) time.Sleep(wait) } if !success { return errors.New("too many renewal attempts; last error: " + err.Error()) } return saveCertResource(storage, newCertMeta) }
func renew(c *cli.Context) { conf, _, client := setup(c) for _, domain := range c.GlobalStringSlice("domains") { // load the cert resource from files. // We store the certificate, private key and metadata in different files // as web servers would not be able to work with a combined file. certPath := path.Join(conf.CertPath(), domain+".crt") privPath := path.Join(conf.CertPath(), domain+".key") metaPath := path.Join(conf.CertPath(), domain+".json") certBytes, err := ioutil.ReadFile(certPath) if err != nil { logger().Printf("Error while loading the certificate for domain %s\n\t%v", domain, err) return } keyBytes, err := ioutil.ReadFile(privPath) if err != nil { logger().Printf("Error while loading the private key for domain %s\n\t%v", domain, err) return } metaBytes, err := ioutil.ReadFile(metaPath) if err != nil { logger().Printf("Error while loading the meta data for domain %s\n\t%v", domain, err) return } var certRes acme.CertificateResource err = json.Unmarshal(metaBytes, &certRes) if err != nil { logger().Printf("Error while marshalling the meta data for domain %s\n\t%v", domain, err) return } certRes.PrivateKey = keyBytes certRes.Certificate = certBytes newCert, err := client.RenewCertificate(certRes, true, true) if err != nil { logger().Printf("%v", err) return } saveCertRes(newCert, conf) } }
// Renew renews the managed certificate for name. Right now our storage // mechanism only supports one name per certificate, so this function only // accepts one domain as input. It can be easily modified to support SAN // certificates if, one day, they become desperately needed enough that our // storage mechanism is upgraded to be more complex to support SAN certs. // // Anyway, this function is safe for concurrent use. func (c *ACMEClient) Renew(name string) error { // Get access to ACME storage storage, err := c.config.StorageFor(c.config.CAUrl) if err != nil { return err } // We must lock the renewal with the storage engine if lockObtained, err := storage.LockRegister(name); err != nil { return err } else if !lockObtained { log.Printf("[INFO] Certificate for %v is already being renewed elsewhere", name) return nil } defer func() { if err := storage.UnlockRegister(name); err != nil { log.Printf("[ERROR] Unable to unlock renewal lock for %v: %v", name, err) } }() // Prepare for renewal (load PEM cert, key, and meta) siteData, err := storage.LoadSite(name) if err != nil { return err } var certMeta acme.CertificateResource err = json.Unmarshal(siteData.Meta, &certMeta) certMeta.Certificate = siteData.Cert certMeta.PrivateKey = siteData.Key // Perform renewal and retry if necessary, but not too many times. var newCertMeta acme.CertificateResource var success bool for attempts := 0; attempts < 2; attempts++ { namesObtaining.Add([]string{name}) acmeMu.Lock() newCertMeta, err = c.RenewCertificate(certMeta, true) acmeMu.Unlock() namesObtaining.Remove([]string{name}) if err == nil { success = true break } // If the legal terms changed and need to be agreed to again, // we can handle that. if _, ok := err.(acme.TOSError); ok { err := c.AgreeToTOS() if err != nil { return err } continue } // For any other kind of error, wait 10s and try again. wait := 10 * time.Second log.Printf("[ERROR] Renewing: %v; trying again in %s", err, wait) time.Sleep(wait) } if !success { return errors.New("too many renewal attempts; last error: " + err.Error()) } return saveCertResource(storage, newCertMeta) }
func (c *Config) renewCertName(name string, allowPrompts bool) error { storage, err := StorageFor(c.CAUrl) if err != nil { return err } // Prepare for renewal (load PEM cert, key, and meta) certBytes, err := ioutil.ReadFile(storage.SiteCertFile(c.Hostname)) if err != nil { return err } keyBytes, err := ioutil.ReadFile(storage.SiteKeyFile(c.Hostname)) if err != nil { return err } metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(c.Hostname)) if err != nil { return err } var certMeta acme.CertificateResource err = json.Unmarshal(metaBytes, &certMeta) certMeta.Certificate = certBytes certMeta.PrivateKey = keyBytes client, err := newACMEClient(c, allowPrompts) if err != nil { return err } // Perform renewal and retry if necessary, but not too many times. var newCertMeta acme.CertificateResource var success bool for attempts := 0; attempts < 2; attempts++ { acmeMu.Lock() newCertMeta, err = client.RenewCertificate(certMeta, true) acmeMu.Unlock() if err == nil { success = true break } // If the legal terms were updated and need to be // agreed to again, we can handle that. if _, ok := err.(acme.TOSError); ok { err := client.AgreeToTOS() if err != nil { return err } continue } // For any other kind of error, wait 10s and try again. time.Sleep(10 * time.Second) } if !success { return errors.New("too many renewal attempts; last error: " + err.Error()) } return saveCertResource(storage, newCertMeta) }
// renewCertificates loops through all configured site and // looks for certificates to renew. Nothing is mutated // through this function; all changes happen directly on disk. // It returns the number of certificates renewed and any errors // that occurred. It only performs a renewal if necessary. // If useCustomPort is true, a custom port will be used, and // whatever is listening at 443 better proxy ACME requests to it. // Otherwise, the acme package will create its own listener on 443. func renewCertificates(configs []server.Config, useCustomPort bool) (int, []error) { log.Printf("[INFO] Checking certificates for %d hosts", len(configs)) var errs []error var n int for _, cfg := range configs { // Host must be TLS-enabled and have existing assets managed by LE if !cfg.TLS.Enabled || !existingCertAndKey(cfg.Host) { continue } // Read the certificate and get the NotAfter time. certBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host)) if err != nil { errs = append(errs, err) continue // still have to check other certificates } expTime, err := acme.GetPEMCertExpiration(certBytes) if err != nil { errs = append(errs, err) continue } // The time returned from the certificate is always in UTC. // So calculate the time left with local time as UTC. // Directly convert it to days for the following checks. daysLeft := int(expTime.Sub(time.Now().UTC()).Hours() / 24) // Renew with two weeks or less remaining. if daysLeft <= 14 { log.Printf("[INFO] Certificate for %s has %d days remaining; attempting renewal", cfg.Host, daysLeft) var client *acme.Client if useCustomPort { client, err = newClientPort("", alternatePort) // email not used for renewal } else { client, err = newClient("") } if err != nil { errs = append(errs, err) continue } // Read and set up cert meta, required for renewal metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(cfg.Host)) if err != nil { errs = append(errs, err) continue } privBytes, err := ioutil.ReadFile(storage.SiteKeyFile(cfg.Host)) if err != nil { errs = append(errs, err) continue } var certMeta acme.CertificateResource err = json.Unmarshal(metaBytes, &certMeta) certMeta.Certificate = certBytes certMeta.PrivateKey = privBytes // Renew certificate Renew: newCertMeta, err := client.RenewCertificate(certMeta, true, true) if err != nil { if _, ok := err.(acme.TOSError); ok { err := client.AgreeToTOS() if err != nil { errs = append(errs, err) } goto Renew } time.Sleep(10 * time.Second) newCertMeta, err = client.RenewCertificate(certMeta, true, true) if err != nil { errs = append(errs, err) continue } } saveCertsAndKeys([]acme.CertificateResource{newCertMeta}) n++ } else if daysLeft <= 30 { // Warn on 30 days remaining. TODO: Just do this once... log.Printf("[WARNING] Certificate for %s has %d days remaining; will automatically renew when 14 days remain\n", cfg.Host, daysLeft) } } return n, errs }
// Renew renews the managed certificate for name. This function is // safe for concurrent use. // // Callers who have access to a Config value should use the RenewCert // method on that instead of this lower-level method. func (c *ACMEClient) Renew(name string) error { // Get access to ACME storage storage, err := c.config.StorageFor(c.config.CAUrl) if err != nil { return err } waiter, err := storage.TryLock(name) if err != nil { return err } if waiter != nil { log.Printf("[INFO] Certificate for %s is already being renewed elsewhere and stored; waiting", name) waiter.Wait() return nil // we assume the process with the lock succeeded, rather than hammering this execution path again } defer func() { if err := storage.Unlock(name); err != nil { log.Printf("[ERROR] Unable to unlock renew call for %s: %v", name, err) } }() // Prepare for renewal (load PEM cert, key, and meta) siteData, err := storage.LoadSite(name) if err != nil { return err } var certMeta acme.CertificateResource err = json.Unmarshal(siteData.Meta, &certMeta) certMeta.Certificate = siteData.Cert certMeta.PrivateKey = siteData.Key // Perform renewal and retry if necessary, but not too many times. var newCertMeta acme.CertificateResource var success bool for attempts := 0; attempts < 2; attempts++ { namesObtaining.Add([]string{name}) acmeMu.Lock() newCertMeta, err = c.acmeClient.RenewCertificate(certMeta, true) acmeMu.Unlock() namesObtaining.Remove([]string{name}) if err == nil { success = true break } // If the legal terms were updated and need to be // agreed to again, we can handle that. if _, ok := err.(acme.TOSError); ok { err := c.acmeClient.AgreeToTOS() if err != nil { return err } continue } // For any other kind of error, wait 10s and try again. wait := 10 * time.Second log.Printf("[ERROR] Renewing: %v; trying again in %s", err, wait) time.Sleep(wait) } if !success { return errors.New("too many renewal attempts; last error: " + err.Error()) } return saveCertResource(storage, newCertMeta) }