Limit TLS cache warming to live domains
After the certificates split, WarmCache loaded every ACTIVE certificate. Org deletes cascade-remove custom_domains but leave certificates behind, so orphans could regain a usable SNI cache entry on rebuild. Warm and serve only certs still referenced by a domain, and purge unreferenced cache rows. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -59,8 +59,13 @@ func (w *CacheStore) WarmCache(ctx context.Context) error {
|
||||
err := w.pg.WithConn(
|
||||
ctx,
|
||||
func(ctx context.Context, conn pg.Querier) error {
|
||||
var caches coredata.CachedCertificates
|
||||
if err := caches.DeleteUnreferenced(ctx, conn); err != nil {
|
||||
return fmt.Errorf("cannot delete unreferenced certificate cache: %w", err)
|
||||
}
|
||||
|
||||
certificates := coredata.Certificates{}
|
||||
if err := certificates.LoadActive(ctx, conn, coredata.NewNoScope()); err != nil {
|
||||
if err := certificates.LoadActiveReferenced(ctx, conn, coredata.NewNoScope()); err != nil {
|
||||
return fmt.Errorf("cannot load active certificates: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ package certmanager
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -89,6 +90,10 @@ func (s *Selector) loadFromDatabase(domain string) (*tls.Certificate, error) {
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(ctx context.Context, conn pg.Querier) error {
|
||||
if err := requireRoutableDomain(ctx, conn, domain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cache coredata.CachedCertificate
|
||||
if err := cache.LoadByDomain(ctx, conn, domain); err != nil {
|
||||
if err := s.rebuildCacheEntry(ctx, conn, domain); err != nil {
|
||||
@@ -123,6 +128,10 @@ func (s *Selector) loadFromDatabase(domain string) (*tls.Certificate, error) {
|
||||
}
|
||||
|
||||
func (s *Selector) rebuildCacheEntry(ctx context.Context, conn pg.Querier, domain string) error {
|
||||
if err := requireRoutableDomain(ctx, conn, domain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var certificate coredata.Certificate
|
||||
if err := certificate.LoadByHostname(ctx, conn, coredata.NewNoScope(), domain); err != nil {
|
||||
return fmt.Errorf("cannot load certificate: %w", err)
|
||||
@@ -167,3 +176,22 @@ func (s *Selector) rebuildCacheEntry(ctx context.Context, conn pg.Querier, domai
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// requireRoutableDomain ensures the SNI hostname still maps to a custom domain
|
||||
// row. Orphaned certificates left after domain deletion must not be served.
|
||||
func requireRoutableDomain(ctx context.Context, conn pg.Querier, domain string) error {
|
||||
var customDomain coredata.CustomDomain
|
||||
if err := customDomain.LoadByDomain(ctx, conn, coredata.NewNoScope(), domain); err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load custom domain: %w", err)
|
||||
}
|
||||
|
||||
if customDomain.CertificateID == nil {
|
||||
return coredata.ErrResourceNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user