@@ -91,7 +91,8 @@ type TLS struct {
91
91
92
92
// set of subjects with managed certificates,
93
93
// and hashes of manually-loaded certificates
94
- managing , loaded map [string ]struct {}
94
+ // (managing's value is an optional issuer key, for distinction)
95
+ managing , loaded map [string ]string
95
96
}
96
97
97
98
// CaddyModule returns the Caddy module information.
@@ -112,7 +113,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
112
113
t .ctx = ctx
113
114
t .logger = ctx .Logger ()
114
115
repl := caddy .NewReplacer ()
115
- t .managing , t .loaded = make (map [string ]struct {} ), make (map [string ]struct {} )
116
+ t .managing , t .loaded = make (map [string ]string ), make (map [string ]string )
116
117
117
118
// set up a new certificate cache; this (re)loads all certificates
118
119
cacheOpts := certmagic.CacheOptions {
@@ -266,7 +267,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
266
267
if err != nil {
267
268
return fmt .Errorf ("caching unmanaged certificate: %v" , err )
268
269
}
269
- t .loaded [hash ] = struct {}{}
270
+ t .loaded [hash ] = ""
270
271
}
271
272
}
272
273
@@ -358,10 +359,27 @@ func (t *TLS) Cleanup() error {
358
359
// compute which certificates were managed or loaded into the cert cache by this
359
360
// app instance (which is being stopped) that are not managed or loaded by the
360
361
// new app instance (which just started), and remove them from the cache
361
- var noLongerManaged , noLongerLoaded []string
362
- for subj := range t .managing {
363
- if _ , ok := nextTLSApp .managing [subj ]; ! ok {
364
- noLongerManaged = append (noLongerManaged , subj )
362
+ var noLongerManaged []certmagic.SubjectIssuer
363
+ var reManage , noLongerLoaded []string
364
+ for subj , currentIssuerKey := range t .managing {
365
+ // It's a bit nuanced: managed certs can sometimes be different enough that we have to
366
+ // swap them out for a different one, even if they are for the same subject/domain.
367
+ // We consider "private" certs (internal CA/locally-trusted/etc) to be significantly
368
+ // distinct from "public" certs (production CAs/globally-trusted/etc) because of the
369
+ // implications when it comes to actual deployments: switching between an internal CA
370
+ // and a production CA, for example, is quite significant. Switching from one public CA
371
+ // to another, however, is not, and for our purposes we consider those to be the same.
372
+ // Anyway, if the next TLS app does not manage a cert for this name at all, definitely
373
+ // remove it from the cache. But if it does, and it's not the same kind of issuer/CA
374
+ // as we have, also remove it, so that it can swap it out for the right one.
375
+ if nextIssuerKey , ok := nextTLSApp .managing [subj ]; ! ok || nextIssuerKey != currentIssuerKey {
376
+ // next app is not managing a cert for this domain at all or is using a different issuer, so remove it
377
+ noLongerManaged = append (noLongerManaged , certmagic.SubjectIssuer {Subject : subj , IssuerKey : currentIssuerKey })
378
+
379
+ // then, if the next app is managing a cert for this name, but with a different issuer, re-manage it
380
+ if ok && nextIssuerKey != currentIssuerKey {
381
+ reManage = append (reManage , subj )
382
+ }
365
383
}
366
384
}
367
385
for hash := range t .loaded {
@@ -370,10 +388,19 @@ func (t *TLS) Cleanup() error {
370
388
}
371
389
}
372
390
391
+ // remove the certs
373
392
certCacheMu .RLock ()
374
393
certCache .RemoveManaged (noLongerManaged )
375
394
certCache .Remove (noLongerLoaded )
376
395
certCacheMu .RUnlock ()
396
+
397
+ // give the new TLS app a "kick" to manage certs that it is configured for
398
+ // with its own configuration instead of the one we just evicted
399
+ if err := nextTLSApp .Manage (reManage ); err != nil {
400
+ t .logger .Error ("re-managing unloaded certificates with new config" ,
401
+ zap .Strings ("subjects" , reManage ),
402
+ zap .Error (err ))
403
+ }
377
404
} else {
378
405
// no more TLS app running, so delete in-memory cert cache
379
406
certCache .Stop ()
@@ -407,7 +434,20 @@ func (t *TLS) Manage(names []string) error {
407
434
return fmt .Errorf ("automate: manage %v: %v" , names , err )
408
435
}
409
436
for _ , name := range names {
410
- t .managing [name ] = struct {}{}
437
+ // certs that are issued solely by our internal issuer are a little bit of
438
+ // a special case: if you have an initial config that manages example.com
439
+ // using internal CA, then after testing it you switch to a production CA,
440
+ // you wouldn't want to keep using the same self-signed cert, obviously;
441
+ // so we differentiate these by associating the subject with its issuer key;
442
+ // we do this because CertMagic has no notion of "InternalIssuer" like we
443
+ // do, so we have to do this logic ourselves
444
+ var issuerKey string
445
+ if len (ap .Issuers ) == 1 {
446
+ if intIss , ok := ap .Issuers [0 ].(* InternalIssuer ); ok && intIss != nil {
447
+ issuerKey = intIss .IssuerKey ()
448
+ }
449
+ }
450
+ t .managing [name ] = issuerKey
411
451
}
412
452
}
413
453
0 commit comments