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
26 changed files with 279 additions and 22 deletions

View File

@@ -323,7 +323,8 @@ func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
// if the padding doesn't match exactly at the start then we can't safely strip
if index != 0 {
return nil, fmt.Errorf("mismatched leading whitespace in heredoc <<%s on line #%d [%s], expected whitespace [%s] to match the closing marker", marker, l.line+lineNum+1, lineText, paddingToStrip)
cleanLineText := strings.TrimRight(lineText, "\r\n")
return nil, fmt.Errorf("mismatched leading whitespace in heredoc <<%s on line #%d [%s], expected whitespace [%s] to match the closing marker", marker, l.line+lineNum+1, cleanLineText, paddingToStrip)
}
// strip, then append the line, with the newline, to the output.

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()