zoneedit.go raw
1 // Package zoneedit implements a DNS provider for solving the DNS-01 challenge using ZoneEdit.
2 package zoneedit
3
4 import (
5 "errors"
6 "fmt"
7 "net/http"
8 "time"
9
10 "github.com/go-acme/lego/v4/challenge/dns01"
11 "github.com/go-acme/lego/v4/platform/config/env"
12 "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
13 "github.com/go-acme/lego/v4/providers/dns/zoneedit/internal"
14 )
15
16 // Environment variables names.
17 const (
18 envNamespace = "ZONEEDIT_"
19
20 EnvUser = envNamespace + "USER"
21 EnAuthToken = envNamespace + "AUTH_TOKEN"
22
23 EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
24 EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
25 EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
26 )
27
28 // Config is used to configure the creation of the DNSProvider.
29 type Config struct {
30 User string
31 AuthToken string
32
33 PropagationTimeout time.Duration
34 PollingInterval time.Duration
35 HTTPClient *http.Client
36 }
37
38 // NewDefaultConfig returns a default configuration for the DNSProvider.
39 func NewDefaultConfig() *Config {
40 return &Config{
41 PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
42 PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
43 HTTPClient: &http.Client{
44 Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
45 },
46 }
47 }
48
49 // DNSProvider implements the challenge.Provider interface.
50 type DNSProvider struct {
51 config *Config
52 client *internal.Client
53 }
54
55 // NewDNSProvider returns a DNSProvider instance configured for ZoneEdit.
56 func NewDNSProvider() (*DNSProvider, error) {
57 values, err := env.Get(EnvUser, EnAuthToken)
58 if err != nil {
59 return nil, fmt.Errorf("zoneedit: %w", err)
60 }
61
62 config := NewDefaultConfig()
63 config.User = values[EnvUser]
64 config.AuthToken = values[EnAuthToken]
65
66 return NewDNSProviderConfig(config)
67 }
68
69 // NewDNSProviderConfig return a DNSProvider instance configured for ZoneEdit.
70 func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
71 if config == nil {
72 return nil, errors.New("zoneedit: the configuration of the DNS provider is nil")
73 }
74
75 client, err := internal.NewClient(config.User, config.AuthToken)
76 if err != nil {
77 return nil, fmt.Errorf("zoneedit: %w", err)
78 }
79
80 if config.HTTPClient != nil {
81 client.HTTPClient = config.HTTPClient
82 }
83
84 client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
85
86 return &DNSProvider{
87 config: config,
88 client: client,
89 }, nil
90 }
91
92 // Present creates a TXT record using the specified parameters.
93 func (d *DNSProvider) Present(domain, token, keyAuth string) error {
94 info := dns01.GetChallengeInfo(domain, keyAuth)
95
96 err := d.client.CreateTXTRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value)
97 if err != nil {
98 return fmt.Errorf("zoneedit: create TXT record: %w", err)
99 }
100
101 // ERROR CODE="702" TEXT="Minimum 10 seconds between requests"
102 time.Sleep(11 * time.Second)
103
104 return nil
105 }
106
107 // CleanUp removes the TXT record matching the specified parameters.
108 func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
109 info := dns01.GetChallengeInfo(domain, keyAuth)
110
111 err := d.client.DeleteTXTRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value)
112 if err != nil {
113 return fmt.Errorf("zoneedit: delete TXT record: %w", err)
114 }
115
116 // ERROR CODE="702" TEXT="Minimum 10 seconds between requests"
117 time.Sleep(11 * time.Second)
118
119 return nil
120 }
121
122 // Timeout returns the timeout and interval to use when checking for DNS propagation.
123 // Adjusting here to cope with spikes in propagation times.
124 func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
125 return d.config.PropagationTimeout, d.config.PollingInterval
126 }
127