hurricane.go raw

   1  package hurricane
   2  
   3  import (
   4  	"context"
   5  	"errors"
   6  	"fmt"
   7  	"net/http"
   8  	"time"
   9  
  10  	"github.com/go-acme/lego/v4/challenge"
  11  	"github.com/go-acme/lego/v4/challenge/dns01"
  12  	"github.com/go-acme/lego/v4/platform/config/env"
  13  	"github.com/go-acme/lego/v4/providers/dns/hurricane/internal"
  14  	"github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
  15  )
  16  
  17  // Environment variables names.
  18  const (
  19  	envNamespace = "HURRICANE_"
  20  
  21  	EnvTokens = envNamespace + "TOKENS"
  22  
  23  	EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
  24  	EnvPollingInterval    = envNamespace + "POLLING_INTERVAL"
  25  	EnvHTTPTimeout        = envNamespace + "HTTP_TIMEOUT"
  26  	EnvSequenceInterval   = envNamespace + "SEQUENCE_INTERVAL"
  27  )
  28  
  29  var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
  30  
  31  // Config is used to configure the creation of the DNSProvider.
  32  type Config struct {
  33  	Credentials        map[string]string
  34  	PropagationTimeout time.Duration
  35  	PollingInterval    time.Duration
  36  	SequenceInterval   time.Duration
  37  	HTTPClient         *http.Client
  38  }
  39  
  40  // NewDefaultConfig returns a default configuration for the DNSProvider.
  41  func NewDefaultConfig() *Config {
  42  	return &Config{
  43  		PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 300*time.Second),
  44  		PollingInterval:    env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
  45  		SequenceInterval:   env.GetOrDefaultSecond(EnvSequenceInterval, dns01.DefaultPropagationTimeout),
  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 Hurricane Electric.
  59  func NewDNSProvider() (*DNSProvider, error) {
  60  	config := NewDefaultConfig()
  61  
  62  	values, err := env.Get(EnvTokens)
  63  	if err != nil {
  64  		return nil, fmt.Errorf("hurricane: %w", err)
  65  	}
  66  
  67  	credentials, err := env.ParsePairs(values[EnvTokens])
  68  	if err != nil {
  69  		return nil, fmt.Errorf("hurricane: credentials: %w", err)
  70  	}
  71  
  72  	config.Credentials = credentials
  73  
  74  	return NewDNSProviderConfig(config)
  75  }
  76  
  77  func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
  78  	if config == nil {
  79  		return nil, errors.New("hurricane: the configuration of the DNS provider is nil")
  80  	}
  81  
  82  	if len(config.Credentials) == 0 {
  83  		return nil, errors.New("hurricane: credentials missing")
  84  	}
  85  
  86  	client := internal.NewClient(config.Credentials)
  87  
  88  	if config.HTTPClient != nil {
  89  		client.HTTPClient = config.HTTPClient
  90  	}
  91  
  92  	client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
  93  
  94  	return &DNSProvider{config: config, client: client}, nil
  95  }
  96  
  97  // Present updates a TXT record to fulfill the dns-01 challenge.
  98  func (d *DNSProvider) Present(domain, _, keyAuth string) error {
  99  	info := dns01.GetChallengeInfo(domain, keyAuth)
 100  
 101  	err := d.client.UpdateTxtRecord(context.Background(), dns01.UnFqdn(info.EffectiveFQDN), info.Value)
 102  	if err != nil {
 103  		return fmt.Errorf("hurricane: %w", err)
 104  	}
 105  
 106  	return nil
 107  }
 108  
 109  // CleanUp updates the TXT record matching the specified parameters.
 110  func (d *DNSProvider) CleanUp(domain, _, keyAuth string) error {
 111  	info := dns01.GetChallengeInfo(domain, keyAuth)
 112  
 113  	err := d.client.UpdateTxtRecord(context.Background(), dns01.UnFqdn(info.EffectiveFQDN), ".")
 114  	if err != nil {
 115  		return fmt.Errorf("hurricane: %w", err)
 116  	}
 117  
 118  	return nil
 119  }
 120  
 121  // Timeout returns the timeout and interval to use when checking for DNS propagation.
 122  // Adjusting here to cope with spikes in propagation times.
 123  func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
 124  	return d.config.PropagationTimeout, d.config.PollingInterval
 125  }
 126  
 127  // Sequential All DNS challenges for this provider will be resolved sequentially.
 128  // Returns the interval between each iteration.
 129  func (d *DNSProvider) Sequential() time.Duration {
 130  	return d.config.SequenceInterval
 131  }
 132