Move providers out of ACME package.

This commit is contained in:
xenolf 2016-02-29 03:48:41 +01:00
parent 39eef1c2f6
commit b412c67aa6
13 changed files with 93 additions and 49 deletions

View file

@ -234,24 +234,6 @@ func findZoneByFqdn(fqdn, nameserver string) (string, error) {
return "", fmt.Errorf("NS %s did not return the expected SOA record in the authority section", nameserver) return "", fmt.Errorf("NS %s did not return the expected SOA record in the authority section", nameserver)
} }
// toFqdn converts the name into a fqdn appending a trailing dot.
func toFqdn(name string) string {
n := len(name)
if n == 0 || name[n-1] == '.' {
return name
}
return name + "."
}
// unFqdn converts the fqdn into a name removing the trailing dot.
func unFqdn(name string) string {
n := len(name)
if n != 0 && name[n-1] == '.' {
return name[:n-1]
}
return name
}
// clearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. // clearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing.
func clearFqdnCache() { func clearFqdnCache() {
fqdnToZone = map[string]string{} fqdnToZone = map[string]string{}

View file

@ -11,6 +11,11 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"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/rfc2136"
"github.com/xenolf/lego/providers/dns/route53"
) )
func checkFolder(path string) error { func checkFolder(path string) error {
@ -67,23 +72,23 @@ 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 = acme.NewDNSProviderCloudFlare("", "") provider, err = cloudflare.NewDNSProviderCloudFlare("", "")
case "digitalocean": case "digitalocean":
authToken := os.Getenv("DO_AUTH_TOKEN") authToken := os.Getenv("DO_AUTH_TOKEN")
provider, err = acme.NewDNSProviderDigitalOcean(authToken) provider, err = digitalocean.NewDNSProviderDigitalOcean(authToken)
case "dnsimple": case "dnsimple":
provider, err = acme.NewDNSProviderDNSimple("", "") provider, err = dnsimple.NewDNSProviderDNSimple("", "")
case "route53": case "route53":
awsRegion := os.Getenv("AWS_REGION") awsRegion := os.Getenv("AWS_REGION")
provider, err = acme.NewDNSProviderRoute53("", "", awsRegion) provider, err = route53.NewDNSProviderRoute53("", "", awsRegion)
case "rfc2136": case "rfc2136":
nameserver := os.Getenv("RFC2136_NAMESERVER") nameserver := os.Getenv("RFC2136_NAMESERVER")
tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM")
tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigKey := os.Getenv("RFC2136_TSIG_KEY")
tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET")
provider, err = acme.NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret) provider, err = rfc2136.NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret)
case "manual": case "manual":
provider, err = acme.NewDNSProviderManual() provider, err = acme.NewDNSProviderManual()
} }

View file

@ -1,4 +1,4 @@
package acme package cloudflare
import ( import (
"bytes" "bytes"
@ -9,6 +9,9 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/providers/dns"
) )
// CloudFlareAPIURL represents the API endpoint to call. // CloudFlareAPIURL represents the API endpoint to call.
@ -39,7 +42,7 @@ func NewDNSProviderCloudFlare(cloudflareEmail, cloudflareKey string) (*DNSProvid
// Present creates a TXT record to fulfil the dns-01 challenge // Present creates a TXT record to fulfil the dns-01 challenge
func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error {
fqdn, value, _ := DNS01Record(domain, keyAuth) fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn) zoneID, err := c.getHostedZoneID(fqdn)
if err != nil { if err != nil {
return err return err
@ -47,7 +50,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error {
rec := cloudFlareRecord{ rec := cloudFlareRecord{
Type: "TXT", Type: "TXT",
Name: unFqdn(fqdn), Name: dns.UnFqdn(fqdn),
Content: value, Content: value,
TTL: 120, TTL: 120,
} }
@ -67,7 +70,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error {
// CleanUp removes the TXT record matching the specified parameters // CleanUp removes the TXT record matching the specified parameters
func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error { func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := DNS01Record(domain, keyAuth) fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
record, err := c.findTxtRecord(fqdn) record, err := c.findTxtRecord(fqdn)
if err != nil { if err != nil {
@ -102,7 +105,7 @@ func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) {
var hostedZone HostedZone var hostedZone HostedZone
for _, zone := range zones { for _, zone := range zones {
name := toFqdn(zone.Name) name := dns.ToFqdn(zone.Name)
if strings.HasSuffix(fqdn, name) { if strings.HasSuffix(fqdn, name) {
if len(zone.Name) > len(hostedZone.Name) { if len(zone.Name) > len(hostedZone.Name) {
hostedZone = zone hostedZone = zone
@ -134,7 +137,7 @@ func (c *DNSProviderCloudFlare) findTxtRecord(fqdn string) (*cloudFlareRecord, e
} }
for _, rec := range records { for _, rec := range records {
if rec.Name == unFqdn(fqdn) && rec.Type == "TXT" { if rec.Name == dns.UnFqdn(fqdn) && rec.Type == "TXT" {
return &rec, nil return &rec, nil
} }
} }
@ -163,7 +166,7 @@ func (c *DNSProviderCloudFlare) makeRequest(method, uri string, body io.Reader)
req.Header.Set("X-Auth-Email", c.authEmail) req.Header.Set("X-Auth-Email", c.authEmail)
req.Header.Set("X-Auth-Key", c.authKey) req.Header.Set("X-Auth-Key", c.authKey)
req.Header.Set("User-Agent", userAgent()) //req.Header.Set("User-Agent", userAgent())
client := http.Client{Timeout: 30 * time.Second} client := http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req) resp, err := client.Do(req)

View file

@ -1,4 +1,4 @@
package acme package cloudflare
import ( import (
"os" "os"

View file

@ -1,4 +1,4 @@
package acme package digitalocean
import ( import (
"bytes" "bytes"
@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"sync" "sync"
"github.com/xenolf/lego/acme"
) )
// DNSProviderDigitalOcean is an implementation of the DNSProvider interface // DNSProviderDigitalOcean is an implementation of the DNSProvider interface
@ -45,7 +47,7 @@ func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error {
} `json:"domain_record"` } `json:"domain_record"`
} }
fqdn, value, _ := DNS01Record(domain, keyAuth) fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, domain) reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, domain)
reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value} reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value}
@ -88,7 +90,7 @@ func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error {
// CleanUp removes the TXT record matching the specified parameters // CleanUp removes the TXT record matching the specified parameters
func (d *DNSProviderDigitalOcean) CleanUp(domain, token, keyAuth string) error { func (d *DNSProviderDigitalOcean) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := DNS01Record(domain, keyAuth) fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
// get the record's unique ID from when we created it // get the record's unique ID from when we created it
d.recordIDsMu.Lock() d.recordIDsMu.Lock()

View file

@ -1,4 +1,4 @@
package acme package digitalocean
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package acme package dnsimple
import ( import (
"fmt" "fmt"
@ -6,6 +6,8 @@ import (
"strings" "strings"
"github.com/weppos/dnsimple-go/dnsimple" "github.com/weppos/dnsimple-go/dnsimple"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/providers/dns"
) )
// DNSProviderDNSimple is an implementation of the DNSProvider interface. // DNSProviderDNSimple is an implementation of the DNSProvider interface.
@ -33,7 +35,7 @@ func NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProviderD
// Present creates a TXT record to fulfil the dns-01 challenge. // Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error { func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
zoneID, zoneName, err := c.getHostedZone(domain) zoneID, zoneName, err := c.getHostedZone(domain)
if err != nil { if err != nil {
@ -51,7 +53,7 @@ func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error {
// CleanUp removes the TXT record matching the specified parameters. // CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProviderDNSimple) CleanUp(domain, token, keyAuth string) error { func (c *DNSProviderDNSimple) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := DNS01Record(domain, keyAuth) fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
records, err := c.findTxtRecords(domain, fqdn) records, err := c.findTxtRecords(domain, fqdn)
if err != nil { if err != nil {
@ -122,7 +124,7 @@ func (c *DNSProviderDNSimple) newTxtRecord(zone, fqdn, value string, ttl int) *d
} }
func (c *DNSProviderDNSimple) extractRecordName(fqdn, domain string) string { func (c *DNSProviderDNSimple) extractRecordName(fqdn, domain string) string {
name := unFqdn(fqdn) name := dns.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 { if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx] return name[:idx]
} }

View file

@ -1,4 +1,4 @@
package acme package dnsimple
import ( import (
"os" "os"

View file

@ -1,4 +1,4 @@
package acme package rfc2136
import ( import (
"fmt" "fmt"
@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/xenolf/lego/acme"
) )
// DNSProviderRFC2136 is an implementation of the ChallengeProvider interface that // DNSProviderRFC2136 is an implementation of the ChallengeProvider interface that
@ -47,13 +48,13 @@ func NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret string
// Present creates a TXT record using the specified parameters // Present creates a TXT record using the specified parameters
func (r *DNSProviderRFC2136) Present(domain, token, keyAuth string) error { func (r *DNSProviderRFC2136) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
return r.changeRecord("INSERT", fqdn, value, ttl) return r.changeRecord("INSERT", fqdn, value, ttl)
} }
// CleanUp removes the TXT record matching the specified parameters // CleanUp removes the TXT record matching the specified parameters
func (r *DNSProviderRFC2136) CleanUp(domain, token, keyAuth string) error { func (r *DNSProviderRFC2136) CleanUp(domain, token, keyAuth string) error {
fqdn, value, ttl := DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
return r.changeRecord("REMOVE", fqdn, value, ttl) return r.changeRecord("REMOVE", fqdn, value, ttl)
} }

View file

@ -1,4 +1,4 @@
package acme package rfc2136
import ( import (
"bytes" "bytes"

View file

@ -1,4 +1,4 @@
package acme package route53
import ( import (
"fmt" "fmt"
@ -7,6 +7,8 @@ import (
"github.com/mitchellh/goamz/aws" "github.com/mitchellh/goamz/aws"
"github.com/mitchellh/goamz/route53" "github.com/mitchellh/goamz/route53"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/providers/dns"
) )
// DNSProviderRoute53 is an implementation of the DNSProvider interface // DNSProviderRoute53 is an implementation of the DNSProvider interface
@ -43,14 +45,14 @@ func NewDNSProviderRoute53(awsAccessKey, awsSecretKey, awsRegionName string) (*D
// Present creates a TXT record using the specified parameters // Present creates a TXT record using the specified parameters
func (r *DNSProviderRoute53) Present(domain, token, keyAuth string) error { func (r *DNSProviderRoute53) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
value = `"` + value + `"` value = `"` + value + `"`
return r.changeRecord("UPSERT", fqdn, value, ttl) return r.changeRecord("UPSERT", fqdn, value, ttl)
} }
// CleanUp removes the TXT record matching the specified parameters // CleanUp removes the TXT record matching the specified parameters
func (r *DNSProviderRoute53) CleanUp(domain, token, keyAuth string) error { func (r *DNSProviderRoute53) CleanUp(domain, token, keyAuth string) error {
fqdn, value, ttl := DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
value = `"` + value + `"` value = `"` + value + `"`
return r.changeRecord("DELETE", fqdn, value, ttl) return r.changeRecord("DELETE", fqdn, value, ttl)
} }
@ -69,7 +71,7 @@ func (r *DNSProviderRoute53) changeRecord(action, fqdn, value string, ttl int) e
return err return err
} }
return waitFor(90, 5, func() (bool, error) { return dns.WaitFor(90, 5, func() (bool, error) {
status, err := r.client.GetChange(resp.ChangeInfo.ID) status, err := r.client.GetChange(resp.ChangeInfo.ID)
if err != nil { if err != nil {
return false, err return false, err

View file

@ -1,4 +1,4 @@
package acme package route53
import ( import (
"net/http" "net/http"

47
providers/dns/utils.go Normal file
View file

@ -0,0 +1,47 @@
package dns
import (
"fmt"
"time"
)
// ToFqdn converts the name into a fqdn appending a trailing dot.
func ToFqdn(name string) string {
n := len(name)
if n == 0 || name[n-1] == '.' {
return name
}
return name + "."
}
// UnFqdn converts the fqdn into a name removing the trailing dot.
func UnFqdn(name string) string {
n := len(name)
if n != 0 && name[n-1] == '.' {
return name[:n-1]
}
return name
}
// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds.
func WaitFor(timeout, interval int, f func() (bool, error)) error {
var lastErr string
timeup := time.After(time.Duration(timeout) * time.Second)
for {
select {
case <-timeup:
return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr)
default:
}
stop, err := f()
if stop {
return nil
}
if err != nil {
lastErr = err.Error()
}
time.Sleep(time.Duration(interval) * time.Second)
}
}