Skip to content

Commit 3cae55b

Browse files
committed
caddytls: Regularly reload static certificates
1 parent ca53b82 commit 3cae55b

File tree

3 files changed

+36
-79
lines changed

3 files changed

+36
-79
lines changed

modules/caddyhttp/reverseproxy/httptransport_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ func TestHTTPTransport_DialTLSContext_ProxyProtocol(t *testing.T) {
129129
defer cancel()
130130

131131
tests := []struct {
132-
name string
133-
tls *TLSConfig
134-
proxyProtocol string
132+
name string
133+
tls *TLSConfig
134+
proxyProtocol string
135135
serverNameHasPlaceholder bool
136-
expectDialTLSContext bool
136+
expectDialTLSContext bool
137137
}{
138138
{
139139
name: "no TLS, no proxy protocol",
@@ -194,4 +194,3 @@ func TestHTTPTransport_DialTLSContext_ProxyProtocol(t *testing.T) {
194194
})
195195
}
196196
}
197-

modules/caddytls/storageloader.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,16 @@ func (sl *StorageLoader) Provision(ctx caddy.Context) error {
7272
return nil
7373
}
7474

75-
// LoadCertificates returns the certificates to be loaded by sl.
76-
func (sl StorageLoader) LoadCertificates() ([]Certificate, error) {
75+
func (sl StorageLoader) Initialize(updateCertificates func(add []Certificate, remove []string) error) error {
7776
certs := make([]Certificate, 0, len(sl.Pairs))
7877
for _, pair := range sl.Pairs {
7978
certData, err := sl.storage.Load(sl.ctx, pair.Certificate)
8079
if err != nil {
81-
return nil, err
80+
return err
8281
}
8382
keyData, err := sl.storage.Load(sl.ctx, pair.Key)
8483
if err != nil {
85-
return nil, err
84+
return err
8685
}
8786

8887
var cert tls.Certificate
@@ -94,21 +93,21 @@ func (sl StorageLoader) LoadCertificates() ([]Certificate, error) {
9493
// if the start of the key file looks like an encrypted private key,
9594
// reject it with a helpful error message
9695
if strings.Contains(string(keyData[:40]), "ENCRYPTED") {
97-
return nil, fmt.Errorf("encrypted private keys are not supported; please decrypt the key first")
96+
return fmt.Errorf("encrypted private keys are not supported; please decrypt the key first")
9897
}
9998

10099
cert, err = tls.X509KeyPair(certData, keyData)
101100

102101
default:
103-
return nil, fmt.Errorf("unrecognized certificate/key encoding format: %s", pair.Format)
102+
return fmt.Errorf("unrecognized certificate/key encoding format: %s", pair.Format)
104103
}
105104
if err != nil {
106-
return nil, err
105+
return err
107106
}
108107

109108
certs = append(certs, Certificate{Certificate: cert, Tags: pair.Tags})
110109
}
111-
return certs, nil
110+
return updateCertificates(certs, []string{})
112111
}
113112

114113
// Interface guard

modules/caddytls/tls.go

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ type TLS struct {
131131
// EXPERIMENTAL: Subject to change.
132132
Resolvers []string `json:"resolvers,omitempty"`
133133

134-
dns any // technically, it should be any/all of the libdns interfaces (RecordSetter, RecordAppender, etc.)
135-
certificateLoaders []CertificateLoader
136-
automateNames map[string]struct{}
137-
ctx caddy.Context
138-
storageCleanTicker *time.Ticker
139-
storageCleanStop chan struct{}
140-
logger *zap.Logger
141-
events *caddyevents.App
134+
dns any // technically, it should be any/all of the libdns interfaces (RecordSetter, RecordAppender, etc.)
135+
certificateLoaders []CertificateLoader
136+
automateNames map[string]struct{}
137+
ctx caddy.Context
138+
storageCleanTicker *time.Ticker
139+
storageCleanStop chan struct{}
140+
logger *zap.Logger
141+
events *caddyevents.App
142142
magic *certmagic.Config
143143
unmanagedCertsTicker *time.Ticker
144144

@@ -251,7 +251,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
251251
// certificates have been manually loaded, and also so that
252252
// commands like validate can be a better test
253253
certCacheMu.RLock()
254-
t.magic = certmagic.New(certCache, certmagic.Config{
254+
magic := certmagic.New(certCache, certmagic.Config{
255255
Storage: ctx.Storage(),
256256
Logger: t.logger,
257257
OnEvent: t.onEvent,
@@ -262,13 +262,13 @@ func (t *TLS) Provision(ctx caddy.Context) error {
262262
})
263263
certCacheMu.RUnlock()
264264

265-
unmanaged, err := t.loadUnmanagedCertificates(ctx)
266-
if err != nil {
267-
return err
268-
}
269-
270-
if unmanaged > 0 {
271-
t.regularlyReloadUnmanagedCertificates()
265+
for _, loader := range t.certificateLoaders {
266+
err := loader.Initialize(func(add []Certificate, remove []string) error {
267+
return t.updateCertificates(ctx, magic, add, remove)
268+
})
269+
if err != nil {
270+
return fmt.Errorf("loading certificates: %v", err)
271+
}
272272
}
273273

274274
// on-demand permission module
@@ -811,58 +811,17 @@ func (t *TLS) HasCertificateForSubject(subject string) bool {
811811
return false
812812
}
813813

814-
func (t *TLS) loadUnmanagedCertificates(ctx caddy.Context) (int, error) {
815-
cached := 0
816-
for _, loader := range t.certificateLoaders {
817-
certs, err := loader.LoadCertificates()
818-
if err != nil {
819-
return 0, fmt.Errorf("loading certificates: %v", err)
820-
}
821-
for _, cert := range certs {
822-
hash, err := t.magic.CacheUnmanagedTLSCertificate(ctx, cert.Certificate, cert.Tags)
823-
if err != nil {
824-
return 0, fmt.Errorf("caching unmanaged certificate: %v", err)
825-
}
826-
t.loaded[hash] = ""
827-
}
828-
}
829-
return cached, nil
830-
}
831-
832-
func (t *TLS) regularlyReloadUnmanagedCertificates() {
833-
t.unmanagedCertsTicker = time.NewTicker(2 * time.Hour)
834-
go func() {
835-
defer func() {
836-
if err := recover(); err != nil {
837-
log.Printf("[PANIC] unmanaged certificates reloader: %v\n%s", err, debug.Stack())
838-
}
839-
}()
840-
t.reloadUnmanagedCertificates()
841-
for {
842-
select {
843-
case <-t.storageCleanStop:
844-
return
845-
case <-t.storageCleanTicker.C:
846-
t.cleanStorageUnits()
847-
}
848-
}
849-
}()
850-
}
851-
852-
func (t *TLS) reloadUnmanagedCertificates() error {
853-
for _, loader := range t.certificateLoaders {
854-
certs, err := loader.LoadCertificates()
814+
func (t *TLS) updateCertificates(ctx caddy.Context, magic *certmagic.Config, add []Certificate, remove []string) error {
815+
for _, cert := range add {
816+
hash, err := magic.CacheUnmanagedTLSCertificate(ctx, cert.Certificate, cert.Tags)
855817
if err != nil {
856-
return fmt.Errorf("loading certificates: %v", err)
857-
}
858-
for _, cert := range certs {
859-
hash, err := t.magic.CacheUnmanagedTLSCertificate(t.ctx, cert.Certificate, cert.Tags)
860-
if err != nil {
861-
return fmt.Errorf("caching unmanaged certificate: %v", err)
862-
}
863-
t.loaded[hash] = ""
818+
return fmt.Errorf("caching unmanaged certificate: %v", err)
864819
}
820+
t.loaded[hash] = ""
865821
}
822+
certCacheMu.Lock()
823+
certCache.Remove(remove)
824+
certCacheMu.Unlock()
866825
return nil
867826
}
868827

@@ -971,7 +930,7 @@ func (t *TLS) onEvent(ctx context.Context, eventName string, data map[string]any
971930
// CertificateLoader is a type that can load certificates.
972931
// Certificates can optionally be associated with tags.
973932
type CertificateLoader interface {
974-
LoadCertificates() ([]Certificate, error)
933+
Initialize(updateCertificates func(add []Certificate, remove []string) error) error
975934
}
976935

977936
// Certificate is a TLS certificate, optionally

0 commit comments

Comments
 (0)