Merge pull request #157 from beevik/dns-credential-handling

Make DNS provider credential-handling more consistent.
This commit is contained in:
xenolf 2016-03-21 19:15:12 +01:00
commit 4d8e4d3ec1
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")
}