Make DNS provider credential-handling more consistent.

Different DNS providers were handling credentials in different ways.
Some were reading credential environment variables in cli_handlers.go
and then passing them into the NewDNSProvider function, while others
were reading the environment variables within their NewDNSProvider
functions.

This change replaces each DNS challenge's NewDNSProvider function with
two new functions: (1) a NewDNSProvider function that takes no
parameters and uses the environment to read credentials, and (2) a
NewDNSProviderCredentials that takes credentials as parameters.
This commit is contained in:
Brett Vickers 2016-03-17 13:59:15 -07:00
parent 43c55a690f
commit 47219adc00
18 changed files with 214 additions and 160 deletions

View file

@ -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 {

View file

@ -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"`

View file

@ -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==")

View file

@ -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),

View file

@ -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")
}

View file

@ -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
}

View file

@ -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==")

View file

@ -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,

View file

@ -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==")

View file

@ -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")
}

View file

@ -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 {

View file

@ -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")
}

View file

@ -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==")

View file

@ -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
}

View file

@ -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") {

View file

@ -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 {

View file

@ -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
}

View file

@ -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")
}