Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions caddyconfig/httpcaddyfile/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func init() {
RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
RegisterGlobalOption("persist_config", parseOptPersistConfig)
RegisterGlobalOption("dns", parseOptDNS)
RegisterGlobalOption("tls_resolvers", parseOptTLSResolvers)
RegisterGlobalOption("ech", parseOptECH)
RegisterGlobalOption("renewal_window_ratio", parseOptRenewalWindowRatio)
}
Expand Down Expand Up @@ -306,6 +307,15 @@ func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {
return val, nil
}

func parseOptTLSResolvers(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
resolvers := d.RemainingArgs()
if len(resolvers) == 0 {
return nil, d.ArgErr()
}
return resolvers, nil
}

func parseOptDefaultBind(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name

Expand Down
104 changes: 104 additions & 0 deletions caddyconfig/httpcaddyfile/options_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package httpcaddyfile

import (
"encoding/json"
"testing"

"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddytls"
_ "github.com/caddyserver/caddy/v2/modules/logging"
)

Expand Down Expand Up @@ -62,3 +64,105 @@ func TestGlobalLogOptionSyntax(t *testing.T) {
}
}
}

func TestGlobalResolversOption(t *testing.T) {
tests := []struct {
name string
input string
expectResolvers []string
expectError bool
}{
{
name: "single resolver",
input: `{
tls_resolvers 1.1.1.1
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1"},
expectError: false,
},
{
name: "two resolvers",
input: `{
tls_resolvers 1.1.1.1 8.8.8.8
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1", "8.8.8.8"},
expectError: false,
},
{
name: "multiple resolvers",
input: `{
tls_resolvers 1.1.1.1 8.8.8.8 9.9.9.9
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1", "8.8.8.8", "9.9.9.9"},
expectError: false,
},
{
name: "no resolvers specified",
input: `{
}
example.com {
}`,
expectResolvers: nil,
expectError: false,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
adapter := caddyfile.Adapter{
ServerType: ServerType{},
}

out, _, err := adapter.Adapt([]byte(tc.input), nil)

if (err != nil) != tc.expectError {
t.Errorf("error expectation failed. Expected error: %v, got: %v", tc.expectError, err)
return
}

if tc.expectError {
return
}

// Parse the output JSON to check resolvers
var config struct {
Apps struct {
TLS *caddytls.TLS `json:"tls"`
} `json:"apps"`
}

if err := json.Unmarshal(out, &config); err != nil {
t.Errorf("failed to unmarshal output: %v", err)
return
}

// Check if resolvers match expected
if config.Apps.TLS == nil {
if tc.expectResolvers != nil {
t.Errorf("Expected TLS config with resolvers %v, but TLS config is nil", tc.expectResolvers)
}
return
}

actualResolvers := config.Apps.TLS.Resolvers
if len(tc.expectResolvers) == 0 && len(actualResolvers) == 0 {
return // Both empty, ok
}
if len(actualResolvers) != len(tc.expectResolvers) {
t.Errorf("Expected %d resolvers, got %d. Expected: %v, got: %v", len(tc.expectResolvers), len(actualResolvers), tc.expectResolvers, actualResolvers)
return
}
for j, expected := range tc.expectResolvers {
if actualResolvers[j] != expected {
t.Errorf("Resolver %d mismatch. Expected: %s, got: %s", j, expected, actualResolvers[j])
}
}
})
}
}
14 changes: 14 additions & 0 deletions caddyconfig/httpcaddyfile/tlsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ func (st ServerType) buildTLSApp(
tlsApp.DNSRaw = caddyconfig.JSONModuleObject(globalDNS, "name", globalDNS.(caddy.Module).CaddyModule().ID.Name(), nil)
}

// set up "global" (to the TLS app) DNS resolvers config
if globalResolvers, ok := options["tls_resolvers"]; ok && globalResolvers != nil {
tlsApp.Resolvers = globalResolvers.([]string)
}

// set up ECH from Caddyfile options
if ech, ok := options["ech"].(*caddytls.ECH); ok {
tlsApp.EncryptedClientHello = ech
Expand Down Expand Up @@ -595,6 +600,15 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalCertLifetime != nil && acmeIssuer.CertificateLifetime == 0 {
acmeIssuer.CertificateLifetime = globalCertLifetime.(caddy.Duration)
}
// apply global resolvers if DNS challenge is configured and resolvers are not already set
globalResolvers := options["tls_resolvers"]
if globalResolvers != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
// Check if DNS challenge is actually configured
hasDNSChallenge := globalACMEDNSok || acmeIssuer.Challenges.DNS.ProviderRaw != nil
if hasDNSChallenge && len(acmeIssuer.Challenges.DNS.Resolvers) == 0 {
acmeIssuer.Challenges.DNS.Resolvers = globalResolvers.([]string)
}
}
return nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
acme_dns
}

example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
tls_resolvers 1.1.1.1 8.8.8.8
}

example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
}

example.com {
tls {
dns mock
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
Loading
Loading