hetzner.go raw
1 // Package hetzner implements a DNS provider for solving the DNS-01 challenge using Hetzner DNS.
2 package hetzner
3
4 import (
5 "errors"
6 "net/http"
7 "time"
8
9 "github.com/go-acme/lego/v4/challenge"
10 "github.com/go-acme/lego/v4/challenge/dns01"
11 "github.com/go-acme/lego/v4/log"
12 "github.com/go-acme/lego/v4/platform/config/env"
13 "github.com/go-acme/lego/v4/providers/dns/hetzner/internal/hetznerv1"
14 "github.com/go-acme/lego/v4/providers/dns/hetzner/internal/legacy"
15 )
16
17 // Environment variables names.
18 const (
19 // Deprecated: use EnvAPIToken instead.
20 EnvAPIKey = legacy.EnvAPIKey
21 EnvAPIToken = hetznerv1.EnvAPIToken
22
23 EnvTTL = hetznerv1.EnvTTL
24 EnvPropagationTimeout = hetznerv1.EnvPropagationTimeout
25 EnvPollingInterval = hetznerv1.EnvPollingInterval
26 EnvHTTPTimeout = hetznerv1.EnvHTTPTimeout
27 )
28
29 const minTTL = 60
30
31 var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
32
33 // Config is used to configure the creation of the DNSProvider.
34 type Config struct {
35 // Deprecated: use APIToken instead
36 APIKey string
37
38 APIToken string
39
40 PropagationTimeout time.Duration
41 PollingInterval time.Duration
42 TTL int
43 HTTPClient *http.Client
44 }
45
46 // NewDefaultConfig returns a default configuration for the DNSProvider.
47 func NewDefaultConfig() *Config {
48 return &Config{
49 TTL: env.GetOrDefaultInt(EnvTTL, minTTL),
50 PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 120*time.Second),
51 PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
52 HTTPClient: &http.Client{
53 Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
54 },
55 }
56 }
57
58 // DNSProvider implements the challenge.Provider interface.
59 type DNSProvider struct {
60 provider challenge.ProviderTimeout
61 }
62
63 // NewDNSProvider returns a DNSProvider instance configured for hetzner.
64 func NewDNSProvider() (*DNSProvider, error) {
65 foundAPIToken := env.GetOrFile(EnvAPIToken) != ""
66 foundAPIKey := env.GetOrFile(EnvAPIKey) != ""
67
68 switch {
69 case foundAPIToken:
70 provider, err := hetznerv1.NewDNSProvider()
71 if err != nil {
72 return nil, err
73 }
74
75 return &DNSProvider{provider: provider}, nil
76
77 case foundAPIKey:
78 log.Warnf("APIKey (legacy Hetzner DNS API) is deprecated, please use APIToken (Hetzner Cloud API) instead.")
79
80 provider, err := legacy.NewDNSProvider()
81 if err != nil {
82 return nil, err
83 }
84
85 return &DNSProvider{provider: provider}, nil
86
87 default:
88 provider, err := hetznerv1.NewDNSProvider()
89 if err != nil {
90 return nil, err
91 }
92
93 return &DNSProvider{provider: provider}, nil
94 }
95 }
96
97 // NewDNSProviderConfig return a DNSProvider instance configured for hetzner.
98 func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
99 if config == nil {
100 return nil, errors.New("hetzner: the configuration of the DNS provider is nil")
101 }
102
103 switch {
104 case config.APIToken != "":
105 cfg := &hetznerv1.Config{
106 APIToken: config.APIToken,
107 PropagationTimeout: config.PropagationTimeout,
108 PollingInterval: config.PollingInterval,
109 TTL: config.TTL,
110 HTTPClient: config.HTTPClient,
111 }
112
113 provider, err := hetznerv1.NewDNSProviderConfig(cfg)
114 if err != nil {
115 return nil, err
116 }
117
118 return &DNSProvider{provider: provider}, nil
119
120 case config.APIKey != "":
121 log.Warnf("%s (legacy Hetzner DNS API) is deprecated, please use %s (Hetzner Cloud API) instead.", EnvAPIKey, EnvAPIToken)
122
123 cfg := &legacy.Config{
124 APIKey: config.APIKey,
125 PropagationTimeout: config.PropagationTimeout,
126 PollingInterval: config.PollingInterval,
127 TTL: config.TTL,
128 HTTPClient: config.HTTPClient,
129 }
130
131 provider, err := legacy.NewDNSProviderConfig(cfg)
132 if err != nil {
133 return nil, err
134 }
135
136 return &DNSProvider{provider: provider}, nil
137 }
138
139 return nil, errors.New("hetzner: credentials missing")
140 }
141
142 // Timeout returns the timeout and interval to use when checking for DNS propagation.
143 // Adjusting here to cope with spikes in propagation times.
144 func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
145 return d.provider.Timeout()
146 }
147
148 // Present creates a TXT record to fulfill the dns-01 challenge.
149 func (d *DNSProvider) Present(domain, token, keyAuth string) error {
150 return d.provider.Present(domain, token, keyAuth)
151 }
152
153 // CleanUp removes the TXT record matching the specified parameters.
154 func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
155 return d.provider.CleanUp(domain, token, keyAuth)
156 }
157