at startup, request missing acme tls certificates more quickly/silently

This commit is contained in:
Mechiel Lukkien 2023-12-22 13:41:00 +01:00
parent dbd6773f6b
commit 4701857d7f
No known key found for this signature in database
2 changed files with 42 additions and 0 deletions

View file

@ -200,6 +200,40 @@ func Load(name, acmeDir, contactEmail, directoryURL string, eabKeyID string, eab
return a, nil
}
// CertAvailable checks whether a non-expired ECDSA certificate is available in the
// cache for host. No other checks than expiration are done.
func (m *Manager) CertAvailable(ctx context.Context, log mlog.Log, host dns.Domain) (bool, error) {
ck := host.ASCII // Would be "+rsa" for rsa keys.
data, err := m.Manager.Cache.Get(ctx, ck)
if err != nil && errors.Is(err, autocert.ErrCacheMiss) {
return false, nil
} else if err != nil {
return false, fmt.Errorf("attempt to get certificate from cache: %v", err)
}
// The cached keycert is of the form: private key, leaf certificate, intermediate certificates...
privb, rem := pem.Decode(data)
if privb == nil {
return false, fmt.Errorf("missing private key in cached keycert file")
}
pubb, _ := pem.Decode(rem)
if pubb == nil {
return false, fmt.Errorf("missing certificate in cached keycert file")
} else if pubb.Type != "CERTIFICATE" {
return false, fmt.Errorf("second pem block is %q, expected CERTIFICATE", pubb.Type)
}
cert, err := x509.ParseCertificate(pubb.Bytes)
if err != nil {
return false, fmt.Errorf("parsing certificate from cached keycert file: %v", err)
}
// We assume the certificate has a matching hostname, and is properly CA-signed. We
// only check the expiration time.
if time.Until(cert.NotBefore) > 0 || time.Since(cert.NotAfter) > 0 {
return false, nil
}
return true, nil
}
// SetAllowedHostnames sets a new list of allowed hostnames for automatic TLS.
// After setting the host names, a goroutine is start to check that new host names
// are fully served by publicIPs (only if non-empty and there is no unspecified

View file

@ -801,6 +801,14 @@ func Serve() {
i := 0
for m, hosts := range ensureManagerHosts {
for host := range hosts {
// Check if certificate is already available. If so, we don't print as much after a
// restart, and finish more quickly if only a few certificates are missing/old.
if avail, err := m.CertAvailable(mox.Shutdown, pkglog, host); err != nil {
pkglog.Errorx("checking acme certificate availability", err, slog.Any("host", host))
} else if avail {
continue
}
if i >= 10 {
// Just in case someone adds quite some domains to their config. We don't want to
// hit any ACME rate limits.