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