@marcel91200 / caddy Mirror · read-only
Fork 0

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, 01:00 UTC

master
go 617 lines 23 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddytls
16
17 import (
18 "context"
19 "crypto/tls"
20 "encoding/json"
21 "errors"
22 "fmt"
23 "net"
24 "slices"
25 "strings"
26
27 "github.com/caddyserver/certmagic"
28 "github.com/mholt/acmez/v3"
29 "go.uber.org/zap"
30 "go.uber.org/zap/zapcore"
31 "golang.org/x/net/idna"
32
33 "github.com/caddyserver/caddy/v2"
34 )
35
36 // AutomationConfig governs the automated management of TLS certificates.
37 type AutomationConfig struct {
38 // The list of automation policies. The first policy matching
39 // a certificate or subject name will be applied.
40 Policies []*AutomationPolicy `json:"policies,omitempty"`
41
42 // On-Demand TLS defers certificate operations to the
43 // moment they are needed, e.g. during a TLS handshake.
44 // Useful when you don't know all the hostnames at
45 // config-time, or when you are not in control of the
46 // domain names you are managing certificates for.
47 // In 2015, Caddy became the first web server to
48 // implement this experimental technology.
49 //
50 // Note that this field does not enable on-demand TLS;
51 // it only configures it for when it is used. To enable
52 // it, create an automation policy with `on_demand`.
53 OnDemand *OnDemandConfig `json:"on_demand,omitempty"`
54
55 // Caddy staples OCSP (and caches the response) for all
56 // qualifying certificates by default. This setting
57 // changes how often it scans responses for freshness,
58 // and updates them if they are getting stale. Default: 1h
59 OCSPCheckInterval caddy.Duration `json:"ocsp_interval,omitempty"`
60
61 // Every so often, Caddy will scan all loaded, managed
62 // certificates for expiration. This setting changes how
63 // frequently the scan for expiring certificates is
64 // performed. Default: 10m
65 RenewCheckInterval caddy.Duration `json:"renew_interval,omitempty"`
66
67 // How often to scan storage units for old or expired
68 // assets and remove them. These scans exert lots of
69 // reads (and list operations) on the storage module, so
70 // choose a longer interval for large deployments.
71 // Default: 24h
72 //
73 // Storage will always be cleaned when the process first
74 // starts. Then, a new cleaning will be started this
75 // duration after the previous cleaning started if the
76 // previous cleaning finished in less than half the time
77 // of this interval (otherwise next start will be skipped).
78 StorageCleanInterval caddy.Duration `json:"storage_clean_interval,omitempty"`
79
80 defaultPublicAutomationPolicy *AutomationPolicy
81 defaultInternalAutomationPolicy *AutomationPolicy // only initialized if necessary
82 }
83
84 // AutomationPolicy designates the policy for automating the
85 // management (obtaining, renewal, and revocation) of managed
86 // TLS certificates.
87 //
88 // An AutomationPolicy value is not valid until it has been
89 // provisioned; use the `AddAutomationPolicy()` method on the
90 // TLS app to properly provision a new policy.
91 type AutomationPolicy struct {
92 // Which subjects (hostnames or IP addresses) this policy applies to.
93 //
94 // This list is a filter, not a command. In other words, it is used
95 // only to filter whether this policy should apply to a subject that
96 // needs a certificate; it does NOT command the TLS app to manage a
97 // certificate for that subject. To have Caddy automate a certificate
98 // or specific subjects, use the "automate" certificate loader module
99 // of the TLS app.
100 SubjectsRaw []string `json:"subjects,omitempty"`
101
102 // The modules that may issue certificates. Default: internal if all
103 // subjects do not qualify for public certificates; otherwise acme and
104 // zerossl.
105 IssuersRaw []json.RawMessage `json:"issuers,omitempty" caddy:"namespace=tls.issuance inline_key=module"`
106
107 // Modules that can get a custom certificate to use for any
108 // given TLS handshake at handshake-time. Custom certificates
109 // can be useful if another entity is managing certificates
110 // and Caddy need only get it and serve it. Specifying a Manager
111 // enables on-demand TLS, i.e. it has the side-effect of setting
112 // the on_demand parameter to `true`.
113 //
114 // TODO: This is an EXPERIMENTAL feature. Subject to change or removal.
115 ManagersRaw []json.RawMessage `json:"get_certificate,omitempty" caddy:"namespace=tls.get_certificate inline_key=via"`
116
117 // If true, certificates will be requested with MustStaple. Not all
118 // CAs support this, and there are potentially serious consequences
119 // of enabling this feature without proper threat modeling.
120 MustStaple bool `json:"must_staple,omitempty"`
121
122 // How long before a certificate's expiration to try renewing it,
123 // as a function of its total lifetime. As a general and conservative
124 // rule, it is a good idea to renew a certificate when it has about
125 // 1/3 of its total lifetime remaining. This utilizes the majority
126 // of the certificate's lifetime while still saving time to
127 // troubleshoot problems. However, for extremely short-lived certs,
128 // you may wish to increase the ratio to ~1/2.
129 RenewalWindowRatio float64 `json:"renewal_window_ratio,omitempty"`
130
131 // The type of key to generate for certificates.
132 // Supported values: `ed25519`, `p256`, `p384`, `rsa2048`, `rsa4096`.
133 KeyType string `json:"key_type,omitempty"`
134
135 // Optionally configure a separate storage module associated with this
136 // manager, instead of using Caddy's global/default-configured storage.
137 StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
138
139 // If true, certificates will be managed "on demand"; that is, during
140 // TLS handshakes or when needed, as opposed to at startup or config
141 // load. This enables On-Demand TLS for this policy.
142 OnDemand bool `json:"on_demand,omitempty"`
143
144 // If true, private keys already existing in storage
145 // will be reused. Otherwise, a new key will be
146 // created for every new certificate to mitigate
147 // pinning and reduce the scope of key compromise.
148 // TEMPORARY: Key pinning is against industry best practices.
149 // This property will likely be removed in the future.
150 // Do not rely on it forever; watch the release notes.
151 ReusePrivateKeys bool `json:"reuse_private_keys,omitempty"`
152
153 // Disables OCSP stapling. Disabling OCSP stapling puts clients at
154 // greater risk, reduces their privacy, and usually lowers client
155 // performance. It is NOT recommended to disable this unless you
156 // are able to justify the costs.
157 // EXPERIMENTAL. Subject to change.
158 DisableOCSPStapling bool `json:"disable_ocsp_stapling,omitempty"`
159
160 // Overrides the URLs of OCSP responders embedded in certificates.
161 // Each key is an OCSP server URL to override, and its value is the
162 // replacement. An empty value will disable querying of that server.
163 // EXPERIMENTAL. Subject to change.
164 OCSPOverrides map[string]string `json:"ocsp_overrides,omitempty"`
165
166 // Issuers and Managers store the decoded issuer and manager modules;
167 // they are only used to populate an underlying certmagic.Config's
168 // fields during provisioning so that the modules can survive a
169 // re-provisioning.
170 Issuers []certmagic.Issuer `json:"-"`
171 Managers []certmagic.Manager `json:"-"`
172
173 subjects []string
174 magic *certmagic.Config
175 storage certmagic.Storage
176
177 // Whether this policy had explicit managers configured directly on it.
178 hadExplicitManagers bool
179 }
180
181 // Provision sets up ap and builds its underlying CertMagic config.
182 func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
183 // replace placeholders in subjects to allow environment variables
184 repl := caddy.NewReplacer()
185 subjects := make([]string, len(ap.SubjectsRaw))
186 for i, sub := range ap.SubjectsRaw {
187 sub = repl.ReplaceAll(sub, "")
188 subASCII, err := idna.ToASCII(sub)
189 if err != nil {
190 return fmt.Errorf("could not convert automation policy subject '%s' to punycode: %v", sub, err)
191 }
192 subjects[i] = subASCII
193 }
194 ap.subjects = subjects
195
196 // policy-specific storage implementation
197 if ap.StorageRaw != nil {
198 val, err := tlsApp.ctx.LoadModule(ap, "StorageRaw")
199 if err != nil {
200 return fmt.Errorf("loading TLS storage module: %v", err)
201 }
202 cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
203 if err != nil {
204 return fmt.Errorf("creating TLS storage configuration: %v", err)
205 }
206 ap.storage = cmStorage
207 }
208
209 // we don't store loaded modules directly in the certmagic config since
210 // policy provisioning may happen more than once (during auto-HTTPS) and
211 // loading a module clears its config bytes; thus, load the module and
212 // store them on the policy before putting it on the config
213
214 // load and provision any cert manager modules
215 if ap.ManagersRaw != nil {
216 ap.hadExplicitManagers = true
217 vals, err := tlsApp.ctx.LoadModule(ap, "ManagersRaw")
218 if err != nil {
219 return fmt.Errorf("loading external certificate manager modules: %v", err)
220 }
221 for _, getCertVal := range vals.([]any) {
222 ap.Managers = append(ap.Managers, getCertVal.(certmagic.Manager))
223 }
224 }
225
226 // load and provision any explicitly-configured issuer modules
227 if ap.IssuersRaw != nil {
228 val, err := tlsApp.ctx.LoadModule(ap, "IssuersRaw")
229 if err != nil {
230 return fmt.Errorf("loading TLS automation management module: %s", err)
231 }
232 for _, issVal := range val.([]any) {
233 ap.Issuers = append(ap.Issuers, issVal.(certmagic.Issuer))
234 }
235 }
236
237 issuers := ap.Issuers
238 if len(issuers) == 0 && !ap.implicitTailscaleManagersOnly() {
239 var err error
240 issuers, err = DefaultIssuersProvisioned(tlsApp.ctx)
241 if err != nil {
242 return err
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 {
279 return certmagic.Config{}, fmt.Errorf("invalid key type %s: %s", ap.KeyType, err)
280 }
281 if _, ok := supportedCertKeyTypes[keyType]; !ok {
282 return certmagic.Config{}, fmt.Errorf("unrecognized key type: %s", keyType)
283 }
284 }
285 keySource := certmagic.StandardKeyGenerator{
286 KeyType: supportedCertKeyTypes[keyType],
287 }
288
289 if storage == nil {
290 storage = tlsApp.ctx.Storage()
291 }
292
293 // on-demand TLS
294 var ond *certmagic.OnDemandConfig
295 if ap.OnDemand || len(ap.Managers) > 0 {
296 // permission module is now required after a number of negligence cases that allowed abuse;
297 // but it may still be optional for explicit subjects (bounded, non-wildcard), for the
298 // internal issuer since it doesn't cause public PKI pressure on ACME servers; subtly, it
299 // is useful to allow on-demand TLS to be enabled so Managers can be used, but to still
300 // prevent issuance from Issuers (when Managers don't provide a certificate) if there's no
301 // permission module configured
302 noProtections := ap.isWildcardOrDefault() && !ap.onlyInternalIssuer() && (tlsApp.Automation == nil || tlsApp.Automation.OnDemand == nil || tlsApp.Automation.OnDemand.permission == nil)
303 failClosed := noProtections && !ap.hadExplicitManagers // don't allow on-demand issuance (other than implicit managers) if no managers have been explicitly configured
304 if noProtections {
305 if !ap.hadExplicitManagers {
306 // no managers, no explicitly-configured permission module, this is a config error
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")
311 }
312 ond = &certmagic.OnDemandConfig{
313 DecisionFunc: func(ctx context.Context, name string) error {
314 if failClosed {
315 return fmt.Errorf("no permission module configured; certificates not allowed except from external Managers")
316 }
317 if tlsApp.Automation == nil || tlsApp.Automation.OnDemand == nil {
318 return nil
319 }
320
321 // logging the remote IP can be useful for servers that want to count
322 // attempts from clients to detect patterns of abuse -- it should NOT be
323 // used solely for decision making, however
324 var remoteIP string
325 if hello, ok := ctx.Value(certmagic.ClientHelloInfoCtxKey).(*tls.ClientHelloInfo); ok && hello != nil {
326 if remote := hello.Conn.RemoteAddr(); remote != nil {
327 remoteIP, _, _ = net.SplitHostPort(remote.String())
328 }
329 }
330 if c := tlsApp.logger.Check(zapcore.DebugLevel, "asking for permission for on-demand certificate"); c != nil {
331 c.Write(
332 zap.String("remote_ip", remoteIP),
333 zap.String("domain", name),
334 )
335 }
336
337 // ask the permission module if this cert is allowed
338 if err := tlsApp.Automation.OnDemand.permission.CertificateAllowed(ctx, name); err != nil {
339 // distinguish true errors from denials, because it's important to elevate actual errors
340 if errors.Is(err, ErrPermissionDenied) {
341 if c := tlsApp.logger.Check(zapcore.DebugLevel, "on-demand certificate issuance denied"); c != nil {
342 c.Write(
343 zap.String("domain", name),
344 zap.Error(err),
345 )
346 }
347 } else {
348 if c := tlsApp.logger.Check(zapcore.ErrorLevel, "failed to get permission for on-demand certificate"); c != nil {
349 c.Write(
350 zap.String("domain", name),
351 zap.Error(err),
352 )
353 }
354 }
355 return err
356 }
357
358 return nil
359 },
360 Managers: ap.Managers,
361 }
362 }
363
364 cfg := certmagic.Config{
365 MustStaple: ap.MustStaple,
366 RenewalWindowRatio: ap.RenewalWindowRatio,
367 KeySource: keySource,
368 OnEvent: tlsApp.onEvent,
369 ShouldEmitFunc: tlsApp.events.ShouldEmit,
370 OnDemand: ond,
371 ReusePrivateKeys: ap.ReusePrivateKeys,
372 OCSP: certmagic.OCSPConfig{
373 DisableStapling: ap.DisableOCSPStapling,
374 ResponderOverrides: ap.OCSPOverrides,
375 },
376 Storage: storage,
377 Issuers: issuers,
378 Logger: tlsApp.logger,
379 }
380
381 return cfg, nil
382 }
383
384 // IsProvisioned reports whether the automation policy has been
385 // provisioned. A provisioned policy has an initialized CertMagic
386 // instance (i.e. ap.magic != nil).
387 func (ap *AutomationPolicy) IsProvisioned() bool { return ap.magic != nil }
388
389 // RebuildCertMagic rebuilds the policy's CertMagic configuration from the
390 // policy's already-populated fields (Issuers, Managers, storage, etc.) and
391 // replaces the internal CertMagic instance. This is a lightweight
392 // alternative to calling Provision because it does not re-provision
393 // modules or re-run module Provision; instead, it constructs a new
394 // certmagic.Config and calls SetConfig on issuers so they receive updated
395 // templates (for example, alternate HTTP/TLS ports supplied by the HTTP
396 // app). RebuildCertMagic should only be called when the policy's required
397 // fields are already populated.
398 func (ap *AutomationPolicy) RebuildCertMagic(tlsApp *TLS) error {
399 cfg, err := ap.makeCertMagicConfig(tlsApp, ap.Issuers, ap.storage)
400 if err != nil {
401 return err
402 }
403 certCacheMu.RLock()
404 ap.magic = certmagic.New(certCache, cfg)
405 certCacheMu.RUnlock()
406
407 // sometimes issuers may need the parent certmagic.Config in
408 // order to function properly (for example, ACMEIssuer needs
409 // access to the correct storage and cache so it can solve
410 // ACME challenges -- it's an annoying, inelegant circular
411 // dependency that I don't know how to resolve nicely!)
412 for _, issuer := range ap.magic.Issuers {
413 if annoying, ok := issuer.(ConfigSetter); ok {
414 annoying.SetConfig(ap.magic)
415 }
416 }
417
418 return nil
419 }
420
421 // Subjects returns the list of subjects with all placeholders replaced.
422 func (ap *AutomationPolicy) Subjects() []string {
423 return ap.subjects
424 }
425
426 // AllInternalSubjects returns true if all the subjects on this policy are internal.
427 func (ap *AutomationPolicy) AllInternalSubjects() bool {
428 return !slices.ContainsFunc(ap.subjects, func(s string) bool {
429 return !certmagic.SubjectIsInternal(s)
430 })
431 }
432
433 // implicitTailscaleManagersOnly returns true if this policy is configured to
434 // serve only Tailscale names from the Tailscale manager at handshake-time.
435 func (ap *AutomationPolicy) implicitTailscaleManagersOnly() bool {
436 if len(ap.subjects) == 0 {
437 return false
438 }
439
440 for _, subject := range ap.subjects {
441 if !strings.HasSuffix(strings.ToLower(subject), tailscaleDomainAliasEnding) {
442 return false
443 }
444 }
445
446 for _, manager := range ap.Managers {
447 switch manager.(type) {
448 case Tailscale, *Tailscale:
449 return true
450 }
451 }
452
453 return false
454 }
455
456 func (ap *AutomationPolicy) onlyInternalIssuer() bool {
457 if len(ap.Issuers) != 1 {
458 return false
459 }
460 _, ok := ap.Issuers[0].(*InternalIssuer)
461 return ok
462 }
463
464 // isWildcardOrDefault determines if the subjects include any wildcard domains,
465 // or is the "default" policy (i.e. no subjects) which is unbounded.
466 func (ap *AutomationPolicy) isWildcardOrDefault() bool {
467 isWildcardOrDefault := len(ap.subjects) == 0
468
469 for _, sub := range ap.subjects {
470 if strings.HasPrefix(sub, "*") {
471 isWildcardOrDefault = true
472 break
473 }
474 }
475 return isWildcardOrDefault
476 }
477
478 // DefaultIssuers returns empty Issuers (not provisioned) to be used as defaults.
479 // This function is experimental and has no compatibility promises.
480 func DefaultIssuers(userEmail string) []certmagic.Issuer {
481 issuers := []certmagic.Issuer{new(ACMEIssuer)}
482 if strings.TrimSpace(userEmail) != "" {
483 issuers = append(issuers, &ACMEIssuer{
484 CA: certmagic.ZeroSSLProductionCA,
485 Email: userEmail,
486 })
487 }
488 return issuers
489 }
490
491 // DefaultIssuersProvisioned returns empty but provisioned default Issuers from
492 // DefaultIssuers(). This function is experimental and has no compatibility promises.
493 func DefaultIssuersProvisioned(ctx caddy.Context) ([]certmagic.Issuer, error) {
494 issuers := DefaultIssuers("")
495 for i, iss := range issuers {
496 if prov, ok := iss.(caddy.Provisioner); ok {
497 err := prov.Provision(ctx)
498 if err != nil {
499 return nil, fmt.Errorf("provisioning default issuer %d: %T: %v", i, iss, err)
500 }
501 }
502 }
503 return issuers, nil
504 }
505
506 // ChallengesConfig configures the ACME challenges.
507 type ChallengesConfig struct {
508 // HTTP configures the ACME HTTP challenge. This
509 // challenge is enabled and used automatically
510 // and by default.
511 HTTP *HTTPChallengeConfig `json:"http,omitempty"`
512
513 // TLSALPN configures the ACME TLS-ALPN challenge.
514 // This challenge is enabled and used automatically
515 // and by default.
516 TLSALPN *TLSALPNChallengeConfig `json:"tls-alpn,omitempty"`
517
518 // Configures the ACME DNS challenge. Because this
519 // challenge typically requires credentials for
520 // interfacing with a DNS provider, this challenge is
521 // not enabled by default. This is the only challenge
522 // type which does not require a direct connection
523 // to Caddy from an external server.
524 //
525 // NOTE: DNS providers are currently being upgraded,
526 // and this API is subject to change, but should be
527 // stabilized soon.
528 DNS *DNSChallengeConfig `json:"dns,omitempty"`
529
530 // Optionally customize the host to which a listener
531 // is bound if required for solving a challenge.
532 BindHost string `json:"bind_host,omitempty"`
533
534 // Whether distributed solving is enabled. This is
535 // enabled by default, so this is only used to
536 // disable it, which should only need to be done if
537 // you cannot reliably or affordably use storage
538 // backend for writing/distributing challenge info.
539 // (Applies to HTTP and TLS-ALPN challenges.)
540 // If set to false, challenges can only be solved
541 // from the Caddy instance that initiated the
542 // challenge, with the exception of HTTP challenges
543 // initiated with the same ACME account that this
544 // config uses. (Caddy can still solve those challenges
545 // without explicitly writing the info to storage.)
546 //
547 // Default: true
548 Distributed *bool `json:"distributed,omitempty"`
549 }
550
551 // HTTPChallengeConfig configures the ACME HTTP challenge.
552 type HTTPChallengeConfig struct {
553 // If true, the HTTP challenge will be disabled.
554 Disabled bool `json:"disabled,omitempty"`
555
556 // An alternate port on which to service this
557 // challenge. Note that the HTTP challenge port is
558 // hard-coded into the spec and cannot be changed,
559 // so you would have to forward packets from the
560 // standard HTTP challenge port to this one.
561 AlternatePort int `json:"alternate_port,omitempty"`
562 }
563
564 // TLSALPNChallengeConfig configures the ACME TLS-ALPN challenge.
565 type TLSALPNChallengeConfig struct {
566 // If true, the TLS-ALPN challenge will be disabled.
567 Disabled bool `json:"disabled,omitempty"`
568
569 // An alternate port on which to service this
570 // challenge. Note that the TLS-ALPN challenge port
571 // is hard-coded into the spec and cannot be changed,
572 // so you would have to forward packets from the
573 // standard TLS-ALPN challenge port to this one.
574 AlternatePort int `json:"alternate_port,omitempty"`
575 }
576
577 // DNSChallengeConfig configures the ACME DNS challenge.
578 //
579 // NOTE: This API is still experimental and is subject to change.
580 type DNSChallengeConfig struct {
581 // The DNS provider module to use which will manage
582 // the DNS records relevant to the ACME challenge.
583 // Required.
584 ProviderRaw json.RawMessage `json:"provider,omitempty" caddy:"namespace=dns.providers inline_key=name"`
585
586 // The TTL of the TXT record used for the DNS challenge.
587 TTL caddy.Duration `json:"ttl,omitempty"`
588
589 // How long to wait before starting propagation checks.
590 // Default: 0 (no wait).
591 PropagationDelay caddy.Duration `json:"propagation_delay,omitempty"`
592
593 // Maximum time to wait for temporary DNS record to appear.
594 // Set to -1 to disable propagation checks.
595 // Default: 2 minutes.
596 PropagationTimeout caddy.Duration `json:"propagation_timeout,omitempty"`
597
598 // Custom DNS resolvers to prefer over system/built-in defaults.
599 // Often necessary to configure when using split-horizon DNS.
600 Resolvers []string `json:"resolvers,omitempty"`
601
602 // Override the domain to use for the DNS challenge. This
603 // is to delegate the challenge to a different domain,
604 // e.g. one that updates faster or one with a provider API.
605 OverrideDomain string `json:"override_domain,omitempty"`
606
607 solver acmez.Solver
608 }
609
610 // ConfigSetter is implemented by certmagic.Issuers that
611 // need access to a parent certmagic.Config as part of
612 // their provisioning phase. For example, the ACMEIssuer
613 // requires a config so it can access storage and the
614 // cache to solve ACME challenges.
615 type ConfigSetter interface {
616 SetConfig(cfg *certmagic.Config)
617 }