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