mydnsjp.go raw
1 // Package mydnsjp implements a DNS provider for solving the DNS-01 challenge using MyDNS.jp.
2 package mydnsjp
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/mydnsjp/internal"
16 )
17
18 // Environment variables names.
19 const (
20 envNamespace = "MYDNSJP_"
21
22 EnvMasterID = envNamespace + "MASTER_ID"
23 EnvPassword = envNamespace + "PASSWORD"
24
25 EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
26 EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
27 EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
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 MasterID string
35 Password string
36 PropagationTimeout time.Duration
37 PollingInterval 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, 2*time.Minute),
45 PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
46 HTTPClient: &http.Client{
47 Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
48 },
49 }
50 }
51
52 // DNSProvider implements the challenge.Provider interface.
53 type DNSProvider struct {
54 config *Config
55 client *internal.Client
56 }
57
58 // NewDNSProvider returns a DNSProvider instance configured for MyDNS.jp.
59 // Credentials must be passed in the environment variables: MYDNSJP_MASTER_ID and MYDNSJP_PASSWORD.
60 func NewDNSProvider() (*DNSProvider, error) {
61 values, err := env.Get(EnvMasterID, EnvPassword)
62 if err != nil {
63 return nil, fmt.Errorf("mydnsjp: %w", err)
64 }
65
66 config := NewDefaultConfig()
67 config.MasterID = values[EnvMasterID]
68 config.Password = values[EnvPassword]
69
70 return NewDNSProviderConfig(config)
71 }
72
73 // NewDNSProviderConfig return a DNSProvider instance configured for MyDNS.jp.
74 func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
75 if config == nil {
76 return nil, errors.New("mydnsjp: the configuration of the DNS provider is nil")
77 }
78
79 if config.MasterID == "" || config.Password == "" {
80 return nil, errors.New("mydnsjp: some credentials information are missing")
81 }
82
83 client := internal.NewClient(config.MasterID, config.Password)
84
85 if config.HTTPClient != nil {
86 client.HTTPClient = config.HTTPClient
87 }
88
89 client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
90
91 return &DNSProvider{
92 config: config,
93 client: client,
94 }, nil
95 }
96
97 // Timeout returns the timeout and interval to use when checking for DNS propagation.
98 // Adjusting here to cope with spikes in propagation times.
99 func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
100 return d.config.PropagationTimeout, d.config.PollingInterval
101 }
102
103 // Present creates a TXT record to fulfill the dns-01 challenge.
104 func (d *DNSProvider) Present(domain, token, keyAuth string) error {
105 info := dns01.GetChallengeInfo(domain, keyAuth)
106
107 // TODO(ldez) replace domain by FQDN to follow CNAME.
108 err := d.client.AddTXTRecord(context.Background(), domain, info.Value)
109 if err != nil {
110 return fmt.Errorf("mydnsjp: %w", err)
111 }
112
113 return nil
114 }
115
116 // CleanUp removes the TXT record matching the specified parameters.
117 func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
118 info := dns01.GetChallengeInfo(domain, keyAuth)
119
120 // TODO(ldez) replace domain by FQDN to follow CNAME.
121 err := d.client.DeleteTXTRecord(context.Background(), domain, info.Value)
122 if err != nil {
123 return fmt.Errorf("mydnsjp: %w", err)
124 }
125
126 return nil
127 }
128