@marcel91200 / caddy Mirror · read-only / commits / 3c003dee
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, 21:00 UTC

httpcaddyfile: Add missing DNS challenge check for `acme_dns` (#7270)

* add optional argument to `mock` DNS provider * preserve local DNS challenge settings when `acme_dns` is specified * add missing check for `acme_dns`

Aditya Bhargava committed Oct 3, 2025 at 16:05 UTC 3c003deec67ed1ca0127f4b303c64760a9a7b19d
7 files changed +250 -17
caddyconfig/httpcaddyfile/builtins.go
+1 -1
@@ -481,7 +481,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
481 // Validate DNS challenge config: any DNS challenge option except "dns" requires a DNS provider
482 if acmeIssuer != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
483 dnsCfg := acmeIssuer.Challenges.DNS
484 - providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil
484 + providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil || h.Option("acme_dns") != nil
485 if len(dnsOptionsSet) > 0 && !providerSet {
486 return nil, h.Errf(
487 "setting DNS challenge options [%s] requires a DNS provider (set with the 'dns' subdirective or 'acme_dns' global option)",
caddyconfig/httpcaddyfile/tlsapp.go
+13 -14
@@ -564,23 +564,22 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
564 if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
565 acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
566 }
567 - if globalACMEDNSok {
567 + if globalACMEDNSok && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil || acmeIssuer.Challenges.DNS.ProviderRaw == nil) {
568 globalDNS := options["dns"]
569 - if globalDNS != nil {
570 - // If global `dns` is set, do NOT set provider in issuer, just set empty dns config
571 - acmeIssuer.Challenges = &caddytls.ChallengesConfig{
572 - DNS: &caddytls.DNSChallengeConfig{},
573 - }
574 - } else if globalACMEDNS != nil {
575 - // Set a global DNS provider if `acme_dns` is set and `dns` is NOT set
576 - acmeIssuer.Challenges = &caddytls.ChallengesConfig{
577 - DNS: &caddytls.DNSChallengeConfig{
578 - ProviderRaw: caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil),
579 - },
580 - }
581 - } else {
569 + if globalDNS == nil && globalACMEDNS == nil {
570 return fmt.Errorf("acme_dns specified without DNS provider config, but no provider specified with 'dns' global option")
571 }
572 + if acmeIssuer.Challenges == nil {
573 + acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
574 + }
575 + if acmeIssuer.Challenges.DNS == nil {
576 + acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
577 + }
578 + // If global `dns` is set, do NOT set provider in issuer, just set empty dns config
579 + if globalDNS == nil && acmeIssuer.Challenges.DNS.ProviderRaw == nil {
580 + // Set a global DNS provider if `acme_dns` is set and `dns` is NOT set
581 + acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil)
582 + }
583 }
584 if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
585 acmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)
caddytest/integration/caddyfile_adapt/acme_dns_configured.caddyfiletest
+1
@@ -53,6 +53,7 @@ example.com {
53 "challenges": {
54 "dns": {
55 "provider": {
56 + "argument": "foo",
57 "name": "mock"
58 }
59 }
caddytest/integration/caddyfile_adapt/tls_dns_override_acme_dns.caddyfiletest new
+79
@@ -0,0 +1,79 @@
1 +{
2 + acme_dns mock foo
3 +}
4 +
5 +localhost {
6 + tls {
7 + dns mock bar
8 + resolvers 8.8.8.8 8.8.4.4
9 + }
10 +}
11 +----------
12 +{
13 + "apps": {
14 + "http": {
15 + "servers": {
16 + "srv0": {
17 + "listen": [
18 + ":443"
19 + ],
20 + "routes": [
21 + {
22 + "match": [
23 + {
24 + "host": [
25 + "localhost"
26 + ]
27 + }
28 + ],
29 + "terminal": true
30 + }
31 + ]
32 + }
33 + }
34 + },
35 + "tls": {
36 + "automation": {
37 + "policies": [
38 + {
39 + "subjects": [
40 + "localhost"
41 + ],
42 + "issuers": [
43 + {
44 + "challenges": {
45 + "dns": {
46 + "provider": {
47 + "argument": "bar",
48 + "name": "mock"
49 + },
50 + "resolvers": [
51 + "8.8.8.8",
52 + "8.8.4.4"
53 + ]
54 + }
55 + },
56 + "module": "acme"
57 + }
58 + ]
59 + },
60 + {
61 + "issuers": [
62 + {
63 + "challenges": {
64 + "dns": {
65 + "provider": {
66 + "argument": "foo",
67 + "name": "mock"
68 + }
69 + }
70 + },
71 + "module": "acme"
72 + }
73 + ]
74 + }
75 + ]
76 + }
77 + }
78 + }
79 +}
caddytest/integration/caddyfile_adapt/tls_dns_override_global_dns.caddyfiletest new
+68
@@ -0,0 +1,68 @@
1 +{
2 + dns mock foo
3 +}
4 +
5 +localhost {
6 + tls {
7 + dns mock bar
8 + resolvers 8.8.8.8 8.8.4.4
9 + }
10 +}
11 +----------
12 +{
13 + "apps": {
14 + "http": {
15 + "servers": {
16 + "srv0": {
17 + "listen": [
18 + ":443"
19 + ],
20 + "routes": [
21 + {
22 + "match": [
23 + {
24 + "host": [
25 + "localhost"
26 + ]
27 + }
28 + ],
29 + "terminal": true
30 + }
31 + ]
32 + }
33 + }
34 + },
35 + "tls": {
36 + "automation": {
37 + "policies": [
38 + {
39 + "subjects": [
40 + "localhost"
41 + ],
42 + "issuers": [
43 + {
44 + "challenges": {
45 + "dns": {
46 + "provider": {
47 + "argument": "bar",
48 + "name": "mock"
49 + },
50 + "resolvers": [
51 + "8.8.8.8",
52 + "8.8.4.4"
53 + ]
54 + }
55 + },
56 + "module": "acme"
57 + }
58 + ]
59 + }
60 + ]
61 + },
62 + "dns": {
63 + "argument": "foo",
64 + "name": "mock"
65 + }
66 + }
67 + }
68 +}
caddytest/integration/caddyfile_adapt/tls_dns_resolvers_with_global_provider.caddyfiletest new
+76
@@ -0,0 +1,76 @@
1 +{
2 + acme_dns mock
3 +}
4 +
5 +localhost {
6 + tls {
7 + resolvers 8.8.8.8 8.8.4.4
8 + }
9 +}
10 +----------
11 +{
12 + "apps": {
13 + "http": {
14 + "servers": {
15 + "srv0": {
16 + "listen": [
17 + ":443"
18 + ],
19 + "routes": [
20 + {
21 + "match": [
22 + {
23 + "host": [
24 + "localhost"
25 + ]
26 + }
27 + ],
28 + "terminal": true
29 + }
30 + ]
31 + }
32 + }
33 + },
34 + "tls": {
35 + "automation": {
36 + "policies": [
37 + {
38 + "subjects": [
39 + "localhost"
40 + ],
41 + "issuers": [
42 + {
43 + "challenges": {
44 + "dns": {
45 + "provider": {
46 + "name": "mock"
47 + },
48 + "resolvers": [
49 + "8.8.8.8",
50 + "8.8.4.4"
51 + ]
52 + }
53 + },
54 + "module": "acme"
55 + }
56 + ]
57 + },
58 + {
59 + "issuers": [
60 + {
61 + "challenges": {
62 + "dns": {
63 + "provider": {
64 + "name": "mock"
65 + }
66 + }
67 + },
68 + "module": "acme"
69 + }
70 + ]
71 + }
72 + ]
73 + }
74 + }
75 + }
76 +}
caddytest/integration/mockdns_test.go
+12 -2
@@ -15,7 +15,9 @@ func init() {
15 }
16
17 // MockDNSProvider is a mock DNS provider, for testing config with DNS modules.
18 -type MockDNSProvider struct{}
18 +type MockDNSProvider struct {
19 + Argument string `json:"argument,omitempty"` // optional argument useful for testing
20 +}
21
22 // CaddyModule returns the Caddy module information.
23 func (MockDNSProvider) CaddyModule() caddy.ModuleInfo {
@@ -31,7 +33,15 @@ func (MockDNSProvider) Provision(ctx caddy.Context) error {
33 }
34
35 // UnmarshalCaddyfile sets up the module from Caddyfile tokens.
34 -func (MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
36 +func (p *MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
37 + d.Next() // consume directive name
38 +
39 + if d.NextArg() {
40 + p.Argument = d.Val()
41 + }
42 + if d.NextArg() {
43 + return d.Errf("unexpected argument '%s'", d.Val())
44 + }
45 return nil
46 }
47