httpcaddyfile: Validates TLS DNS challenge options (#7099)

* httpcaddyfile: Validates TLS DNS challenge options

Adds validation to the TLS Caddyfile adapter to ensure that when DNS challenge options (such as propagation_delay or dns_ttl) are specified, a DNS provider is also configured.

Adds new integration tests to verify this validation logic, and implements a new mechanism for adapt tests to assert a config adapt error.

* Add some more AI-generated tests asserting config errors

* Parallel doesn't work here, we use global variables

* Windows fix
This commit is contained in:
Francis Lavoie 2025-06-30 19:58:16 -04:00 committed by GitHub
parent c712cfcd76
commit 77dd12cc78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 279 additions and 22 deletions

View file

@ -130,6 +130,9 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var reusePrivateKeys bool
var forceAutomate bool
// Track which DNS challenge options are set
var dnsOptionsSet []string
firstLine := h.RemainingArgs()
switch len(firstLine) {
case 0:
@ -350,6 +353,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
dnsOptionsSet = append(dnsOptionsSet, "resolvers")
acmeIssuer.Challenges.DNS.Resolvers = args
case "propagation_delay":
@ -371,6 +375,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
dnsOptionsSet = append(dnsOptionsSet, "propagation_delay")
acmeIssuer.Challenges.DNS.PropagationDelay = caddy.Duration(delay)
case "propagation_timeout":
@ -398,6 +403,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
dnsOptionsSet = append(dnsOptionsSet, "propagation_timeout")
acmeIssuer.Challenges.DNS.PropagationTimeout = caddy.Duration(timeout)
case "dns_ttl":
@ -419,6 +425,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
dnsOptionsSet = append(dnsOptionsSet, "dns_ttl")
acmeIssuer.Challenges.DNS.TTL = caddy.Duration(ttl)
case "dns_challenge_override_domain":
@ -435,6 +442,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
dnsOptionsSet = append(dnsOptionsSet, "dns_challenge_override_domain")
acmeIssuer.Challenges.DNS.OverrideDomain = arg[0]
case "ca_root":
@ -470,6 +478,18 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
}
}
// Validate DNS challenge config: any DNS challenge option except "dns" requires a DNS provider
if acmeIssuer != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
dnsCfg := acmeIssuer.Challenges.DNS
providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil
if len(dnsOptionsSet) > 0 && !providerSet {
return nil, h.Errf(
"setting DNS challenge options [%s] requires a DNS provider (set with the 'dns' subdirective or 'acme_dns' global option)",
strings.Join(dnsOptionsSet, ", "),
)
}
}
// a naked tls directive is not allowed
if len(firstLine) == 0 && !hasBlock {
return nil, h.ArgErr()