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/cloudflare"
"github.com/xenolf/lego/providers/dns/digitalocean" "github.com/xenolf/lego/providers/dns/digitalocean"
"github.com/xenolf/lego/providers/dns/dnsimple" "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/gandi"
"github.com/xenolf/lego/providers/dns/googlecloud" "github.com/xenolf/lego/providers/dns/googlecloud"
"github.com/xenolf/lego/providers/dns/namecheap" "github.com/xenolf/lego/providers/dns/namecheap"
"github.com/xenolf/lego/providers/dns/rfc2136" "github.com/xenolf/lego/providers/dns/rfc2136"
"github.com/xenolf/lego/providers/dns/route53" "github.com/xenolf/lego/providers/dns/route53"
"github.com/xenolf/lego/providers/dns/dyn"
"github.com/xenolf/lego/providers/http/webroot" "github.com/xenolf/lego/providers/http/webroot"
) )
@ -89,38 +89,25 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
var provider acme.ChallengeProvider var provider acme.ChallengeProvider
switch c.GlobalString("dns") { switch c.GlobalString("dns") {
case "cloudflare": case "cloudflare":
provider, err = cloudflare.NewDNSProvider("", "") provider, err = cloudflare.NewDNSProvider()
case "digitalocean": case "digitalocean":
authToken := os.Getenv("DO_AUTH_TOKEN") provider, err = digitalocean.NewDNSProvider()
provider, err = digitalocean.NewDNSProvider(authToken)
case "dnsimple": case "dnsimple":
provider, err = dnsimple.NewDNSProvider("", "") 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)
case "dyn": case "dyn":
dynCustomerName := os.Getenv("DYN_CUSTOMER_NAME") provider, err = dyn.NewDNSProvider()
dynUserName := os.Getenv("DYN_USER_NAME") case "gandi":
dynPassword := os.Getenv("DYN_PASSWORD") provider, err = gandi.NewDNSProvider()
case "gcloud":
provider, err = dyn.NewDNSProvider(dynCustomerName, dynUserName, dynPassword) provider, err = googlecloud.NewDNSProvider()
case "manual": case "manual":
provider, err = acme.NewDNSProviderManual() 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 { 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 package cloudflare
import ( import (
@ -24,19 +25,25 @@ type DNSProvider struct {
authKey string authKey string
} }
// NewDNSProvider returns a DNSProvider instance with a configured cloudflare client. // NewDNSProvider returns a DNSProvider instance configured for cloudflare.
// Credentials can either be passed as arguments or through CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY env vars. // Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL
func NewDNSProvider(cloudflareEmail, cloudflareKey string) (*DNSProvider, error) { // and CLOUDFLARE_API_KEY.
if cloudflareEmail == "" || cloudflareKey == "" { func NewDNSProvider() (*DNSProvider, error) {
cloudflareEmail, cloudflareKey = cloudflareEnvAuth() email := os.Getenv("CLOUDFLARE_EMAIL")
if cloudflareEmail == "" || cloudflareKey == "" { key := os.Getenv("CLOUDFLARE_API_KEY")
return nil, fmt.Errorf("CloudFlare credentials missing") 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{ return &DNSProvider{
authEmail: cloudflareEmail, authEmail: email,
authKey: cloudflareKey, authKey: key,
}, nil }, nil
} }
@ -192,15 +199,6 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
return r.Result, nil 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 // cloudFlareRecord represents a CloudFlare DNS record
type cloudFlareRecord struct { type cloudFlareRecord struct {
Name string `json:"name"` Name string `json:"name"`

View file

@ -32,7 +32,7 @@ func restoreCloudFlareEnv() {
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_EMAIL", "")
os.Setenv("CLOUDFLARE_API_KEY", "") os.Setenv("CLOUDFLARE_API_KEY", "")
_, err := NewDNSProvider("123", "123") _, err := NewDNSProviderCredentials("123", "123")
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudFlareEnv() restoreCloudFlareEnv()
} }
@ -40,7 +40,7 @@ func TestNewDNSProviderValid(t *testing.T) {
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("CLOUDFLARE_EMAIL", "test@example.com") os.Setenv("CLOUDFLARE_EMAIL", "test@example.com")
os.Setenv("CLOUDFLARE_API_KEY", "123") os.Setenv("CLOUDFLARE_API_KEY", "123")
_, err := NewDNSProvider("", "") _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudFlareEnv() restoreCloudFlareEnv()
} }
@ -48,7 +48,7 @@ func TestNewDNSProviderValidEnv(t *testing.T) {
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_EMAIL", "")
os.Setenv("CLOUDFLARE_API_KEY", "") os.Setenv("CLOUDFLARE_API_KEY", "")
_, err := NewDNSProvider("", "") _, err := NewDNSProvider()
assert.EqualError(t, err, "CloudFlare credentials missing") assert.EqualError(t, err, "CloudFlare credentials missing")
restoreCloudFlareEnv() restoreCloudFlareEnv()
} }
@ -58,7 +58,7 @@ func TestCloudFlarePresent(t *testing.T) {
t.Skip("skipping live test") t.Skip("skipping live test")
} }
provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.Present(cflareDomain, "", "123d==") err = provider.Present(cflareDomain, "", "123d==")
@ -72,7 +72,7 @@ func TestCloudFlareCleanUp(t *testing.T) {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.CleanUp(cflareDomain, "", "123d==") 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 package digitalocean
import ( import (
@ -6,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"sync" "sync"
"time" "time"
@ -20,10 +22,20 @@ type DNSProvider struct {
recordIDsMu sync.Mutex recordIDsMu sync.Mutex
} }
// NewDNSProvider returns a new DNSProvider instance. // NewDNSProvider returns a DNSProvider instance configured for Digital
// apiAuthToken is the personal access token created in the DigitalOcean account // Ocean. Credentials must be passed in the environment variable:
// control panel, and it will be sent in bearer authorization headers. // DO_AUTH_TOKEN.
func NewDNSProvider(apiAuthToken string) (*DNSProvider, error) { 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{ return &DNSProvider{
apiAuthToken: apiAuthToken, apiAuthToken: apiAuthToken,
recordIDs: make(map[string]int), recordIDs: make(map[string]int),

View file

@ -53,7 +53,7 @@ func TestDigitalOceanPresent(t *testing.T) {
defer mock.Close() defer mock.Close()
digitalOceanBaseURL = mock.URL digitalOceanBaseURL = mock.URL
doprov, err := NewDNSProvider(fakeDigitalOceanAuth) doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth)
if doprov == nil { if doprov == nil {
t.Fatal("Expected non-nil DigitalOcean provider, but was nil") t.Fatal("Expected non-nil DigitalOcean provider, but was nil")
} }
@ -95,7 +95,7 @@ func TestDigitalOceanCleanUp(t *testing.T) {
defer mock.Close() defer mock.Close()
digitalOceanBaseURL = mock.URL digitalOceanBaseURL = mock.URL
doprov, err := NewDNSProvider(fakeDigitalOceanAuth) doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth)
if doprov == nil { if doprov == nil {
t.Fatal("Expected non-nil DigitalOcean provider, but was 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 package dnsimple
import ( import (
@ -15,22 +16,25 @@ type DNSProvider struct {
client *dnsimple.Client client *dnsimple.Client
} }
// NewDNSProvider returns a DNSProvider instance with a configured dnsimple client. // NewDNSProvider returns a DNSProvider instance configured for dnsimple.
// Authentication is either done using the passed credentials or - when empty - using the environment // Credentials must be passed in the environment variables: DNSIMPLE_EMAIL
// variables DNSIMPLE_EMAIL and DNSIMPLE_API_KEY. // and DNSIMPLE_API_KEY.
func NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
if dnsimpleEmail == "" || dnsimpleAPIKey == "" { email := os.Getenv("DNSIMPLE_EMAIL")
dnsimpleEmail, dnsimpleAPIKey = dnsimpleEnvAuth() key := os.Getenv("DNSIMPLE_API_KEY")
if dnsimpleEmail == "" || dnsimpleAPIKey == "" { return NewDNSProviderCredentials(email, key)
return nil, fmt.Errorf("DNSimple credentials missing") }
}
// 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{ return &DNSProvider{
client: dnsimple.NewClient(dnsimpleAPIKey, dnsimpleEmail), client: dnsimple.NewClient(key, email),
} }, nil
return c, nil
} }
// Present creates a TXT record to fulfil the dns-01 challenge. // 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 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) { func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_EMAIL", "")
os.Setenv("DNSIMPLE_API_KEY", "") os.Setenv("DNSIMPLE_API_KEY", "")
_, err := NewDNSProvider("example@example.com", "123") _, err := NewDNSProviderCredentials("example@example.com", "123")
assert.NoError(t, err) assert.NoError(t, err)
restoreDNSimpleEnv() restoreDNSimpleEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("DNSIMPLE_EMAIL", "example@example.com") os.Setenv("DNSIMPLE_EMAIL", "example@example.com")
os.Setenv("DNSIMPLE_API_KEY", "123") os.Setenv("DNSIMPLE_API_KEY", "123")
_, err := NewDNSProvider("", "") _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreDNSimpleEnv() restoreDNSimpleEnv()
} }
@ -47,7 +47,7 @@ func TestNewDNSProviderValidEnv(t *testing.T) {
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_EMAIL", "")
os.Setenv("DNSIMPLE_API_KEY", "") os.Setenv("DNSIMPLE_API_KEY", "")
_, err := NewDNSProvider("", "") _, err := NewDNSProvider()
assert.EqualError(t, err, "DNSimple credentials missing") assert.EqualError(t, err, "DNSimple credentials missing")
restoreDNSimpleEnv() restoreDNSimpleEnv()
} }
@ -57,7 +57,7 @@ func TestLiveDNSimplePresent(t *testing.T) {
t.Skip("skipping live test") t.Skip("skipping live test")
} }
provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) provider, err := NewDNSProviderCredentials(dnsimpleEmail, dnsimpleAPIKey)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.Present(dnsimpleDomain, "", "123d==") err = provider.Present(dnsimpleDomain, "", "123d==")
@ -71,7 +71,7 @@ func TestLiveDNSimpleCleanUp(t *testing.T) {
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) provider, err := NewDNSProviderCredentials(dnsimpleEmail, dnsimpleAPIKey)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.CleanUp(dnsimpleDomain, "", "123d==") 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 package dyn
import ( import (
@ -6,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strconv" "strconv"
"time" "time"
@ -37,10 +39,23 @@ type DNSProvider struct {
token string token string
} }
// NewDNSProvider returns a new DNSProvider instance. customerName is // NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
// the customer name of the Dyn account. userName is the user name. password is // Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME,
// the password. // DYN_USER_NAME and DYN_PASSWORD.
func NewDNSProvider(customerName, userName, password string) (*DNSProvider, error) { 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{ return &DNSProvider{
customerName: customerName, customerName: customerName,
userName: userName, userName: userName,

View file

@ -31,7 +31,7 @@ func TestLiveDynPresent(t *testing.T) {
t.Skip("skipping live test") t.Skip("skipping live test")
} }
provider, err := NewDNSProvider(dynCustomerName, dynUserName, dynPassword) provider, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
err = provider.Present(dynDomain, "", "123d==") err = provider.Present(dynDomain, "", "123d==")
@ -45,7 +45,7 @@ func TestLiveDynCleanUp(t *testing.T) {
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
provider, err := NewDNSProvider(dynCustomerName, dynUserName, dynPassword) provider, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
err = provider.CleanUp(dynDomain, "", "123d==") err = provider.CleanUp(dynDomain, "", "123d==")

View file

@ -9,6 +9,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -35,9 +36,16 @@ type DNSProvider struct {
inProgressMu sync.Mutex inProgressMu sync.Mutex
} }
// NewDNSProvider returns a new DNSProvider instance. apiKey is the // NewDNSProvider returns a DNSProvider instance configured for Gandi.
// API access key obtained from the Gandi account control panel. // Credentials must be passed in the environment variable: GANDI_API_KEY.
func NewDNSProvider(apiKey string) (*DNSProvider, error) { 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 == "" { if apiKey == "" {
return nil, fmt.Errorf("No Gandi API Key given") return nil, fmt.Errorf("No Gandi API Key given")
} }

View file

@ -1,4 +1,4 @@
package gandi_test package gandi
import ( import (
"io" "io"
@ -8,8 +8,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"github.com/xenolf/lego/providers/dns/gandi"
) )
// TestDNSProvider runs Present and CleanUp against a fake Gandi RPC // TestDNSProvider runs Present and CleanUp against a fake Gandi RPC
@ -17,7 +15,7 @@ import (
func TestDNSProvider(t *testing.T) { func TestDNSProvider(t *testing.T) {
fakeAPIKey := "123412341234123412341234" fakeAPIKey := "123412341234123412341234"
fakeKeyAuth := "XXXX" fakeKeyAuth := "XXXX"
provider, err := gandi.NewDNSProvider(fakeAPIKey) provider, err := NewDNSProviderCredentials(fakeAPIKey)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -47,11 +45,11 @@ func TestDNSProvider(t *testing.T) {
})) }))
defer fakeServer.Close() defer fakeServer.Close()
// override gandi endpoint to point to fake server // override gandi endpoint to point to fake server
savedEndpoint := gandi.Endpoint savedEndpoint := Endpoint
defer func() { defer func() {
gandi.Endpoint = savedEndpoint Endpoint = savedEndpoint
}() }()
gandi.Endpoint = fakeServer.URL + "/" Endpoint = fakeServer.URL + "/"
// run Present // run Present
err = provider.Present("abc.def.example.com", "", fakeKeyAuth) err = provider.Present("abc.def.example.com", "", fakeKeyAuth)
if err != nil { if err != nil {

View file

@ -22,12 +22,16 @@ type DNSProvider struct {
client *dns.Service client *dns.Service
} }
// NewDNSProvider returns a DNSProvider instance with a configured gcloud client. // NewDNSProvider returns a DNSProvider instance configured for Google Cloud
// Authentication is done using the local account credentials managed by the gcloud utility. // DNS. Credentials must be passed in the environment variable: GCE_PROJECT.
func NewDNSProvider(project string) (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
if project == "" { project := os.Getenv("GCE_PROJECT")
project = gcloudEnvAuth() 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 == "" { if project == "" {
return nil, fmt.Errorf("Google Cloud project name missing") 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 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)") t.Skip("skipping live test (requires credentials)")
} }
os.Setenv("GCE_PROJECT", "") os.Setenv("GCE_PROJECT", "")
_, err := NewDNSProvider("my-project") _, err := NewDNSProviderCredentials("my-project")
assert.NoError(t, err) assert.NoError(t, err)
restoreGCloudEnv() restoreGCloudEnv()
} }
@ -46,14 +46,14 @@ func TestNewDNSProviderValidEnv(t *testing.T) {
t.Skip("skipping live test (requires credentials)") t.Skip("skipping live test (requires credentials)")
} }
os.Setenv("GCE_PROJECT", "my-project") os.Setenv("GCE_PROJECT", "my-project")
_, err := NewDNSProvider("") _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreGCloudEnv() restoreGCloudEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("GCE_PROJECT", "") os.Setenv("GCE_PROJECT", "")
_, err := NewDNSProvider("") _, err := NewDNSProvider()
assert.EqualError(t, err, "Google Cloud project name missing") assert.EqualError(t, err, "Google Cloud project name missing")
restoreGCloudEnv() restoreGCloudEnv()
} }
@ -63,7 +63,7 @@ func TestLiveGoogleCloudPresent(t *testing.T) {
t.Skip("skipping live test") t.Skip("skipping live test")
} }
provider, err := NewDNSProvider(gcloudProject) provider, err := NewDNSProviderCredentials(gcloudProject)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.Present(gcloudDomain, "", "123d==") err = provider.Present(gcloudDomain, "", "123d==")
@ -77,7 +77,7 @@ func TestLiveGoogleCloudCleanUp(t *testing.T) {
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
provider, err := NewDNSProvider(gcloudProject) provider, err := NewDNSProviderCredentials(gcloudProject)
assert.NoError(t, err) assert.NoError(t, err)
err = provider.CleanUp(gcloudDomain, "", "123d==") err = provider.CleanUp(gcloudDomain, "", "123d==")

View file

@ -44,15 +44,20 @@ type DNSProvider struct {
clientIP string clientIP string
} }
// NewDNSProvider returns a new DNSProvider instance. apiUser is the namecheap // NewDNSProvider returns a DNSProvider instance configured for namecheap.
// API user's account name, and apiKey is the account's API access key. // Credentials must be passed in the environment variables: NAMECHEAP_API_USER
func NewDNSProvider(apiUser, apiKey string) (*DNSProvider, error) { // 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 == "" { if apiUser == "" || apiKey == "" {
apiUser = os.Getenv("NAMECHEAP_API_USER") return nil, fmt.Errorf("Namecheap credentials missing")
apiKey = os.Getenv("NAMECHEAP_API_KEY")
if apiUser == "" || apiKey == "" {
return nil, fmt.Errorf("Namecheap credentials missing")
}
} }
clientIP, err := getClientIP() clientIP, err := getClientIP()
@ -68,8 +73,9 @@ func NewDNSProvider(apiUser, apiKey string) (*DNSProvider, error) {
}, nil }, nil
} }
// Timeout : Namecheap can sometimes take a long time to complete an update, so wait // Timeout returns the timeout and interval to use when checking for DNS
// up to 60 minutes for the update to propagate. // 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) { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return 60 * time.Minute, 15 * time.Second 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 package rfc2136
import ( import (
"fmt" "fmt"
"net" "net"
"os"
"strings" "strings"
"time" "time"
@ -20,10 +22,29 @@ type DNSProvider struct {
tsigSecret string tsigSecret string
} }
// NewDNSProvider returns a new DNSProvider instance. // NewDNSProvider returns a DNSProvider instance configured for rfc2136
// To disable TSIG authentication 'tsigAlgorithm, 'tsigKey' and 'tsigSecret' must be set to the empty string. // dynamic update. Credentials must be passed in the environment variables:
// 'nameserver' must be a network address in the the form "host" or "host:port". // RFC2136_NAMESERVER, RFC2136_TSIG_ALGORITHM, RFC2136_TSIG_KEY and
func NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProvider, error) { // 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. // Append the default DNS port if none is specified.
if _, _, err := net.SplitHostPort(nameserver); err != nil { if _, _, err := net.SplitHostPort(nameserver); err != nil {
if strings.Contains(err.Error(), "missing port") { if strings.Contains(err.Error(), "missing port") {

View file

@ -61,9 +61,9 @@ func TestRFC2136ServerSuccess(t *testing.T) {
} }
defer server.Shutdown() defer server.Shutdown()
provider, err := NewDNSProvider(addrstr, "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "")
if err != nil { 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 { if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil {
t.Errorf("Expected Present() to return no error but the error was -> %v", err) 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() defer server.Shutdown()
provider, err := NewDNSProvider(addrstr, "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "")
if err != nil { 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 { if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil {
t.Errorf("Expected Present() to return an error but it did not.") t.Errorf("Expected Present() to return an error but it did not.")
@ -103,9 +103,9 @@ func TestRFC2136TsigClient(t *testing.T) {
} }
defer server.Shutdown() defer server.Shutdown()
provider, err := NewDNSProvider(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret) provider, err := NewDNSProviderCredentials(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret)
if err != nil { 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 { if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil {
t.Errorf("Expected Present() to return no error but the error was -> %v", err) 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) t.Fatalf("Error packing expect msg: %v", err)
} }
provider, err := NewDNSProvider(addrstr, "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "")
if err != nil { 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 { 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 package route53
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -16,25 +18,35 @@ type DNSProvider struct {
client *route53.Route53 client *route53.Route53
} }
// NewDNSProvider returns a DNSProvider instance with a configured route53 client. // NewDNSProvider returns a DNSProvider instance configured for the AWS
// Authentication is either done using the passed credentials or - when empty - falling back to // route53 service. The AWS region name must be passed in the environment
// the customary AWS credential mechanisms, including the file referenced by $AWS_CREDENTIAL_FILE // variable AWS_REGION.
// (defaulting to $HOME/.aws/credentials) optionally scoped to $AWS_PROFILE, credentials func NewDNSProvider() (*DNSProvider, error) {
// supplied by the environment variables AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY [ + AWS_SECURITY_TOKEN ], regionName := os.Getenv("AWS_REGION")
// and finally credentials available via the EC2 instance metadata service. return NewDNSProviderCredentials("", "", regionName)
func NewDNSProvider(awsAccessKey, awsSecretKey, awsRegionName string) (*DNSProvider, error) { }
region, ok := aws.Regions[awsRegionName]
// 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 { 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: // 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_PROFILE / AWS_CREDENTIAL_FILE, if provided
// - uses AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY and optionally AWS_SECURITY_TOKEN, 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 // - uses EC2 instance metadata credentials (http://169.254.169.254/latest/meta-data/…), if available
// ...and otherwise returns an error // ...and otherwise returns an error
auth, err := aws.GetAuth(awsAccessKey, awsSecretKey) auth, err := aws.GetAuth(accessKey, secretKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -100,7 +100,8 @@ func makeRoute53Provider(server *testutil.HTTPServer) *DNSProvider {
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("AWS_ACCESS_KEY_ID", "") os.Setenv("AWS_ACCESS_KEY_ID", "")
os.Setenv("AWS_SECRET_ACCESS_KEY", "") 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) assert.NoError(t, err)
restoreRoute53Env() restoreRoute53Env()
} }
@ -108,7 +109,8 @@ func TestNewDNSProviderValid(t *testing.T) {
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("AWS_ACCESS_KEY_ID", "123") os.Setenv("AWS_ACCESS_KEY_ID", "123")
os.Setenv("AWS_SECRET_ACCESS_KEY", "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) assert.NoError(t, err)
restoreRoute53Env() restoreRoute53Env()
} }
@ -124,7 +126,7 @@ func TestNewDNSProviderMissingAuthErr(t *testing.T) {
awsClient := aws.RetryingClient awsClient := aws.RetryingClient
aws.RetryingClient = &http.Client{Timeout: time.Millisecond} 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") assert.EqualError(t, err, "No valid AWS authentication found")
restoreRoute53Env() restoreRoute53Env()
@ -133,7 +135,7 @@ func TestNewDNSProviderMissingAuthErr(t *testing.T) {
} }
func TestNewDNSProviderInvalidRegionErr(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") assert.EqualError(t, err, "Invalid AWS region name us-east-3")
} }