91
92
// set of subjects with managed certificates,
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
96
}
97
98
// CaddyModule returns the Caddy module information.
113
t.ctx = ctx
114
t.logger = ctx.Logger()
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)
117
118
// set up a new certificate cache; this (re)loads all certificates
119
cacheOpts := certmagic.CacheOptions{
267
if err != nil {
268
return fmt.Errorf("caching unmanaged certificate: %v", err)
269
}
269
- t.loaded[hash] = struct{}{}
270
+ t.loaded[hash] = ""
271
}
272
}
273
359
// compute which certificates were managed or loaded into the cert cache by this
360
// app instance (which is being stopped) that are not managed or loaded by the
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
+ }
383
}
384
}
385
for hash := range t.loaded {
388
}
389
}
390
391
+ // remove the certs
392
certCacheMu.RLock()
393
certCache.RemoveManaged(noLongerManaged)
394
certCache.Remove(noLongerLoaded)
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
+ }
404
} else {
405
// no more TLS app running, so delete in-memory cert cache
406
certCache.Stop()
434
return fmt.Errorf("automate: manage %v: %v", names, err)
435
}
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
451
}
452
}
453