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`
This commit is contained in:
Aditya Bhargava 2025-10-03 16:05:46 -04:00 committed by GitHub
parent afbdcec08b
commit 3c003deec6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 250 additions and 17 deletions

View file

@ -481,7 +481,7 @@ 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
providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil || h.Option("acme_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)",

View file

@ -564,23 +564,22 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
}
if globalACMEDNSok {
if globalACMEDNSok && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil || acmeIssuer.Challenges.DNS.ProviderRaw == nil) {
globalDNS := options["dns"]
if globalDNS != nil {
// If global `dns` is set, do NOT set provider in issuer, just set empty dns config
acmeIssuer.Challenges = &caddytls.ChallengesConfig{
DNS: &caddytls.DNSChallengeConfig{},
}
} else if globalACMEDNS != nil {
// Set a global DNS provider if `acme_dns` is set and `dns` is NOT set
acmeIssuer.Challenges = &caddytls.ChallengesConfig{
DNS: &caddytls.DNSChallengeConfig{
ProviderRaw: caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil),
},
}
} else {
if globalDNS == nil && globalACMEDNS == nil {
return fmt.Errorf("acme_dns specified without DNS provider config, but no provider specified with 'dns' global option")
}
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
// If global `dns` is set, do NOT set provider in issuer, just set empty dns config
if globalDNS == nil && acmeIssuer.Challenges.DNS.ProviderRaw == nil {
// Set a global DNS provider if `acme_dns` is set and `dns` is NOT set
acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil)
}
}
if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
acmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)

View file

@ -53,6 +53,7 @@ example.com {
"challenges": {
"dns": {
"provider": {
"argument": "foo",
"name": "mock"
}
}

View file

@ -0,0 +1,79 @@
{
acme_dns mock foo
}
localhost {
tls {
dns mock bar
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "foo",
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}

View file

@ -0,0 +1,68 @@
{
dns mock foo
}
localhost {
tls {
dns mock bar
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
}
]
},
"dns": {
"argument": "foo",
"name": "mock"
}
}
}
}

View file

@ -0,0 +1,76 @@
{
acme_dns mock
}
localhost {
tls {
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}

View file

@ -15,7 +15,9 @@ func init() {
}
// MockDNSProvider is a mock DNS provider, for testing config with DNS modules.
type MockDNSProvider struct{}
type MockDNSProvider struct {
Argument string `json:"argument,omitempty"` // optional argument useful for testing
}
// CaddyModule returns the Caddy module information.
func (MockDNSProvider) CaddyModule() caddy.ModuleInfo {
@ -31,7 +33,15 @@ func (MockDNSProvider) Provision(ctx caddy.Context) error {
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
func (p *MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // consume directive name
if d.NextArg() {
p.Argument = d.Val()
}
if d.NextArg() {
return d.Errf("unexpected argument '%s'", d.Val())
}
return nil
}