1
0
Fork 0

Add options to control ACME propagation checks

This commit is contained in:
Ludovic Fernandez 2024-11-26 09:08:04 +01:00 committed by GitHub
parent 0ec12c7aa7
commit 33c1d700c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 455 additions and 28 deletions

View file

@ -306,6 +306,36 @@ func (c *Configuration) SetEffectiveConfiguration() {
c.Providers.KubernetesIngress.DefaultRuleSyntax = c.Core.DefaultRuleSyntax
}
for _, resolver := range c.CertificatesResolvers {
if resolver.ACME == nil {
continue
}
if resolver.ACME.DNSChallenge == nil {
continue
}
if resolver.ACME.DNSChallenge.DisablePropagationCheck {
log.Warn().Msgf("disablePropagationCheck is now deprecated, please use propagation.disableAllChecks instead.")
if resolver.ACME.DNSChallenge.Propagation == nil {
resolver.ACME.DNSChallenge.Propagation = &acmeprovider.Propagation{}
}
resolver.ACME.DNSChallenge.Propagation.DisableChecks = true
}
if resolver.ACME.DNSChallenge.DelayBeforeCheck > 0 {
log.Warn().Msgf("delayBeforeCheck is now deprecated, please use propagation.delayBeforeCheck instead.")
if resolver.ACME.DNSChallenge.Propagation == nil {
resolver.ACME.DNSChallenge.Propagation = &acmeprovider.Propagation{}
}
resolver.ACME.DNSChallenge.Propagation.DelayBeforeChecks = resolver.ACME.DNSChallenge.DelayBeforeCheck
}
}
c.initACMEProvider()
}

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/traefik/traefik/v3/pkg/provider/acme"
)
func TestHasEntrypoint(t *testing.T) {
@ -37,3 +38,233 @@ func TestHasEntrypoint(t *testing.T) {
})
}
}
func TestConfiguration_SetEffectiveConfiguration(t *testing.T) {
testCases := []struct {
desc string
conf *Configuration
expected *Configuration
}{
{
desc: "empty",
conf: &Configuration{
Providers: &Providers{},
},
expected: &Configuration{
EntryPoints: EntryPoints{"http": &EntryPoint{
Address: ":80",
AllowACMEByPass: false,
ReusePort: false,
AsDefault: false,
Transport: &EntryPointsTransport{
LifeCycle: &LifeCycle{
GraceTimeOut: 10000000000,
},
RespondingTimeouts: &RespondingTimeouts{
ReadTimeout: 60000000000,
IdleTimeout: 180000000000,
},
},
ProxyProtocol: nil,
ForwardedHeaders: &ForwardedHeaders{},
HTTP: HTTPConfig{
MaxHeaderBytes: 1048576,
},
HTTP2: &HTTP2Config{
MaxConcurrentStreams: 250,
},
HTTP3: nil,
UDP: &UDPConfig{
Timeout: 3000000000,
},
}},
Providers: &Providers{},
},
},
{
desc: "ACME simple",
conf: &Configuration{
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
},
},
},
},
},
expected: &Configuration{
EntryPoints: EntryPoints{"http": &EntryPoint{
Address: ":80",
AllowACMEByPass: false,
ReusePort: false,
AsDefault: false,
Transport: &EntryPointsTransport{
LifeCycle: &LifeCycle{
GraceTimeOut: 10000000000,
},
RespondingTimeouts: &RespondingTimeouts{
ReadTimeout: 60000000000,
IdleTimeout: 180000000000,
},
},
ProxyProtocol: nil,
ForwardedHeaders: &ForwardedHeaders{},
HTTP: HTTPConfig{
MaxHeaderBytes: 1048576,
},
HTTP2: &HTTP2Config{
MaxConcurrentStreams: 250,
},
HTTP3: nil,
UDP: &UDPConfig{
Timeout: 3000000000,
},
}},
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
CAServer: "https://acme-v02.api.letsencrypt.org/directory",
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
},
},
},
},
},
},
{
desc: "ACME deprecation DelayBeforeCheck",
conf: &Configuration{
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
DelayBeforeCheck: 123,
},
},
},
},
},
expected: &Configuration{
EntryPoints: EntryPoints{"http": &EntryPoint{
Address: ":80",
AllowACMEByPass: false,
ReusePort: false,
AsDefault: false,
Transport: &EntryPointsTransport{
LifeCycle: &LifeCycle{
GraceTimeOut: 10000000000,
},
RespondingTimeouts: &RespondingTimeouts{
ReadTimeout: 60000000000,
IdleTimeout: 180000000000,
},
},
ProxyProtocol: nil,
ForwardedHeaders: &ForwardedHeaders{},
HTTP: HTTPConfig{
MaxHeaderBytes: 1048576,
},
HTTP2: &HTTP2Config{
MaxConcurrentStreams: 250,
},
HTTP3: nil,
UDP: &UDPConfig{
Timeout: 3000000000,
},
}},
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
CAServer: "https://acme-v02.api.letsencrypt.org/directory",
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
DelayBeforeCheck: 123,
Propagation: &acme.Propagation{
DelayBeforeChecks: 123,
},
},
},
},
},
},
},
{
desc: "ACME deprecation DisablePropagationCheck",
conf: &Configuration{
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
DisablePropagationCheck: true,
},
},
},
},
},
expected: &Configuration{
EntryPoints: EntryPoints{"http": &EntryPoint{
Address: ":80",
AllowACMEByPass: false,
ReusePort: false,
AsDefault: false,
Transport: &EntryPointsTransport{
LifeCycle: &LifeCycle{
GraceTimeOut: 10000000000,
},
RespondingTimeouts: &RespondingTimeouts{
ReadTimeout: 60000000000,
IdleTimeout: 180000000000,
},
},
ProxyProtocol: nil,
ForwardedHeaders: &ForwardedHeaders{},
HTTP: HTTPConfig{
MaxHeaderBytes: 1048576,
},
HTTP2: &HTTP2Config{
MaxConcurrentStreams: 250,
},
HTTP3: nil,
UDP: &UDPConfig{
Timeout: 3000000000,
},
}},
Providers: &Providers{},
CertificatesResolvers: map[string]CertificateResolver{
"foo": {
ACME: &acme.Configuration{
CAServer: "https://acme-v02.api.letsencrypt.org/directory",
DNSChallenge: &acme.DNSChallenge{
Provider: "bar",
DisablePropagationCheck: true,
Propagation: &acme.Propagation{
DisableChecks: true,
},
},
},
},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
test.conf.SetEffectiveConfiguration()
assert.Equal(t, test.expected, test.conf)
})
}
}

View file

@ -85,10 +85,21 @@ type EAB struct {
// DNSChallenge contains DNS challenge configuration.
type DNSChallenge struct {
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty" export:"true"`
DelayBeforeCheck ptypes.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty" export:"true"`
Resolvers []string `description:"Use following DNS servers to resolve the FQDN authority." json:"resolvers,omitempty" toml:"resolvers,omitempty" yaml:"resolvers,omitempty"`
DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty" export:"true"`
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty" export:"true"`
Resolvers []string `description:"Use following DNS servers to resolve the FQDN authority." json:"resolvers,omitempty" toml:"resolvers,omitempty" yaml:"resolvers,omitempty"`
Propagation *Propagation `description:"DNS propagation checks configuration" json:"propagation,omitempty" toml:"propagation,omitempty" yaml:"propagation,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
// Deprecated: please use Propagation.DelayBeforeCheck instead.
DelayBeforeCheck ptypes.Duration `description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty" export:"true"`
// Deprecated: please use Propagation.DisableAllChecks instead.
DisablePropagationCheck bool `description:"(Deprecated) Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty" export:"true"`
}
type Propagation struct {
DisableChecks bool `description:"Disables the challenge TXT record propagation checks (not recommended)." json:"disableChecks,omitempty" toml:"disableChecks,omitempty" yaml:"disableChecks,omitempty" export:"true"`
DisableANSChecks bool `description:"Disables the challenge TXT record propagation checks against authoritative nameservers." json:"disableANSChecks,omitempty" toml:"disableANSChecks,omitempty" yaml:"disableANSChecks,omitempty" export:"true"`
RequireAllRNS bool `description:"Requires the challenge TXT record to be propagated to all recursive nameservers." json:"requireAllRNS,omitempty" toml:"requireAllRNS,omitempty" yaml:"requireAllRNS,omitempty" export:"true"`
DelayBeforeChecks ptypes.Duration `description:"Defines the delay before checking the challenge TXT record propagation." json:"delayBeforeChecks,omitempty" toml:"delayBeforeChecks,omitempty" yaml:"delayBeforeChecks,omitempty" export:"true"`
}
// HTTPChallenge contains HTTP challenge configuration.
@ -137,7 +148,7 @@ func (p *Provider) ListenConfiguration(config dynamic.Configuration) {
p.configFromListenerChan <- config
}
// Init for compatibility reason the BaseProvider implements an empty Init.
// Init inits the provider.
func (p *Provider) Init() error {
logger := log.With().Str(logs.ProviderName, p.ResolverName+resolverSuffix).Logger()
@ -311,11 +322,25 @@ func (p *Provider) getClient() (*lego.Client, error) {
return nil, err
}
err = client.Challenge.SetDNS01Provider(provider,
dns01.CondOption(len(p.DNSChallenge.Resolvers) > 0,
dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers)),
dns01.PropagationWait(time.Duration(p.DNSChallenge.DelayBeforeCheck), p.DNSChallenge.DisablePropagationCheck),
)
var opts []dns01.ChallengeOption
if len(p.DNSChallenge.Resolvers) > 0 {
opts = append(opts, dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers))
}
if p.DNSChallenge.Propagation != nil {
if p.DNSChallenge.Propagation.RequireAllRNS {
opts = append(opts, dns01.RecursiveNSsPropagationRequirement())
}
if p.DNSChallenge.Propagation.DisableANSChecks {
opts = append(opts, dns01.DisableAuthoritativeNssPropagationRequirement())
}
opts = append(opts, dns01.PropagationWait(time.Duration(p.DNSChallenge.Propagation.DelayBeforeChecks), p.DNSChallenge.Propagation.DisableChecks))
}
err = client.Challenge.SetDNS01Provider(provider, opts...)
if err != nil {
return nil, err
}