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
| 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 | } |