internetbs.go raw
1 // Package internetbs implements a DNS provider for solving the DNS-01 challenge using internet.bs.
2 package internetbs
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/internal/clientdebug"
15 "github.com/go-acme/lego/v4/providers/dns/internetbs/internal"
16 )
17
18 // Environment variables names.
19 const (
20 envNamespace = "INTERNET_BS_"
21
22 EnvAPIKey = envNamespace + "API_KEY"
23 EnvPassword = envNamespace + "PASSWORD"
24
25 EnvTTL = envNamespace + "TTL"
26 EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
27 EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
28 EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
29 )
30
31 var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
32
33 // Config is used to configure the creation of the DNSProvider.
34 type Config struct {
35 APIKey string
36 Password string
37 PropagationTimeout time.Duration
38 PollingInterval time.Duration
39 TTL int
40 HTTPClient *http.Client
41 }
42
43 // NewDefaultConfig returns a default configuration for the DNSProvider.
44 func NewDefaultConfig() *Config {
45 return &Config{
46 TTL: env.GetOrDefaultInt(EnvTTL, 3600),
47 PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
48 PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
49 HTTPClient: &http.Client{
50 Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
51 },
52 }
53 }
54
55 // DNSProvider implements the challenge.Provider interface.
56 type DNSProvider struct {
57 config *Config
58 client *internal.Client
59 }
60
61 // NewDNSProvider returns a DNSProvider instance configured for internet.bs.
62 // Credentials must be passed in the environment variables: INTERNET_BS_API_KEY, INTERNET_BS_PASSWORD.
63 func NewDNSProvider() (*DNSProvider, error) {
64 values, err := env.Get(EnvAPIKey, EnvPassword)
65 if err != nil {
66 return nil, fmt.Errorf("internetbs: %w", err)
67 }
68
69 config := NewDefaultConfig()
70 config.APIKey = values[EnvAPIKey]
71 config.Password = values[EnvPassword]
72
73 return NewDNSProviderConfig(config)
74 }
75
76 // NewDNSProviderConfig return a DNSProvider instance configured for internet.bs.
77 func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
78 if config == nil {
79 return nil, errors.New("internetbs: the configuration of the DNS provider is nil")
80 }
81
82 if config.APIKey == "" || config.Password == "" {
83 return nil, errors.New("internetbs: missing credentials")
84 }
85
86 client := internal.NewClient(config.APIKey, config.Password)
87
88 if config.HTTPClient != nil {
89 client.HTTPClient = config.HTTPClient
90 }
91
92 client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
93
94 return &DNSProvider{
95 config: config,
96 client: client,
97 }, nil
98 }
99
100 // Timeout returns the timeout and interval to use when checking for DNS propagation.
101 // Adjusting here to cope with spikes in propagation times.
102 func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
103 return d.config.PropagationTimeout, d.config.PollingInterval
104 }
105
106 // Present creates a TXT record using the specified parameters.
107 func (d *DNSProvider) Present(domain, token, keyAuth string) error {
108 info := dns01.GetChallengeInfo(domain, keyAuth)
109
110 query := internal.RecordQuery{
111 FullRecordName: dns01.UnFqdn(info.EffectiveFQDN),
112 Type: "TXT",
113 Value: info.Value,
114 TTL: d.config.TTL,
115 }
116
117 err := d.client.AddRecord(context.Background(), query)
118 if err != nil {
119 return fmt.Errorf("internetbs: %w", err)
120 }
121
122 return nil
123 }
124
125 // CleanUp removes the TXT record matching the specified parameters.
126 func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
127 info := dns01.GetChallengeInfo(domain, keyAuth)
128
129 query := internal.RecordQuery{
130 FullRecordName: dns01.UnFqdn(info.EffectiveFQDN),
131 Type: "TXT",
132 Value: info.Value,
133 TTL: d.config.TTL,
134 }
135
136 err := d.client.RemoveRecord(context.Background(), query)
137 if err != nil {
138 return fmt.Errorf("internetbs: %w", err)
139 }
140
141 return nil
142 }
143