Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 14:45 UTC
autohttps: Ensure CertMagic config is recreated after autohttps runs (#7510)
Francis Lavoie committed
Mar 3, 2026 at 16:44 UTC
d935a6956c16902623b8e8f6d1aafec4f6124f46
2 files changed
+77
-6
modules/caddyhttp/autohttps.go
+21
@@ -614,6 +614,27 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames, tails
614
}
615
}
616
617
+ // Ensure automation policies' CertMagic configs are rebuilt when
618
+ // ACME issuer templates may have been modified above (for example,
619
+ // alternate ports filled in by the HTTP app). If a policy is already
620
+ // provisioned, perform a lightweight rebuild of the CertMagic config
621
+ // so issuers receive SetConfig with the updated templates; otherwise
622
+ // run a normal Provision to initialize the policy.
623
+ for i, ap := range app.tlsApp.Automation.Policies {
624
+ // If the policy is already provisioned, rebuild only the CertMagic
625
+ // config so issuers get SetConfig with updated templates. Otherwise
626
+ // provision the policy normally (which may load modules).
627
+ if ap.IsProvisioned() {
628
+ if err := ap.RebuildCertMagic(app.tlsApp); err != nil {
629
+ return fmt.Errorf("rebuilding certmagic config for automation policy %d: %v", i, err)
630
+ }
631
+ } else {
632
+ if err := ap.Provision(app.tlsApp); err != nil {
633
+ return fmt.Errorf("provisioning automation policy %d after auto-HTTPS defaults: %v", i, err)
634
+ }
635
+ }
636
+ }
637
+
638
if basePolicy == nil {
639
// no base policy found; we will make one
640
basePolicy = new(caddytls.AutomationPolicy)
modules/caddytls/automation.go
+56
-6
@@ -243,22 +243,49 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
243
}
244
}
245
246
+ // build certmagic.Config and attach it to the policy
247
+ storage := ap.storage
248
+ if storage == nil {
249
+ storage = tlsApp.ctx.Storage()
250
+ }
251
+ cfg, err := ap.makeCertMagicConfig(tlsApp, issuers, storage)
252
+ if err != nil {
253
+ return err
254
+ }
255
+ certCacheMu.RLock()
256
+ ap.magic = certmagic.New(certCache, cfg)
257
+ certCacheMu.RUnlock()
258
+
259
+ // give issuers a chance to see the config pointer
260
+ for _, issuer := range ap.magic.Issuers {
261
+ if annoying, ok := issuer.(ConfigSetter); ok {
262
+ annoying.SetConfig(ap.magic)
263
+ }
264
+ }
265
+
266
+ return nil
267
+}
268
+
269
+// makeCertMagicConfig constructs a certmagic.Config for this policy using the
270
+// provided issuers and storage. It encapsulates common logic shared between
271
+// Provision and RebuildCertMagic so we don't duplicate code.
272
+func (ap *AutomationPolicy) makeCertMagicConfig(tlsApp *TLS, issuers []certmagic.Issuer, storage certmagic.Storage) (certmagic.Config, error) {
273
+ // key source
274
keyType := ap.KeyType
275
if keyType != "" {
276
var err error
277
keyType, err = caddy.NewReplacer().ReplaceOrErr(ap.KeyType, true, true)
278
if err != nil {
251
- return fmt.Errorf("invalid key type %s: %s", ap.KeyType, err)
279
+ return certmagic.Config{}, fmt.Errorf("invalid key type %s: %s", ap.KeyType, err)
280
}
281
if _, ok := supportedCertKeyTypes[keyType]; !ok {
254
- return fmt.Errorf("unrecognized key type: %s", keyType)
282
+ return certmagic.Config{}, fmt.Errorf("unrecognized key type: %s", keyType)
283
}
284
}
285
keySource := certmagic.StandardKeyGenerator{
286
KeyType: supportedCertKeyTypes[keyType],
287
}
288
261
- storage := ap.storage
289
if storage == nil {
290
storage = tlsApp.ctx.Storage()
291
}
@@ -277,7 +304,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
304
if noProtections {
305
if !ap.hadExplicitManagers {
306
// no managers, no explicitly-configured permission module, this is a config error
280
- return fmt.Errorf("on-demand TLS cannot be enabled without a permission module to prevent abuse; please refer to documentation for details")
307
+ return certmagic.Config{}, fmt.Errorf("on-demand TLS cannot be enabled without a permission module to prevent abuse; please refer to documentation for details")
308
}
309
// allow on-demand to be enabled but only for the purpose of the Managers; issuance won't be allowed from Issuers
310
tlsApp.logger.Warn("on-demand TLS can only get certificates from the configured external manager(s) because no ask endpoint / permission module is specified")
@@ -334,7 +361,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
361
}
362
}
363
337
- template := certmagic.Config{
364
+ cfg := certmagic.Config{
365
MustStaple: ap.MustStaple,
366
RenewalWindowRatio: ap.RenewalWindowRatio,
367
KeySource: keySource,
@@ -349,8 +376,31 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
376
Issuers: issuers,
377
Logger: tlsApp.logger,
378
}
379
+
380
+ return cfg, nil
381
+}
382
+
383
+// IsProvisioned reports whether the automation policy has been
384
+// provisioned. A provisioned policy has an initialized CertMagic
385
+// instance (i.e. ap.magic != nil).
386
+func (ap *AutomationPolicy) IsProvisioned() bool { return ap.magic != nil }
387
+
388
+// RebuildCertMagic rebuilds the policy's CertMagic configuration from the
389
+// policy's already-populated fields (Issuers, Managers, storage, etc.) and
390
+// replaces the internal CertMagic instance. This is a lightweight
391
+// alternative to calling Provision because it does not re-provision
392
+// modules or re-run module Provision; instead, it constructs a new
393
+// certmagic.Config and calls SetConfig on issuers so they receive updated
394
+// templates (for example, alternate HTTP/TLS ports supplied by the HTTP
395
+// app). RebuildCertMagic should only be called when the policy's required
396
+// fields are already populated.
397
+func (ap *AutomationPolicy) RebuildCertMagic(tlsApp *TLS) error {
398
+ cfg, err := ap.makeCertMagicConfig(tlsApp, ap.Issuers, ap.storage)
399
+ if err != nil {
400
+ return err
401
+ }
402
certCacheMu.RLock()
353
- ap.magic = certmagic.New(certCache, template)
403
+ ap.magic = certmagic.New(certCache, cfg)
404
certCacheMu.RUnlock()
405
406
// sometimes issuers may need the parent certmagic.Config in