diff options
author | Matthew Holt <[email protected]> | 2024-04-26 18:51:56 -0600 |
---|---|---|
committer | Matthew Holt <[email protected]> | 2024-04-26 18:51:56 -0600 |
commit | b4a4ddd3231fec193a3331c7ff936cc124c6c96c (patch) | |
tree | 179dc31c3afaa3d2371d519aa4077d3dbf222825 | |
parent | f8cafb901bbcd2c11ebff6c93804c675941d4cc4 (diff) | |
download | caddy-b4a4ddd3231fec193a3331c7ff936cc124c6c96c.tar.gz caddy-b4a4ddd3231fec193a3331c7ff936cc124c6c96c.zip |
Actually make it worktls-cert-cache-internal-reload
-rw-r--r-- | modules/caddytls/tls.go | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go index 095571ae6..14965533e 100644 --- a/modules/caddytls/tls.go +++ b/modules/caddytls/tls.go @@ -22,7 +22,6 @@ import ( "log" "net/http" "runtime/debug" - "strings" "sync" "time" @@ -92,7 +91,8 @@ type TLS struct { // set of subjects with managed certificates, // and hashes of manually-loaded certificates - managing, loaded map[string]struct{} + // (managing's value is an optional issuer key, for distinction) + managing, loaded map[string]string } // CaddyModule returns the Caddy module information. @@ -113,7 +113,7 @@ func (t *TLS) Provision(ctx caddy.Context) error { t.ctx = ctx t.logger = ctx.Logger() repl := caddy.NewReplacer() - t.managing, t.loaded = make(map[string]struct{}), make(map[string]struct{}) + t.managing, t.loaded = make(map[string]string), make(map[string]string) // set up a new certificate cache; this (re)loads all certificates cacheOpts := certmagic.CacheOptions{ @@ -267,7 +267,7 @@ func (t *TLS) Provision(ctx caddy.Context) error { if err != nil { return fmt.Errorf("caching unmanaged certificate: %v", err) } - t.loaded[hash] = struct{}{} + t.loaded[hash] = "" } } @@ -361,14 +361,24 @@ func (t *TLS) Cleanup() error { // new app instance (which just started), and remove them from the cache var noLongerManaged []certmagic.SubjectIssuer var reManage, noLongerLoaded []string - for subj := range t.managing { - if _, ok := nextTLSApp.managing[subj]; !ok { - name, issuerKey, _ := strings.Cut(subj, "~") - noLongerManaged = append(noLongerManaged, certmagic.SubjectIssuer{Subject: name, IssuerKey: issuerKey}) - // if the new TLS app is managing a cert for the same subject we are evicting, - // make sure it obtains or loads the cert using that config afterwards - if _, ok := nextTLSApp.managing[name]; ok { - reManage = append(reManage, name) + for subj, currentIssuerKey := range t.managing { + // It's a bit nuanced: managed certs can sometimes be different enough that we have to + // swap them out for a different one, even if they are for the same subject/domain. + // We consider "private" certs (internal CA/locally-trusted/etc) to be significantly + // distinct from "public" certs (production CAs/globally-trusted/etc) because of the + // implications when it comes to actual deployments: switching between an internal CA + // and a production CA, for example, is quite significant. Switching from one public CA + // to another, however, is not, and for our purposes we consider those to be the same. + // Anyway, if the next TLS app does not manage a cert for this name at all, definitely + // remove it from the cache. But if it does, and it's not the same kind of issuer/CA + // as we have, also remove it, so that it can swap it out for the right one. + if nextIssuerKey, ok := nextTLSApp.managing[subj]; !ok || nextIssuerKey != currentIssuerKey { + // next app is not managing a cert for this domain at all or is using a different issuer, so remove it + noLongerManaged = append(noLongerManaged, certmagic.SubjectIssuer{Subject: subj, IssuerKey: currentIssuerKey}) + + // then, if the next app is managing a cert for this name, but with a different issuer, re-manage it + if ok && nextIssuerKey != currentIssuerKey { + reManage = append(reManage, subj) } } } @@ -378,6 +388,7 @@ func (t *TLS) Cleanup() error { } } + // remove the certs certCacheMu.RLock() certCache.RemoveManaged(noLongerManaged) certCache.Remove(noLongerLoaded) @@ -427,13 +438,16 @@ func (t *TLS) Manage(names []string) error { // a special case: if you have an initial config that manages example.com // using internal CA, then after testing it you switch to a production CA, // you wouldn't want to keep using the same self-signed cert, obviously; - // so we differentiate these in the list + // so we differentiate these by associating the subject with its issuer key; + // we do this because CertMagic has no notion of "InternalIssuer" like we + // do, so we have to do this logic ourselves + var issuerKey string if len(ap.Issuers) == 1 { - if _, ok := ap.Issuers[0].(*InternalIssuer); ok { - name += "~" + ap.Issuers[0].IssuerKey() + if intIss, ok := ap.Issuers[0].(*InternalIssuer); ok && intIss != nil { + issuerKey = intIss.IssuerKey() } } - t.managing[name] = struct{}{} + t.managing[name] = issuerKey } } |