dode.go raw
1 // Package dode implements a DNS provider for solving the DNS-01 challenge using do.de.
2 package dode
3
4 import (
5 "context"
6 "errors"
7 "fmt"
8 "net/http"
9 "time"
10
11 "github.com/go-acme/lego/v4/challenge"
12 "github.com/go-acme/lego/v4/challenge/dns01"
13 "github.com/go-acme/lego/v4/platform/config/env"
14 "github.com/go-acme/lego/v4/providers/dns/dode/internal"
15 "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
16 )
17
18 // Environment variables names.
19 const (
20 envNamespace = "DODE_"
21
22 EnvToken = envNamespace + "TOKEN"
23
24 EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
25 EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
26 EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
27 EnvSequenceInterval = envNamespace + "SEQUENCE_INTERVAL"
28 )
29
30 var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
31
32 // Config is used to configure the creation of the DNSProvider.
33 type Config struct {
34 Token string
35 PropagationTimeout time.Duration
36 PollingInterval time.Duration
37 SequenceInterval time.Duration
38 HTTPClient *http.Client
39 }
40
41 // NewDefaultConfig returns a default configuration for the DNSProvider.
42 func NewDefaultConfig() *Config {
43 return &Config{
44 PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
45 PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
46 SequenceInterval: env.GetOrDefaultSecond(EnvSequenceInterval, dns01.DefaultPropagationTimeout),
47 HTTPClient: &http.Client{
48 Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
49 },
50 }
51 }
52
53 // DNSProvider implements the challenge.Provider interface.
54 type DNSProvider struct {
55 config *Config
56 client *internal.Client
57 }
58
59 // NewDNSProvider returns a new DNS provider using
60 // environment variable DODE_TOKEN for adding and removing the DNS record.
61 func NewDNSProvider() (*DNSProvider, error) {
62 values, err := env.Get(EnvToken)
63 if err != nil {
64 return nil, fmt.Errorf("do.de: %w", err)
65 }
66
67 config := NewDefaultConfig()
68 config.Token = values[EnvToken]
69
70 return NewDNSProviderConfig(config)
71 }
72
73 // NewDNSProviderConfig return a DNSProvider instance configured for do.de.
74 func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
75 if config == nil {
76 return nil, errors.New("do.de: the configuration of the DNS provider is nil")
77 }
78
79 if config.Token == "" {
80 return nil, errors.New("do.de: credentials missing")
81 }
82
83 client := internal.NewClient(config.Token)
84
85 if config.HTTPClient != nil {
86 client.HTTPClient = config.HTTPClient
87 }
88
89 client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
90
91 return &DNSProvider{config: config, client: client}, nil
92 }
93
94 // Present creates a TXT record to fulfill the dns-01 challenge.
95 func (d *DNSProvider) Present(domain, token, keyAuth string) error {
96 info := dns01.GetChallengeInfo(domain, keyAuth)
97 return d.client.UpdateTxtRecord(context.Background(), info.EffectiveFQDN, info.Value, false)
98 }
99
100 // CleanUp clears TXT record.
101 func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
102 info := dns01.GetChallengeInfo(domain, keyAuth)
103 return d.client.UpdateTxtRecord(context.Background(), info.EffectiveFQDN, "", true)
104 }
105
106 // Timeout returns the timeout and interval to use when checking for DNS propagation.
107 // Adjusting here to cope with spikes in propagation times.
108 func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
109 return d.config.PropagationTimeout, d.config.PollingInterval
110 }
111
112 // Sequential All DNS challenges for this provider will be resolved sequentially.
113 // Returns the interval between each iteration.
114 func (d *DNSProvider) Sequential() time.Duration {
115 return d.config.SequenceInterval
116 }
117