diff --git a/cmd/main.go b/cmd/main.go index 7eb3fac..b026885 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -109,6 +109,9 @@ func Serve(ctx *cli.Context) error { certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, keyDatabase) + // TODO: make it graceful + go certificates.MaintainCertDB(mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + if enableHTTPServer { go func() { err := httpServer.ListenAndServe("[::]:80") diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index d2f7c76..e484d78 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -444,75 +444,65 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } } - - go (func() { - for { - err := keyDatabase.Sync() - if err != nil { - log.Printf("[ERROR] Syncing key database failed: %s", err) - } - time.Sleep(5 * time.Minute) - // TODO: graceful exit - } - })() - go (func() { - for { - // clean up expired certs - now := time.Now() - expiredCertCount := 0 - keyDatabaseIterator := keyDatabase.Items() - key, resBytes, err := keyDatabaseIterator.Next() - for err == nil { - if !bytes.Equal(key, mainDomainSuffix) { - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - res := &certificate.Resource{} - err = resDec.Decode(res) - if err != nil { - panic(err) - } - - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := keyDatabase.Delete(key) - if err != nil { - log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) - } else { - expiredCertCount++ - } - } - } - key, resBytes, err = keyDatabaseIterator.Next() - } - log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) - - // compact the database - result, err := keyDatabase.Compact() - if err != nil { - log.Printf("[ERROR] Compacting key database failed: %s", err) - } else { - log.Printf("[INFO] Compacted key database (%+v)", result) - } - - // update main cert - res := &certificate.Resource{} - if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") - } else { - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - - // renew main certificate 30 days before it expires - if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { - go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) - if err != nil { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) - } - })() - } - } - - time.Sleep(12 * time.Hour) - } - })() +} + +func MaintainCertDB(mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) { + for { + // clean up expired certs + now := time.Now() + expiredCertCount := 0 + keyDatabaseIterator := keyDatabase.Items() + key, resBytes, err := keyDatabaseIterator.Next() + for err == nil { + if !bytes.Equal(key, mainDomainSuffix) { + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + res := &certificate.Resource{} + err = resDec.Decode(res) + if err != nil { + panic(err) + } + + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) + if err != nil || !tlsCertificates[0].NotAfter.After(now) { + err := keyDatabase.Delete(key) + if err != nil { + log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) + } else { + expiredCertCount++ + } + } + } + key, resBytes, err = keyDatabaseIterator.Next() + } + log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) + + // compact the database + result, err := keyDatabase.Compact() + if err != nil { + log.Printf("[ERROR] Compacting key database failed: %s", err) + } else { + log.Printf("[INFO] Compacted key database (%+v)", result) + } + + // update main cert + res := &certificate.Resource{} + if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") + } else { + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) + + // renew main certificate 30 days before it expires + if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { + go (func() { + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + if err != nil { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) + } + })() + } + } + + time.Sleep(12 * time.Hour) + } }