diff --git a/cli_handlers.go b/cli_handlers.go index eea75ede..fe51438d 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -14,12 +14,12 @@ import ( "github.com/xenolf/lego/providers/dns/cloudflare" "github.com/xenolf/lego/providers/dns/digitalocean" "github.com/xenolf/lego/providers/dns/dnsimple" + "github.com/xenolf/lego/providers/dns/dyn" "github.com/xenolf/lego/providers/dns/gandi" "github.com/xenolf/lego/providers/dns/googlecloud" "github.com/xenolf/lego/providers/dns/namecheap" "github.com/xenolf/lego/providers/dns/rfc2136" "github.com/xenolf/lego/providers/dns/route53" - "github.com/xenolf/lego/providers/dns/dyn" "github.com/xenolf/lego/providers/http/webroot" ) @@ -89,38 +89,25 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": - provider, err = cloudflare.NewDNSProvider("", "") + provider, err = cloudflare.NewDNSProvider() case "digitalocean": - authToken := os.Getenv("DO_AUTH_TOKEN") - - provider, err = digitalocean.NewDNSProvider(authToken) + provider, err = digitalocean.NewDNSProvider() case "dnsimple": - provider, err = dnsimple.NewDNSProvider("", "") - case "gandi": - apiKey := os.Getenv("GANDI_API_KEY") - provider, err = gandi.NewDNSProvider(apiKey) - case "gcloud": - provider, err = googlecloud.NewDNSProvider("") - case "namecheap": - provider, err = namecheap.NewDNSProvider("", "") - case "route53": - awsRegion := os.Getenv("AWS_REGION") - provider, err = route53.NewDNSProvider("", "", awsRegion) - case "rfc2136": - nameserver := os.Getenv("RFC2136_NAMESERVER") - tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") - tsigKey := os.Getenv("RFC2136_TSIG_KEY") - tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") - - provider, err = rfc2136.NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret) + provider, err = dnsimple.NewDNSProvider() case "dyn": - dynCustomerName := os.Getenv("DYN_CUSTOMER_NAME") - dynUserName := os.Getenv("DYN_USER_NAME") - dynPassword := os.Getenv("DYN_PASSWORD") - - provider, err = dyn.NewDNSProvider(dynCustomerName, dynUserName, dynPassword) + provider, err = dyn.NewDNSProvider() + case "gandi": + provider, err = gandi.NewDNSProvider() + case "gcloud": + provider, err = googlecloud.NewDNSProvider() case "manual": provider, err = acme.NewDNSProviderManual() + case "namecheap": + provider, err = namecheap.NewDNSProvider() + case "route53": + provider, err = route53.NewDNSProvider() + case "rfc2136": + provider, err = rfc2136.NewDNSProvider() } if err != nil { diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index 307cc4ef..d0067e22 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -1,4 +1,5 @@ -// Package cloudflare implements a DNS provider for solving the DNS-01 challenge using cloudflare DNS. +// Package cloudflare implements a DNS provider for solving the DNS-01 +// challenge using cloudflare DNS. package cloudflare import ( @@ -24,19 +25,25 @@ type DNSProvider struct { authKey string } -// NewDNSProvider returns a DNSProvider instance with a configured cloudflare client. -// Credentials can either be passed as arguments or through CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY env vars. -func NewDNSProvider(cloudflareEmail, cloudflareKey string) (*DNSProvider, error) { - if cloudflareEmail == "" || cloudflareKey == "" { - cloudflareEmail, cloudflareKey = cloudflareEnvAuth() - if cloudflareEmail == "" || cloudflareKey == "" { - return nil, fmt.Errorf("CloudFlare credentials missing") - } +// NewDNSProvider returns a DNSProvider instance configured for cloudflare. +// Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL +// and CLOUDFLARE_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + email := os.Getenv("CLOUDFLARE_EMAIL") + key := os.Getenv("CLOUDFLARE_API_KEY") + return NewDNSProviderCredentials(email, key) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for cloudflare. +func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { + if email == "" || key == "" { + return nil, fmt.Errorf("CloudFlare credentials missing") } return &DNSProvider{ - authEmail: cloudflareEmail, - authKey: cloudflareKey, + authEmail: email, + authKey: key, }, nil } @@ -192,15 +199,6 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return r.Result, nil } -func cloudflareEnvAuth() (email, apiKey string) { - email = os.Getenv("CLOUDFLARE_EMAIL") - apiKey = os.Getenv("CLOUDFLARE_API_KEY") - if len(email) == 0 || len(apiKey) == 0 { - return "", "" - } - return -} - // cloudFlareRecord represents a CloudFlare DNS record type cloudFlareRecord struct { Name string `json:"name"` diff --git a/providers/dns/cloudflare/cloudflare_test.go b/providers/dns/cloudflare/cloudflare_test.go index 63936ce6..19b5a40b 100644 --- a/providers/dns/cloudflare/cloudflare_test.go +++ b/providers/dns/cloudflare/cloudflare_test.go @@ -32,7 +32,7 @@ func restoreCloudFlareEnv() { func TestNewDNSProviderValid(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProvider("123", "123") + _, err := NewDNSProviderCredentials("123", "123") assert.NoError(t, err) restoreCloudFlareEnv() } @@ -40,7 +40,7 @@ func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "test@example.com") os.Setenv("CLOUDFLARE_API_KEY", "123") - _, err := NewDNSProvider("", "") + _, err := NewDNSProvider() assert.NoError(t, err) restoreCloudFlareEnv() } @@ -48,7 +48,7 @@ func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProvider("", "") + _, err := NewDNSProvider() assert.EqualError(t, err, "CloudFlare credentials missing") restoreCloudFlareEnv() } @@ -58,7 +58,7 @@ func TestCloudFlarePresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) + provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey) assert.NoError(t, err) err = provider.Present(cflareDomain, "", "123d==") @@ -72,7 +72,7 @@ func TestCloudFlareCleanUp(t *testing.T) { time.Sleep(time.Second * 2) - provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) + provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey) assert.NoError(t, err) err = provider.CleanUp(cflareDomain, "", "123d==") diff --git a/providers/dns/digitalocean/digitalocean.go b/providers/dns/digitalocean/digitalocean.go index 5aeaf11d..152cf81e 100644 --- a/providers/dns/digitalocean/digitalocean.go +++ b/providers/dns/digitalocean/digitalocean.go @@ -1,4 +1,5 @@ -// Package digitalocean implements a DNS provider for solving the DNS-01 challenge using digitalocean DNS. +// Package digitalocean implements a DNS provider for solving the DNS-01 +// challenge using digitalocean DNS. package digitalocean import ( @@ -6,6 +7,7 @@ import ( "encoding/json" "fmt" "net/http" + "os" "sync" "time" @@ -20,10 +22,20 @@ type DNSProvider struct { recordIDsMu sync.Mutex } -// NewDNSProvider returns a new DNSProvider instance. -// apiAuthToken is the personal access token created in the DigitalOcean account -// control panel, and it will be sent in bearer authorization headers. -func NewDNSProvider(apiAuthToken string) (*DNSProvider, error) { +// NewDNSProvider returns a DNSProvider instance configured for Digital +// Ocean. Credentials must be passed in the environment variable: +// DO_AUTH_TOKEN. +func NewDNSProvider() (*DNSProvider, error) { + apiAuthToken := os.Getenv("DO_AUTH_TOKEN") + return NewDNSProviderCredentials(apiAuthToken) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for Digital Ocean. +func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { + if apiAuthToken == "" { + return nil, fmt.Errorf("DigitalOcean credentials missing") + } return &DNSProvider{ apiAuthToken: apiAuthToken, recordIDs: make(map[string]int), diff --git a/providers/dns/digitalocean/digitalocean_test.go b/providers/dns/digitalocean/digitalocean_test.go index 8fc383a0..7498508b 100644 --- a/providers/dns/digitalocean/digitalocean_test.go +++ b/providers/dns/digitalocean/digitalocean_test.go @@ -53,7 +53,7 @@ func TestDigitalOceanPresent(t *testing.T) { defer mock.Close() digitalOceanBaseURL = mock.URL - doprov, err := NewDNSProvider(fakeDigitalOceanAuth) + doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) if doprov == nil { t.Fatal("Expected non-nil DigitalOcean provider, but was nil") } @@ -95,7 +95,7 @@ func TestDigitalOceanCleanUp(t *testing.T) { defer mock.Close() digitalOceanBaseURL = mock.URL - doprov, err := NewDNSProvider(fakeDigitalOceanAuth) + doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) if doprov == nil { t.Fatal("Expected non-nil DigitalOcean provider, but was nil") } diff --git a/providers/dns/dnsimple/dnsimple.go b/providers/dns/dnsimple/dnsimple.go index cde77298..bb66935d 100644 --- a/providers/dns/dnsimple/dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -1,4 +1,5 @@ -// Package dnsimple implements a DNS provider for solving the DNS-01 challenge using dnsimple DNS. +// Package dnsimple implements a DNS provider for solving the DNS-01 challenge +// using dnsimple DNS. package dnsimple import ( @@ -15,22 +16,25 @@ type DNSProvider struct { client *dnsimple.Client } -// NewDNSProvider returns a DNSProvider instance with a configured dnsimple client. -// Authentication is either done using the passed credentials or - when empty - using the environment -// variables DNSIMPLE_EMAIL and DNSIMPLE_API_KEY. -func NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProvider, error) { - if dnsimpleEmail == "" || dnsimpleAPIKey == "" { - dnsimpleEmail, dnsimpleAPIKey = dnsimpleEnvAuth() - if dnsimpleEmail == "" || dnsimpleAPIKey == "" { - return nil, fmt.Errorf("DNSimple credentials missing") - } +// NewDNSProvider returns a DNSProvider instance configured for dnsimple. +// Credentials must be passed in the environment variables: DNSIMPLE_EMAIL +// and DNSIMPLE_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + email := os.Getenv("DNSIMPLE_EMAIL") + key := os.Getenv("DNSIMPLE_API_KEY") + return NewDNSProviderCredentials(email, key) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for dnsimple. +func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { + if email == "" || key == "" { + return nil, fmt.Errorf("DNSimple credentials missing") } - c := &DNSProvider{ - client: dnsimple.NewClient(dnsimpleAPIKey, dnsimpleEmail), - } - - return c, nil + return &DNSProvider{ + client: dnsimple.NewClient(key, email), + }, nil } // Present creates a TXT record to fulfil the dns-01 challenge. @@ -130,12 +134,3 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string { } return name } - -func dnsimpleEnvAuth() (email, apiKey string) { - email = os.Getenv("DNSIMPLE_EMAIL") - apiKey = os.Getenv("DNSIMPLE_API_KEY") - if len(email) == 0 || len(apiKey) == 0 { - return "", "" - } - return -} diff --git a/providers/dns/dnsimple/dnsimple_test.go b/providers/dns/dnsimple/dnsimple_test.go index 7cb19fea..4926b3df 100644 --- a/providers/dns/dnsimple/dnsimple_test.go +++ b/providers/dns/dnsimple/dnsimple_test.go @@ -32,14 +32,14 @@ func restoreDNSimpleEnv() { func TestNewDNSProviderValid(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_API_KEY", "") - _, err := NewDNSProvider("example@example.com", "123") + _, err := NewDNSProviderCredentials("example@example.com", "123") assert.NoError(t, err) restoreDNSimpleEnv() } func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "example@example.com") os.Setenv("DNSIMPLE_API_KEY", "123") - _, err := NewDNSProvider("", "") + _, err := NewDNSProvider() assert.NoError(t, err) restoreDNSimpleEnv() } @@ -47,7 +47,7 @@ func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_API_KEY", "") - _, err := NewDNSProvider("", "") + _, err := NewDNSProvider() assert.EqualError(t, err, "DNSimple credentials missing") restoreDNSimpleEnv() } @@ -57,7 +57,7 @@ func TestLiveDNSimplePresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) + provider, err := NewDNSProviderCredentials(dnsimpleEmail, dnsimpleAPIKey) assert.NoError(t, err) err = provider.Present(dnsimpleDomain, "", "123d==") @@ -71,7 +71,7 @@ func TestLiveDNSimpleCleanUp(t *testing.T) { time.Sleep(time.Second * 1) - provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) + provider, err := NewDNSProviderCredentials(dnsimpleEmail, dnsimpleAPIKey) assert.NoError(t, err) err = provider.CleanUp(dnsimpleDomain, "", "123d==") diff --git a/providers/dns/dyn/dyn.go b/providers/dns/dyn/dyn.go index a88a6e7a..86daf96c 100644 --- a/providers/dns/dyn/dyn.go +++ b/providers/dns/dyn/dyn.go @@ -1,4 +1,5 @@ -// Package dyn implements a DNS provider for solving the DNS-01 challenge using Dyn Managed DNS. +// Package dyn implements a DNS provider for solving the DNS-01 challenge +// using Dyn Managed DNS. package dyn import ( @@ -6,6 +7,7 @@ import ( "encoding/json" "fmt" "net/http" + "os" "strconv" "time" @@ -37,10 +39,23 @@ type DNSProvider struct { token string } -// NewDNSProvider returns a new DNSProvider instance. customerName is -// the customer name of the Dyn account. userName is the user name. password is -// the password. -func NewDNSProvider(customerName, userName, password string) (*DNSProvider, error) { +// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS. +// Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME, +// DYN_USER_NAME and DYN_PASSWORD. +func NewDNSProvider() (*DNSProvider, error) { + customerName := os.Getenv("DYN_CUSTOMER_NAME") + userName := os.Getenv("DYN_USER_NAME") + password := os.Getenv("DYN_PASSWORD") + return NewDNSProviderCredentials(customerName, userName, password) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for Dyn DNS. +func NewDNSProviderCredentials(customerName, userName, password string) (*DNSProvider, error) { + if customerName == "" || userName == "" || password == "" { + return nil, fmt.Errorf("DynDNS credentials missing") + } + return &DNSProvider{ customerName: customerName, userName: userName, diff --git a/providers/dns/dyn/dyn_test.go b/providers/dns/dyn/dyn_test.go index fcc3d63e..0d28d5d0 100644 --- a/providers/dns/dyn/dyn_test.go +++ b/providers/dns/dyn/dyn_test.go @@ -31,7 +31,7 @@ func TestLiveDynPresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProvider(dynCustomerName, dynUserName, dynPassword) + provider, err := NewDNSProvider() assert.NoError(t, err) err = provider.Present(dynDomain, "", "123d==") @@ -45,7 +45,7 @@ func TestLiveDynCleanUp(t *testing.T) { time.Sleep(time.Second * 1) - provider, err := NewDNSProvider(dynCustomerName, dynUserName, dynPassword) + provider, err := NewDNSProvider() assert.NoError(t, err) err = provider.CleanUp(dynDomain, "", "123d==") diff --git a/providers/dns/gandi/gandi.go b/providers/dns/gandi/gandi.go index fa1f88e6..014a085b 100644 --- a/providers/dns/gandi/gandi.go +++ b/providers/dns/gandi/gandi.go @@ -9,6 +9,7 @@ import ( "io" "io/ioutil" "net/http" + "os" "strings" "sync" "time" @@ -35,9 +36,16 @@ type DNSProvider struct { inProgressMu sync.Mutex } -// NewDNSProvider returns a new DNSProvider instance. apiKey is the -// API access key obtained from the Gandi account control panel. -func NewDNSProvider(apiKey string) (*DNSProvider, error) { +// NewDNSProvider returns a DNSProvider instance configured for Gandi. +// Credentials must be passed in the environment variable: GANDI_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + apiKey := os.Getenv("GANDI_API_KEY") + return NewDNSProviderCredentials(apiKey) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for Gandi. +func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { if apiKey == "" { return nil, fmt.Errorf("No Gandi API Key given") } diff --git a/providers/dns/gandi/gandi_test.go b/providers/dns/gandi/gandi_test.go index 1f44a962..6b3b8d74 100644 --- a/providers/dns/gandi/gandi_test.go +++ b/providers/dns/gandi/gandi_test.go @@ -1,4 +1,4 @@ -package gandi_test +package gandi import ( "io" @@ -8,8 +8,6 @@ import ( "regexp" "strings" "testing" - - "github.com/xenolf/lego/providers/dns/gandi" ) // TestDNSProvider runs Present and CleanUp against a fake Gandi RPC @@ -17,7 +15,7 @@ import ( func TestDNSProvider(t *testing.T) { fakeAPIKey := "123412341234123412341234" fakeKeyAuth := "XXXX" - provider, err := gandi.NewDNSProvider(fakeAPIKey) + provider, err := NewDNSProviderCredentials(fakeAPIKey) if err != nil { t.Fatal(err) } @@ -47,11 +45,11 @@ func TestDNSProvider(t *testing.T) { })) defer fakeServer.Close() // override gandi endpoint to point to fake server - savedEndpoint := gandi.Endpoint + savedEndpoint := Endpoint defer func() { - gandi.Endpoint = savedEndpoint + Endpoint = savedEndpoint }() - gandi.Endpoint = fakeServer.URL + "/" + Endpoint = fakeServer.URL + "/" // run Present err = provider.Present("abc.def.example.com", "", fakeKeyAuth) if err != nil { diff --git a/providers/dns/googlecloud/googlecloud.go b/providers/dns/googlecloud/googlecloud.go index ace3500f..f5026056 100644 --- a/providers/dns/googlecloud/googlecloud.go +++ b/providers/dns/googlecloud/googlecloud.go @@ -22,12 +22,16 @@ type DNSProvider struct { client *dns.Service } -// NewDNSProvider returns a DNSProvider instance with a configured gcloud client. -// Authentication is done using the local account credentials managed by the gcloud utility. -func NewDNSProvider(project string) (*DNSProvider, error) { - if project == "" { - project = gcloudEnvAuth() - } +// NewDNSProvider returns a DNSProvider instance configured for Google Cloud +// DNS. Credentials must be passed in the environment variable: GCE_PROJECT. +func NewDNSProvider() (*DNSProvider, error) { + project := os.Getenv("GCE_PROJECT") + return NewDNSProviderCredentials(project) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for Google Cloud DNS. +func NewDNSProviderCredentials(project string) (*DNSProvider, error) { if project == "" { return nil, fmt.Errorf("Google Cloud project name missing") } @@ -149,7 +153,3 @@ func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSe return found, nil } - -func gcloudEnvAuth() (gcloud string) { - return os.Getenv("GCE_PROJECT") -} diff --git a/providers/dns/googlecloud/googlecloud_test.go b/providers/dns/googlecloud/googlecloud_test.go index 156ad773..d7378816 100644 --- a/providers/dns/googlecloud/googlecloud_test.go +++ b/providers/dns/googlecloud/googlecloud_test.go @@ -36,7 +36,7 @@ func TestNewDNSProviderValid(t *testing.T) { t.Skip("skipping live test (requires credentials)") } os.Setenv("GCE_PROJECT", "") - _, err := NewDNSProvider("my-project") + _, err := NewDNSProviderCredentials("my-project") assert.NoError(t, err) restoreGCloudEnv() } @@ -46,14 +46,14 @@ func TestNewDNSProviderValidEnv(t *testing.T) { t.Skip("skipping live test (requires credentials)") } os.Setenv("GCE_PROJECT", "my-project") - _, err := NewDNSProvider("") + _, err := NewDNSProvider() assert.NoError(t, err) restoreGCloudEnv() } func TestNewDNSProviderMissingCredErr(t *testing.T) { os.Setenv("GCE_PROJECT", "") - _, err := NewDNSProvider("") + _, err := NewDNSProvider() assert.EqualError(t, err, "Google Cloud project name missing") restoreGCloudEnv() } @@ -63,7 +63,7 @@ func TestLiveGoogleCloudPresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProvider(gcloudProject) + provider, err := NewDNSProviderCredentials(gcloudProject) assert.NoError(t, err) err = provider.Present(gcloudDomain, "", "123d==") @@ -77,7 +77,7 @@ func TestLiveGoogleCloudCleanUp(t *testing.T) { time.Sleep(time.Second * 1) - provider, err := NewDNSProvider(gcloudProject) + provider, err := NewDNSProviderCredentials(gcloudProject) assert.NoError(t, err) err = provider.CleanUp(gcloudDomain, "", "123d==") diff --git a/providers/dns/namecheap/namecheap.go b/providers/dns/namecheap/namecheap.go index dea75b65..d7eb4093 100644 --- a/providers/dns/namecheap/namecheap.go +++ b/providers/dns/namecheap/namecheap.go @@ -44,15 +44,20 @@ type DNSProvider struct { clientIP string } -// NewDNSProvider returns a new DNSProvider instance. apiUser is the namecheap -// API user's account name, and apiKey is the account's API access key. -func NewDNSProvider(apiUser, apiKey string) (*DNSProvider, error) { +// NewDNSProvider returns a DNSProvider instance configured for namecheap. +// Credentials must be passed in the environment variables: NAMECHEAP_API_USER +// and NAMECHEAP_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + apiUser := os.Getenv("NAMECHEAP_API_USER") + apiKey := os.Getenv("NAMECHEAP_API_KEY") + return NewDNSProviderCredentials(apiUser, apiKey) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for namecheap. +func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { if apiUser == "" || apiKey == "" { - apiUser = os.Getenv("NAMECHEAP_API_USER") - apiKey = os.Getenv("NAMECHEAP_API_KEY") - if apiUser == "" || apiKey == "" { - return nil, fmt.Errorf("Namecheap credentials missing") - } + return nil, fmt.Errorf("Namecheap credentials missing") } clientIP, err := getClientIP() @@ -68,8 +73,9 @@ func NewDNSProvider(apiUser, apiKey string) (*DNSProvider, error) { }, nil } -// Timeout : Namecheap can sometimes take a long time to complete an update, so wait -// up to 60 minutes for the update to propagate. +// Timeout returns the timeout and interval to use when checking for DNS +// propagation. Namecheap can sometimes take a long time to complete an +// update, so wait up to 60 minutes for the update to propagate. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 60 * time.Minute, 15 * time.Second } diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index 3d32409b..9f597aa2 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -1,9 +1,11 @@ -// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge using the rfc2136 dynamic update. +// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge +// using the rfc2136 dynamic update. package rfc2136 import ( "fmt" "net" + "os" "strings" "time" @@ -20,10 +22,29 @@ type DNSProvider struct { tsigSecret string } -// NewDNSProvider returns a new DNSProvider instance. -// To disable TSIG authentication 'tsigAlgorithm, 'tsigKey' and 'tsigSecret' must be set to the empty string. -// 'nameserver' must be a network address in the the form "host" or "host:port". -func NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProvider, error) { +// NewDNSProvider returns a DNSProvider instance configured for rfc2136 +// dynamic update. Credentials must be passed in the environment variables: +// RFC2136_NAMESERVER, RFC2136_TSIG_ALGORITHM, RFC2136_TSIG_KEY and +// RFC2136_TSIG_SECRET. To disable TSIG authentication, leave the TSIG +// variables unset. RFC2136_NAMESERVER must be a network address in the form +// "host" or "host:port". +func NewDNSProvider() (*DNSProvider, error) { + nameserver := os.Getenv("RFC2136_NAMESERVER") + tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") + tsigKey := os.Getenv("RFC2136_TSIG_KEY") + tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") + return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for rfc2136 dynamic update. To disable TSIG +// authentication, leave the TSIG parameters as empty strings. +// nameserver must be a network address in the form "host" or "host:port". +func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProvider, error) { + if nameserver == "" { + return nil, fmt.Errorf("RFC2136 nameserver missing") + } + // Append the default DNS port if none is specified. if _, _, err := net.SplitHostPort(nameserver); err != nil { if strings.Contains(err.Error(), "missing port") { diff --git a/providers/dns/rfc2136/rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go index 2aa8aa22..a2515e99 100644 --- a/providers/dns/rfc2136/rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -61,9 +61,9 @@ func TestRFC2136ServerSuccess(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProvider(addrstr, "", "", "") + provider, err := NewDNSProviderCredentials(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) @@ -81,9 +81,9 @@ func TestRFC2136ServerError(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProvider(addrstr, "", "", "") + provider, err := NewDNSProviderCredentials(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil { t.Errorf("Expected Present() to return an error but it did not.") @@ -103,9 +103,9 @@ func TestRFC2136TsigClient(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProvider(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret) + provider, err := NewDNSProviderCredentials(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret) if err != nil { - t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) @@ -135,9 +135,9 @@ func TestRFC2136ValidUpdatePacket(t *testing.T) { t.Fatalf("Error packing expect msg: %v", err) } - provider, err := NewDNSProvider(addrstr, "", "", "") + provider, err := NewDNSProviderCredentials(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { diff --git a/providers/dns/route53/route53.go b/providers/dns/route53/route53.go index eb1ffdf3..7e9e131d 100644 --- a/providers/dns/route53/route53.go +++ b/providers/dns/route53/route53.go @@ -1,8 +1,10 @@ -// Package route53 implements a DNS provider for solving the DNS-01 challenge using route53 DNS. +// Package route53 implements a DNS provider for solving the DNS-01 challenge +// using route53 DNS. package route53 import ( "fmt" + "os" "strings" "time" @@ -16,25 +18,35 @@ type DNSProvider struct { client *route53.Route53 } -// NewDNSProvider returns a DNSProvider instance with a configured route53 client. -// Authentication is either done using the passed credentials or - when empty - falling back to -// the customary AWS credential mechanisms, including the file referenced by $AWS_CREDENTIAL_FILE -// (defaulting to $HOME/.aws/credentials) optionally scoped to $AWS_PROFILE, credentials -// supplied by the environment variables AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY [ + AWS_SECURITY_TOKEN ], -// and finally credentials available via the EC2 instance metadata service. -func NewDNSProvider(awsAccessKey, awsSecretKey, awsRegionName string) (*DNSProvider, error) { - region, ok := aws.Regions[awsRegionName] +// NewDNSProvider returns a DNSProvider instance configured for the AWS +// route53 service. The AWS region name must be passed in the environment +// variable AWS_REGION. +func NewDNSProvider() (*DNSProvider, error) { + regionName := os.Getenv("AWS_REGION") + return NewDNSProviderCredentials("", "", regionName) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for the AWS route53 service. Authentication +// is done using the passed credentials or, if empty, falling back to the +// custonmary AWS credential mechanisms, including the file referenced by +// $AWS_CREDENTIAL_FILE (defaulting to $HOME/.aws/credentials) optionally +// scoped to $AWS_PROFILE, credentials supplied by the environment variables +// AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY [ + AWS_SECURITY_TOKEN ], and +// finally credentials available via the EC2 instance metadata service. +func NewDNSProviderCredentials(accessKey, secretKey, regionName string) (*DNSProvider, error) { + region, ok := aws.Regions[regionName] if !ok { - return nil, fmt.Errorf("Invalid AWS region name %s", awsRegionName) + return nil, fmt.Errorf("Invalid AWS region name %s", regionName) } // use aws.GetAuth, which tries really hard to find credentails: - // - uses awsAccessKey and awsSecretKey, if provided + // - uses accessKey and secretKey, if provided // - uses AWS_PROFILE / AWS_CREDENTIAL_FILE, if provided // - uses AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY and optionally AWS_SECURITY_TOKEN, if provided // - uses EC2 instance metadata credentials (http://169.254.169.254/latest/meta-data/…), if available // ...and otherwise returns an error - auth, err := aws.GetAuth(awsAccessKey, awsSecretKey) + auth, err := aws.GetAuth(accessKey, secretKey) if err != nil { return nil, err } diff --git a/providers/dns/route53/route53_test.go b/providers/dns/route53/route53_test.go index 8bb675c3..2b23f960 100644 --- a/providers/dns/route53/route53_test.go +++ b/providers/dns/route53/route53_test.go @@ -100,7 +100,8 @@ func makeRoute53Provider(server *testutil.HTTPServer) *DNSProvider { func TestNewDNSProviderValid(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "") os.Setenv("AWS_SECRET_ACCESS_KEY", "") - _, err := NewDNSProvider("123", "123", "us-east-1") + os.Setenv("AWS_REGION", "") + _, err := NewDNSProviderCredentials("123", "123", "us-east-1") assert.NoError(t, err) restoreRoute53Env() } @@ -108,7 +109,8 @@ func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "123") os.Setenv("AWS_SECRET_ACCESS_KEY", "123") - _, err := NewDNSProvider("", "", "us-east-1") + os.Setenv("AWS_REGION", "us-east-1") + _, err := NewDNSProvider() assert.NoError(t, err) restoreRoute53Env() } @@ -124,7 +126,7 @@ func TestNewDNSProviderMissingAuthErr(t *testing.T) { awsClient := aws.RetryingClient aws.RetryingClient = &http.Client{Timeout: time.Millisecond} - _, err := NewDNSProvider("", "", "us-east-1") + _, err := NewDNSProviderCredentials("", "", "us-east-1") assert.EqualError(t, err, "No valid AWS authentication found") restoreRoute53Env() @@ -133,7 +135,7 @@ func TestNewDNSProviderMissingAuthErr(t *testing.T) { } func TestNewDNSProviderInvalidRegionErr(t *testing.T) { - _, err := NewDNSProvider("123", "123", "us-east-3") + _, err := NewDNSProviderCredentials("123", "123", "us-east-3") assert.EqualError(t, err, "Invalid AWS region name us-east-3") }