From a1585a7b9afa8dcd05777b1d0528b87ff2b7c30a Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 21 Jun 2018 19:06:16 +0200 Subject: [PATCH] Review DNS providers (#580) * refactor: create log.Infof and log.Warnf * refactor: review DNS providers. - use one `http.Client` by provider instead of one client by request - use the same receiver name `d` for all `DNSProvider` - use `http.MethodXXX` * refactor: logger init. --- acme/client.go | 44 +++++----- acme/client_test.go | 10 +-- acme/dns_challenge.go | 10 +-- acme/dns_challenge_manual.go | 10 +-- acme/http.go | 6 +- acme/http_challenge.go | 4 +- acme/http_challenge_server.go | 6 +- acme/http_test.go | 6 +- acme/tls_alpn_challenge.go | 4 +- log/logger.go | 32 +++---- providers/dns/auroradns/auroradns.go | 34 ++++---- providers/dns/auroradns/auroradns_test.go | 6 +- providers/dns/azure/azure.go | 36 ++++---- providers/dns/bluecat/bluecat.go | 36 ++++---- providers/dns/cloudflare/cloudflare.go | 65 +++++++------- providers/dns/cloudxns/cloudxns.go | 40 ++++----- providers/dns/digitalocean/digitalocean.go | 25 +++--- providers/dns/dns_providers.go | 69 ++++++++------- providers/dns/dnsimple/dnsimple.go | 44 +++++----- providers/dns/dnsmadeeasy/dnsmadeeasy.go | 27 +++--- providers/dns/dnspod/dnspod.go | 34 ++++---- providers/dns/dyn/dyn.go | 23 ++--- providers/dns/exoscale/exoscale.go | 24 +++--- providers/dns/fastdns/fastdns.go | 26 +++--- providers/dns/gandi/gandi.go | 28 +++---- providers/dns/gandiv5/gandiv5.go | 16 ++-- providers/dns/gcloud/googlecloud.go | 30 +++---- providers/dns/glesys/glesys.go | 27 +++--- providers/dns/godaddy/godaddy.go | 40 +++++---- providers/dns/lightsail/lightsail.go | 14 ++-- providers/dns/linode/linode.go | 12 +-- providers/dns/linode/linode_test.go | 12 +-- providers/dns/namecheap/namecheap.go | 22 +++-- providers/dns/namecheap/namecheap_test.go | 7 +- providers/dns/namedotcom/namedotcom.go | 18 ++-- providers/dns/ns1/ns1.go | 20 ++--- providers/dns/otc/mock.go | 10 +-- providers/dns/otc/otc.go | 10 +-- providers/dns/ovh/ovh.go | 9 +- providers/dns/pdns/pdns.go | 86 +++++++++++-------- providers/dns/rackspace/rackspace.go | 98 ++++++++++++---------- providers/dns/vultr/vultr.go | 42 +++++----- 42 files changed, 573 insertions(+), 549 deletions(-) diff --git a/acme/client.go b/acme/client.go index 6e4c9d5f..ebfd11a2 100644 --- a/acme/client.go +++ b/acme/client.go @@ -164,7 +164,7 @@ func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) { if c == nil || c.user == nil { return nil, errors.New("acme: cannot register a nil client or user") } - log.Printf("[INFO] acme: Registering account for %s", c.user.GetEmail()) + log.Infof("acme: Registering account for %s", c.user.GetEmail()) accMsg := accountMessage{} if c.user.GetEmail() != "" { @@ -198,7 +198,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, if c == nil || c.user == nil { return nil, errors.New("acme: cannot register a nil client or user") } - log.Printf("[INFO] acme: Registering account (EAB) for %s", c.user.GetEmail()) + log.Infof("acme: Registering account (EAB) for %s", c.user.GetEmail()) accMsg := accountMessage{} if c.user.GetEmail() != "" { @@ -244,7 +244,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, // ResolveAccountByKey will attempt to look up an account using the given account key // and return its registration resource. func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) { - log.Printf("[INFO] acme: Trying to resolve account by key") + log.Infof("acme: Trying to resolve account by key") acc := accountMessage{OnlyReturnExisting: true} hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil) @@ -273,7 +273,7 @@ func (c *Client) DeleteRegistration() error { if c == nil || c.user == nil { return errors.New("acme: cannot unregister a nil client or user") } - log.Printf("[INFO] acme: Deleting account for %s", c.user.GetEmail()) + log.Infof("acme: Deleting account for %s", c.user.GetEmail()) accMsg := accountMessage{ Status: "deactivated", @@ -293,7 +293,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) { return nil, errors.New("acme: cannot query the registration of a nil client or user") } // Log the URL here instead of the email as the email may not be set - log.Printf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI) + log.Infof("acme: Querying account for %s", c.user.GetRegistration().URI) accMsg := accountMessage{} @@ -339,9 +339,9 @@ DNSNames: } if bundle { - log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) } else { - log.Printf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) } order, err := c.createOrderForIdentifiers(domains) @@ -363,7 +363,7 @@ DNSNames: return nil, err } - log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) failures := make(ObtainError) cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil) @@ -399,9 +399,9 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto } if bundle { - log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) } else { - log.Printf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) } order, err := c.createOrderForIdentifiers(domains) @@ -423,7 +423,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto return nil, err } - log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) failures := make(ObtainError) cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple) @@ -482,7 +482,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b // This is just meant to be informal for the user. timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) + log.Infof("[%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) // We always need to request a new certificate to renew. // Start by checking to see if the certificate was based off a CSR, and @@ -556,7 +556,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error { for _, authz := range authorizations { if authz.Status == "valid" { // Boulder might recycle recent validated authz (see issue #267) - log.Printf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) + log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) continue } @@ -587,7 +587,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) { if solver, ok := c.solvers[Challenge(challenge.Type)]; ok { return i, solver } - log.Printf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type) + log.Infof("[%s] acme: Could not find solver for: %s", domain, challenge.Type) } return 0, nil } @@ -639,7 +639,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) func logAuthz(order orderResource) { for i, auth := range order.Authorizations { - log.Printf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth) + log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) } } @@ -756,7 +756,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou if err != nil { // If we fail to acquire the issuer cert, return the issued certificate - do not fail. - log.Printf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) + log.Warnf("[%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) } else { issuerCert = pemEncode(derCertificateBytes(issuerCert)) @@ -773,21 +773,21 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou certRes.Certificate = cert certRes.CertURL = order.Certificate certRes.CertStableURL = order.Certificate - log.Printf("[INFO][%s] Server responded with a certificate.", certRes.Domain) + log.Infof("[%s] Server responded with a certificate.", certRes.Domain) return true, nil case "processing": return false, nil case "invalid": - return false, errors.New("Order has invalid state: invalid") + return false, errors.New("order has invalid state: invalid") + default: + return false, nil } - - return false, nil } // getIssuerCertificate requests the issuer certificate func (c *Client) getIssuerCertificate(url string) ([]byte, error) { - log.Printf("[INFO] acme: Requesting issuer cert from %s", url) + log.Infof("acme: Requesting issuer cert from %s", url) resp, err := httpGet(url) if err != nil { return nil, err @@ -841,7 +841,7 @@ func validate(j *jws, domain, uri string, c challenge) error { for { switch chlng.Status { case "valid": - log.Printf("[INFO][%s] The server validated our request", domain) + log.Infof("[%s] The server validated our request", domain) return nil case "pending": case "processing": diff --git a/acme/client_test.go b/acme/client_test.go index b37d1bdf..a84b6f30 100644 --- a/acme/client_test.go +++ b/acme/client_test.go @@ -152,13 +152,13 @@ func TestValidate(t *testing.T) { w.Header().Add("Replay-Nonce", "12345") w.Header().Add("Retry-After", "0") switch r.Method { - case "HEAD": - case "POST": + case http.MethodHead: + case http.MethodPost: st := statuses[0] statuses = statuses[1:] writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"}) - case "GET": + case http.MethodGet: st := statuses[0] statuses = statuses[1:] writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"}) @@ -199,7 +199,7 @@ func TestGetChallenges(t *testing.T) { var ts *httptest.Server ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { - case "GET", "HEAD": + case http.MethodGet, http.MethodHead: w.Header().Add("Replay-Nonce", "12345") w.Header().Add("Retry-After", "0") writeJSONResponse(w, directory{ @@ -209,7 +209,7 @@ func TestGetChallenges(t *testing.T) { RevokeCertURL: ts.URL, KeyChangeURL: ts.URL, }) - case "POST": + case http.MethodPost: writeJSONResponse(w, orderMessage{}) } })) diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index d494501c..c8a35eb8 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -72,10 +72,10 @@ type dnsChallenge struct { } func (s *dnsChallenge) Solve(chlng challenge, domain string) error { - log.Printf("[INFO][%s] acme: Trying to solve DNS-01", domain) + log.Infof("[%s] acme: Trying to solve DNS-01", domain) if s.provider == nil { - return errors.New("No DNS Provider configured") + return errors.New("no DNS Provider configured") } // Generate the Key Authorization for the challenge @@ -86,18 +86,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { err = s.provider.Present(domain, chlng.Token, keyAuth) if err != nil { - return fmt.Errorf("Error presenting token: %s", err) + return fmt.Errorf("error presenting token: %s", err) } defer func() { err := s.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Printf("Error cleaning up %s: %v ", domain, err) + log.Warnf("Error cleaning up %s: %v ", domain, err) } }() fqdn, value, _ := DNS01Record(domain, keyAuth) - log.Printf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) + log.Infof("[%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) var timeout, interval time.Duration switch provider := s.provider.(type) { diff --git a/acme/dns_challenge_manual.go b/acme/dns_challenge_manual.go index cd4c3c8a..ca94fcac 100644 --- a/acme/dns_challenge_manual.go +++ b/acme/dns_challenge_manual.go @@ -30,9 +30,9 @@ func (*DNSProviderManual) Present(domain, token, keyAuth string) error { return err } - log.Printf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone) - log.Printf("[INFO] acme: %s", dnsRecord) - log.Printf("[INFO] acme: Press 'Enter' when you are done") + log.Infof("acme: Please create the following TXT record in your %s zone:", authZone) + log.Infof("acme: %s", dnsRecord) + log.Infof("acme: Press 'Enter' when you are done") reader := bufio.NewReader(os.Stdin) _, _ = reader.ReadString('\n') @@ -49,7 +49,7 @@ func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error { return err } - log.Printf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone) - log.Printf("[INFO] acme: %s", dnsRecord) + log.Infof("acme: You can now remove this TXT record from your %s zone:", authZone) + log.Infof("acme: %s", dnsRecord) return nil } diff --git a/acme/http.go b/acme/http.go index 66caa728..e65a2382 100644 --- a/acme/http.go +++ b/acme/http.go @@ -80,7 +80,7 @@ func initCertPool() *x509.CertPool { // httpHead performs a HEAD request with a proper User-Agent string. // The response body (resp.Body) is already closed when this function returns. func httpHead(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("HEAD", url, nil) + req, err := http.NewRequest(http.MethodHead, url, nil) if err != nil { return nil, fmt.Errorf("failed to head %q: %v", url, err) } @@ -98,7 +98,7 @@ func httpHead(url string) (resp *http.Response, err error) { // httpPost performs a POST request with a proper User-Agent string. // Callers should close resp.Body when done reading from it. func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) { - req, err := http.NewRequest("POST", url, body) + req, err := http.NewRequest(http.MethodPost, url, body) if err != nil { return nil, fmt.Errorf("failed to post %q: %v", url, err) } @@ -111,7 +111,7 @@ func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, // httpGet performs a GET request with a proper User-Agent string. // Callers should close resp.Body when done reading from it. func httpGet(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("failed to get %q: %v", url, err) } diff --git a/acme/http_challenge.go b/acme/http_challenge.go index 7659bfc5..77a8edd4 100644 --- a/acme/http_challenge.go +++ b/acme/http_challenge.go @@ -19,7 +19,7 @@ func HTTP01ChallengePath(token string) string { func (s *httpChallenge) Solve(chlng challenge, domain string) error { - log.Printf("[INFO][%s] acme: Trying to solve HTTP-01", domain) + log.Infof("[%s] acme: Trying to solve HTTP-01", domain) // Generate the Key Authorization for the challenge keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) @@ -34,7 +34,7 @@ func (s *httpChallenge) Solve(chlng challenge, domain string) error { defer func() { err := s.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Printf("[%s] error cleaning up: %v", domain, err) + log.Warnf("[%s] error cleaning up: %v", domain, err) } }() diff --git a/acme/http_challenge_server.go b/acme/http_challenge_server.go index 214a278f..319e2618 100644 --- a/acme/http_challenge_server.go +++ b/acme/http_challenge_server.go @@ -60,12 +60,12 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { // For validation it then writes the token the server returned with the challenge mux := http.NewServeMux() mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, domain) && r.Method == "GET" { + if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet { w.Header().Add("Content-Type", "text/plain") w.Write([]byte(keyAuth)) - log.Printf("[INFO][%s] Served key authentication", domain) + log.Infof("[%s] Served key authentication", domain) } else { - log.Printf("[WARN] Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) + log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) w.Write([]byte("TEST")) } }) diff --git a/acme/http_test.go b/acme/http_test.go index 9b88e8a7..370e1245 100644 --- a/acme/http_test.go +++ b/acme/http_test.go @@ -22,7 +22,7 @@ func TestHTTPHeadUserAgent(t *testing.T) { t.Fatal(err) } - if method != "HEAD" { + if method != http.MethodHead { t.Errorf("Expected method to be HEAD, got %s", method) } if !strings.Contains(ua, ourUserAgent) { @@ -44,7 +44,7 @@ func TestHTTPGetUserAgent(t *testing.T) { } res.Body.Close() - if method != "GET" { + if method != http.MethodGet { t.Errorf("Expected method to be GET, got %s", method) } if !strings.Contains(ua, ourUserAgent) { @@ -66,7 +66,7 @@ func TestHTTPPostUserAgent(t *testing.T) { } res.Body.Close() - if method != "POST" { + if method != http.MethodPost { t.Errorf("Expected method to be POST, got %s", method) } if !strings.Contains(ua, ourUserAgent) { diff --git a/acme/tls_alpn_challenge.go b/acme/tls_alpn_challenge.go index 397d0709..d8035199 100644 --- a/acme/tls_alpn_challenge.go +++ b/acme/tls_alpn_challenge.go @@ -23,7 +23,7 @@ type tlsALPNChallenge struct { // Solve manages the provider to validate and solve the challenge. func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error { - log.Printf("[INFO][%s] acme: Trying to solve TLS-ALPN-01", domain) + log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", domain) // Generate the Key Authorization for the challenge keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey) @@ -38,7 +38,7 @@ func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error { defer func() { err := t.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Printf("[%s] error cleaning up: %v", domain, err) + log.Warnf("[%s] error cleaning up: %v", domain, err) } }() diff --git a/log/logger.go b/log/logger.go index 291541c0..101a2c99 100644 --- a/log/logger.go +++ b/log/logger.go @@ -6,54 +6,44 @@ import ( ) // Logger is an optional custom logger. -var Logger *log.Logger +var Logger = log.New(os.Stdout, "", log.LstdFlags) // Fatal writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Fatal(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stderr, "", log.LstdFlags) - } - Logger.Fatal(args...) } // Fatalf writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Fatalf(format string, args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stderr, "", log.LstdFlags) - } - Logger.Fatalf(format, args...) } // Print writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Print(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Print(args...) } // Println writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Println(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Println(args...) } // Printf writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Printf(format string, args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Printf(format, args...) } + +// Warnf writes a log entry. +func Warnf(format string, args ...interface{}) { + Printf("[WARN] "+format, args...) +} + +// Infof writes a log entry. +func Infof(format string, args ...interface{}) { + Printf("[INFO] "+format, args...) +} diff --git a/providers/dns/auroradns/auroradns.go b/providers/dns/auroradns/auroradns.go index d7c09603..5c821507 100644 --- a/providers/dns/auroradns/auroradns.go +++ b/providers/dns/auroradns/auroradns.go @@ -51,8 +51,8 @@ func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSP }, nil } -func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { - zs, err := provider.client.GetZones() +func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { + zs, err := d.client.GetZones() if err != nil { return zones.ZoneRecord{}, err @@ -68,7 +68,7 @@ func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRe } // Present creates a record with a secret -func (provider *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) @@ -87,7 +87,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) - zoneRecord, err := provider.getZoneInformationByName(authZone) + zoneRecord, err := d.getZoneInformationByName(authZone) if err != nil { return fmt.Errorf("could not create record: %v", err) } @@ -100,25 +100,25 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error { TTL: 300, } - respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData) + respData, err := d.client.CreateRecord(zoneRecord.ID, reqData) if err != nil { return fmt.Errorf("could not create record: %v", err) } - provider.recordIDsMu.Lock() - provider.recordIDs[fqdn] = respData.ID - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + d.recordIDs[fqdn] = respData.ID + d.recordIDsMu.Unlock() return nil } // CleanUp removes a given record that was generated by Present -func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - provider.recordIDsMu.Lock() - recordID, ok := provider.recordIDs[fqdn] - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + recordID, ok := d.recordIDs[fqdn] + d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown recordID for %q", fqdn) @@ -131,19 +131,19 @@ func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) - zoneRecord, err := provider.getZoneInformationByName(authZone) + zoneRecord, err := d.getZoneInformationByName(authZone) if err != nil { return err } - _, err = provider.client.RemoveRecord(zoneRecord.ID, recordID) + _, err = d.client.RemoveRecord(zoneRecord.ID, recordID) if err != nil { return err } - provider.recordIDsMu.Lock() - delete(provider.recordIDs, fqdn) - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + delete(d.recordIDs, fqdn) + d.recordIDsMu.Unlock() return nil } diff --git a/providers/dns/auroradns/auroradns_test.go b/providers/dns/auroradns/auroradns_test.go index d8ffd82b..2c41ccbd 100644 --- a/providers/dns/auroradns/auroradns_test.go +++ b/providers/dns/auroradns/auroradns_test.go @@ -18,7 +18,7 @@ func TestAuroraDNSPresent(t *testing.T) { var requestReceived bool mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && r.URL.Path == "/zones" { + if r.Method == http.MethodGet && r.URL.Path == "/zones" { w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `[{ "id": "c56a4180-65aa-42ec-a945-5fd21dec0538", @@ -62,7 +62,7 @@ func TestAuroraDNSCleanUp(t *testing.T) { var requestReceived bool mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && r.URL.Path == "/zones" { + if r.Method == http.MethodGet && r.URL.Path == "/zones" { w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `[{ "id": "c56a4180-65aa-42ec-a945-5fd21dec0538", @@ -71,7 +71,7 @@ func TestAuroraDNSCleanUp(t *testing.T) { return } - if r.Method == "POST" && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" { + if r.Method == http.MethodPost && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" { w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "id": "ec56a4180-65aa-42ec-a945-5fd21dec0538", diff --git a/providers/dns/azure/azure.go b/providers/dns/azure/azure.go index abd342eb..bbf2c7fb 100644 --- a/providers/dns/azure/azure.go +++ b/providers/dns/azure/azure.go @@ -67,20 +67,20 @@ func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZoneID(fqdn) + zone, err := d.getHostedZoneID(fqdn) if err != nil { return err } - rsc := dns.NewRecordSetsClient(c.subscriptionID) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.subscriptionID) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } @@ -96,7 +96,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { }, } - _, err = rsc.CreateOrUpdate(c.context, c.resourceGroup, zone, relative, dns.TXT, rec, "", "") + _, err = rsc.CreateOrUpdate(d.context, d.resourceGroup, zone, relative, dns.TXT, rec, "", "") return err } @@ -106,44 +106,44 @@ func toRelativeRecord(domain, zone string) string { } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZoneID(fqdn) + zone, err := d.getHostedZoneID(fqdn) if err != nil { return err } relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) - rsc := dns.NewRecordSetsClient(c.subscriptionID) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.subscriptionID) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } rsc.Authorizer = autorest.NewBearerAuthorizer(spt) - _, err = rsc.Delete(c.context, c.resourceGroup, zone, relative, dns.TXT, "") + _, err = rsc.Delete(d.context, d.resourceGroup, zone, relative, dns.TXT, "") return err } // Checks that azure has a zone for this domain name. -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return "", err } // Now we want to to Azure and get the zone. - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return "", err } - dc := dns.NewZonesClient(c.subscriptionID) + dc := dns.NewZonesClient(d.subscriptionID) dc.Authorizer = autorest.NewBearerAuthorizer(spt) - zone, err := dc.Get(c.context, c.resourceGroup, acme.UnFqdn(authZone)) + zone, err := dc.Get(d.context, d.resourceGroup, acme.UnFqdn(authZone)) if err != nil { return "", err } @@ -154,10 +154,10 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the // passed credentials map. -func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantID) +func (d *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { + oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.tenantID) if err != nil { return nil, err } - return adal.NewServicePrincipalToken(*oauthConfig, c.clientID, c.clientSecret, scope) + return adal.NewServicePrincipalToken(*oauthConfig, d.clientID, d.clientSecret, scope) } diff --git a/providers/dns/bluecat/bluecat.go b/providers/dns/bluecat/bluecat.go index 83a502ca..d88d73b1 100644 --- a/providers/dns/bluecat/bluecat.go +++ b/providers/dns/bluecat/bluecat.go @@ -39,7 +39,7 @@ type DNSProvider struct { configName string dnsView string token string - httpClient *http.Client + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS. @@ -85,7 +85,7 @@ func NewDNSProviderCredentials(server, userName, password, configName, dnsView s password: password, configName: configName, dnsView: dnsView, - httpClient: client, + client: client, }, nil } @@ -114,7 +114,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, q.Add(argName, argVal) } req.URL.RawQuery = q.Encode() - resp, err := d.httpClient.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -137,13 +137,16 @@ func (d *DNSProvider) login() error { "password": d.password, } - resp, err := d.sendRequest("GET", "login", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs) if err != nil { return err } defer resp.Body.Close() - authBytes, _ := ioutil.ReadAll(resp.Body) + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } authResp := string(authBytes) if strings.Contains(authResp, "Authentication Error") { @@ -164,7 +167,7 @@ func (d *DNSProvider) logout() error { return nil } - resp, err := d.sendRequest("GET", "logout", nil, nil) + resp, err := d.sendRequest(http.MethodGet, "logout", nil, nil) if err != nil { return err } @@ -174,7 +177,10 @@ func (d *DNSProvider) logout() error { return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode) } - authBytes, _ := ioutil.ReadAll(resp.Body) + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } authResp := string(authBytes) if !strings.Contains(authResp, "successfully") { @@ -195,7 +201,7 @@ func (d *DNSProvider) lookupConfID() (uint, error) { "type": configType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return 0, err } @@ -222,7 +228,7 @@ func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { "type": viewType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return 0, err } @@ -271,7 +277,7 @@ func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { "type": zoneType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) // Return an empty zone if the named zone doesn't exist if resp != nil && resp.StatusCode == 404 { return 0, fmt.Errorf("Bluecat API could not find zone named %s", name) @@ -321,8 +327,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value), } - resp, err := d.sendRequest("POST", "addEntity", body, queryArgs) - + resp, err := d.sendRequest(http.MethodPost, "addEntity", body, queryArgs) if err != nil { return err } @@ -350,8 +355,7 @@ func (d *DNSProvider) deploy(entityID uint) error { "entityId": strconv.FormatUint(uint64(entityID), 10), } - resp, err := d.sendRequest("POST", "quickDeploy", nil, queryArgs) - + resp, err := d.sendRequest(http.MethodPost, "quickDeploy", nil, queryArgs) if err != nil { return err } @@ -385,7 +389,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { "type": txtType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return err } @@ -400,7 +404,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { "objectId": strconv.FormatUint(uint64(txtRec.ID), 10), } - resp, err = d.sendRequest("DELETE", "delete", nil, queryArgs) + resp, err = d.sendRequest(http.MethodDelete, http.MethodDelete, nil, queryArgs) if err != nil { return err } diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index 85bbe498..7d8979ef 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -24,6 +24,7 @@ const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" type DNSProvider struct { authEmail string authKey string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for cloudflare. @@ -48,19 +49,20 @@ func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { return &DNSProvider{ authEmail: email, authKey: key, + client: &http.Client{Timeout: 30 * time.Second}, }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } @@ -77,24 +79,24 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) + _, err = d.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - record, err := c.findTxtRecord(fqdn) + record, err := d.findTxtRecord(fqdn) if err != nil { return err } - _, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) + _, err = d.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) return err } -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // HostedZone represents a CloudFlare DNS zone type HostedZone struct { ID string `json:"id"` @@ -106,7 +108,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", err } - result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil) + result, err := d.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil) if err != nil { return "", err } @@ -124,14 +126,14 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return hostedZone[0].ID, nil } -func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { - zoneID, err := c.getHostedZoneID(fqdn) +func (d *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return nil, err } - result, err := c.makeRequest( - "GET", + result, err := d.doRequest( + http.MethodGet, fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil, ) @@ -154,31 +156,16 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { return nil, fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - // APIError contains error details for failed requests - type APIError struct { - Code int `json:"code,omitempty"` - Message string `json:"message,omitempty"` - ErrorChain []APIError `json:"error_chain,omitempty"` - } - - // APIResponse represents a response from CloudFlare API - type APIResponse struct { - Success bool `json:"success"` - Errors []*APIError `json:"errors"` - Result json.RawMessage `json:"result"` - } - +func (d *DNSProvider) doRequest(method, uri string, body io.Reader) (json.RawMessage, error) { req, err := http.NewRequest(method, fmt.Sprintf("%s%s", CloudFlareAPIURL, uri), body) if err != nil { return nil, err } - req.Header.Set("X-Auth-Email", c.authEmail) - req.Header.Set("X-Auth-Key", c.authKey) + req.Header.Set("X-Auth-Email", d.authEmail) + req.Header.Set("X-Auth-Key", d.authKey) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, fmt.Errorf("error querying Cloudflare API -> %v", err) } @@ -212,6 +199,20 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return r.Result, nil } +// APIError contains error details for failed requests +type APIError struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + ErrorChain []APIError `json:"error_chain,omitempty"` +} + +// APIResponse represents a response from CloudFlare API +type APIResponse struct { + Success bool `json:"success"` + Errors []*APIError `json:"errors"` + Result json.RawMessage `json:"result"` +} + // cloudFlareRecord represents a CloudFlare DNS record type cloudFlareRecord struct { Name string `json:"name"` diff --git a/providers/dns/cloudxns/cloudxns.go b/providers/dns/cloudxns/cloudxns.go index 26655552..ae819f12 100644 --- a/providers/dns/cloudxns/cloudxns.go +++ b/providers/dns/cloudxns/cloudxns.go @@ -50,33 +50,33 @@ func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - return c.addTxtRecord(zoneID, fqdn, value, ttl) + return d.addTxtRecord(zoneID, fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - recordID, err := c.findTxtRecord(zoneID, fqdn) + recordID, err := d.findTxtRecord(zoneID, fqdn) if err != nil { return err } - return c.delTxtRecord(recordID, zoneID) + return d.delTxtRecord(recordID, zoneID) } -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { type Data struct { ID string `json:"id"` Domain string `json:"domain"` @@ -87,7 +87,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", err } - result, err := c.makeRequest("GET", "domain", nil) + result, err := d.makeRequest(http.MethodGet, "domain", nil) if err != nil { return "", err } @@ -107,8 +107,8 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", fmt.Errorf("zone %s not found in cloudxns for domain %s", authZone, fqdn) } -func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { - result, err := c.makeRequest("GET", fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) +func (d *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) if err != nil { return "", err } @@ -128,7 +128,7 @@ func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { return "", fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { +func (d *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { id, err := strconv.Atoi(zoneID) if err != nil { return err @@ -148,21 +148,21 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { return err } - _, err = c.makeRequest("POST", "record", body) + _, err = d.makeRequest(http.MethodPost, "record", body) return err } -func (c *DNSProvider) delTxtRecord(recordID, zoneID string) error { - _, err := c.makeRequest("DELETE", fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) +func (d *DNSProvider) delTxtRecord(recordID, zoneID string) error { + _, err := d.makeRequest(http.MethodDelete, fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) return err } -func (c *DNSProvider) hmac(url, date, body string) string { - sum := md5.Sum([]byte(c.apiKey + url + body + date + c.secretKey)) +func (d *DNSProvider) hmac(url, date, body string) string { + sum := md5.Sum([]byte(d.apiKey + url + body + date + d.secretKey)) return hex.EncodeToString(sum[:]) } -func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { +func (d *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { type APIResponse struct { Code int `json:"code"` Message string `json:"message"` @@ -177,9 +177,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMess requestDate := time.Now().Format(time.RFC1123Z) - req.Header.Set("API-KEY", c.apiKey) + req.Header.Set("API-KEY", d.apiKey) req.Header.Set("API-REQUEST-DATE", requestDate) - req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body))) + req.Header.Set("API-HMAC", d.hmac(url, requestDate, string(body))) req.Header.Set("API-FORMAT", "json") resp, err := acme.HTTPClient.Do(req) diff --git a/providers/dns/digitalocean/digitalocean.go b/providers/dns/digitalocean/digitalocean.go index 9abb97d8..e4247046 100644 --- a/providers/dns/digitalocean/digitalocean.go +++ b/providers/dns/digitalocean/digitalocean.go @@ -20,12 +20,7 @@ type DNSProvider struct { apiAuthToken string recordIDs map[string]int recordIDsMu sync.Mutex -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 60 * time.Second, 5 * time.Second + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Digital @@ -49,9 +44,16 @@ func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { return &DNSProvider{ apiAuthToken: apiAuthToken, recordIDs: make(map[string]int), + client: &http.Client{Timeout: 30 * time.Second}, }, nil } +// Timeout returns the timeout and interval to use when checking for DNS +// propagation. Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return 60 * time.Second, 5 * time.Second +} + // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) @@ -70,15 +72,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } - req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } @@ -123,15 +124,15 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) - req, err := http.NewRequest("DELETE", reqURL, nil) + req, err := http.NewRequest(http.MethodDelete, reqURL, nil) if err != nil { return err } + req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } diff --git a/providers/dns/dns_providers.go b/providers/dns/dns_providers.go index d0002861..0f54c27f 100644 --- a/providers/dns/dns_providers.go +++ b/providers/dns/dns_providers.go @@ -39,75 +39,72 @@ import ( // NewDNSChallengeProviderByName Factory for DNS providers func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) { - var err error - var provider acme.ChallengeProvider switch name { case "azure": - provider, err = azure.NewDNSProvider() + return azure.NewDNSProvider() case "auroradns": - provider, err = auroradns.NewDNSProvider() + return auroradns.NewDNSProvider() case "bluecat": - provider, err = bluecat.NewDNSProvider() + return bluecat.NewDNSProvider() case "cloudflare": - provider, err = cloudflare.NewDNSProvider() + return cloudflare.NewDNSProvider() case "cloudxns": - provider, err = cloudxns.NewDNSProvider() + return cloudxns.NewDNSProvider() case "digitalocean": - provider, err = digitalocean.NewDNSProvider() + return digitalocean.NewDNSProvider() case "dnsimple": - provider, err = dnsimple.NewDNSProvider() + return dnsimple.NewDNSProvider() case "dnsmadeeasy": - provider, err = dnsmadeeasy.NewDNSProvider() + return dnsmadeeasy.NewDNSProvider() case "dnspod": - provider, err = dnspod.NewDNSProvider() + return dnspod.NewDNSProvider() case "duckdns": - provider, err = duckdns.NewDNSProvider() + return duckdns.NewDNSProvider() case "dyn": - provider, err = dyn.NewDNSProvider() + return dyn.NewDNSProvider() case "fastdns": - provider, err = fastdns.NewDNSProvider() + return fastdns.NewDNSProvider() case "exoscale": - provider, err = exoscale.NewDNSProvider() + return exoscale.NewDNSProvider() case "gandi": - provider, err = gandi.NewDNSProvider() + return gandi.NewDNSProvider() case "gandiv5": - provider, err = gandiv5.NewDNSProvider() + return gandiv5.NewDNSProvider() case "glesys": - provider, err = glesys.NewDNSProvider() + return glesys.NewDNSProvider() case "gcloud": - provider, err = gcloud.NewDNSProvider() + return gcloud.NewDNSProvider() case "godaddy": - provider, err = godaddy.NewDNSProvider() + return godaddy.NewDNSProvider() case "lightsail": - provider, err = lightsail.NewDNSProvider() + return lightsail.NewDNSProvider() case "linode": - provider, err = linode.NewDNSProvider() + return linode.NewDNSProvider() case "manual": - provider, err = acme.NewDNSProviderManual() + return acme.NewDNSProviderManual() case "namecheap": - provider, err = namecheap.NewDNSProvider() + return namecheap.NewDNSProvider() case "namedotcom": - provider, err = namedotcom.NewDNSProvider() + return namedotcom.NewDNSProvider() case "rackspace": - provider, err = rackspace.NewDNSProvider() + return rackspace.NewDNSProvider() case "route53": - provider, err = route53.NewDNSProvider() + return route53.NewDNSProvider() case "rfc2136": - provider, err = rfc2136.NewDNSProvider() + return rfc2136.NewDNSProvider() case "vultr": - provider, err = vultr.NewDNSProvider() + return vultr.NewDNSProvider() case "ovh": - provider, err = ovh.NewDNSProvider() + return ovh.NewDNSProvider() case "pdns": - provider, err = pdns.NewDNSProvider() + return pdns.NewDNSProvider() case "ns1": - provider, err = ns1.NewDNSProvider() + return ns1.NewDNSProvider() case "otc": - provider, err = otc.NewDNSProvider() + return otc.NewDNSProvider() case "exec": - provider, err = exec.NewDNSProvider() + return exec.NewDNSProvider() default: - err = fmt.Errorf("unrecognised DNS provider: %s", name) + return nil, fmt.Errorf("unrecognised DNS provider: %s", name) } - return provider, err } diff --git a/providers/dns/dnsimple/dnsimple.go b/providers/dns/dnsimple/dnsimple.go index 30c7fc2f..a9f8424a 100644 --- a/providers/dns/dnsimple/dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -46,22 +46,22 @@ func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneName, err := c.getHostedZone(domain) + zoneName, err := d.getHostedZone(domain) if err != nil { return err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return err } - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + _, err = d.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) if err != nil { return fmt.Errorf("DNSimple API call failed: %v", err) } @@ -70,21 +70,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.findTxtRecords(domain, fqdn) + records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return err } for _, rec := range records { - _, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID) + _, err := d.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID) if err != nil { return err } @@ -93,20 +93,20 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, error) { +func (d *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return "", err } zoneName := acme.UnFqdn(authZone) - zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) + zones, err := d.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) if err != nil { return "", fmt.Errorf("DNSimple API call failed: %v", err) } @@ -125,20 +125,20 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { return hostedZone.Name, nil } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) { - zoneName, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) { + zoneName, err := d.getHostedZone(domain) if err != nil { return nil, err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return nil, err } - recordName := c.extractRecordName(fqdn, zoneName) + recordName := d.extractRecordName(fqdn, zoneName) - result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}}) + result, err := d.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}}) if err != nil { return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err) } @@ -146,8 +146,8 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord return result.Data, nil } -func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { - name := c.extractRecordName(fqdn, zoneName) +func (d *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { + name := d.extractRecordName(fqdn, zoneName) return &dnsimple.ZoneRecord{ Type: "TXT", @@ -157,7 +157,7 @@ func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsim } } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] @@ -165,8 +165,8 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string { return name } -func (c *DNSProvider) getAccountID() (string, error) { - whoamiResponse, err := c.client.Identity.Whoami() +func (d *DNSProvider) getAccountID() (string, error) { + whoamiResponse, err := d.client.Identity.Whoami() if err != nil { return "", err } diff --git a/providers/dns/dnsmadeeasy/dnsmadeeasy.go b/providers/dns/dnsmadeeasy/dnsmadeeasy.go index 2083cdc5..9f7f31a9 100644 --- a/providers/dns/dnsmadeeasy/dnsmadeeasy.go +++ b/providers/dns/dnsmadeeasy/dnsmadeeasy.go @@ -24,6 +24,7 @@ type DNSProvider struct { baseURL string apiKey string apiSecret string + client *http.Client } // Domain holds the DNSMadeEasy API representation of a Domain @@ -68,10 +69,19 @@ func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider, return nil, fmt.Errorf("DNS Made Easy credentials missing") } + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: transport, + Timeout: 10 * time.Second, + } + return &DNSProvider{ baseURL: baseURL, apiKey: apiKey, apiSecret: apiSecret, + client: client, }, nil } @@ -135,7 +145,7 @@ func (d *DNSProvider) getDomain(authZone string) (*Domain, error) { domainName := authZone[0 : len(authZone)-1] resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName) - resp, err := d.sendRequest("GET", resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return nil, err } @@ -153,7 +163,7 @@ func (d *DNSProvider) getDomain(authZone string) (*Domain, error) { func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) (*[]Record, error) { resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType) - resp, err := d.sendRequest("GET", resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return nil, err } @@ -175,7 +185,7 @@ func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records") - resp, err := d.sendRequest("POST", url, record) + resp, err := d.sendRequest(http.MethodPost, url, record) if err != nil { return err } @@ -187,7 +197,7 @@ func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { func (d *DNSProvider) deleteRecord(record Record) error { resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID) - resp, err := d.sendRequest("DELETE", resource, nil) + resp, err := d.sendRequest(http.MethodDelete, resource, nil) if err != nil { return err } @@ -217,14 +227,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) req.Header.Set("accept", "application/json") req.Header.Set("content-type", "application/json") - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{ - Transport: transport, - Timeout: 10 * time.Second, - } - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } diff --git a/providers/dns/dnspod/dnspod.go b/providers/dns/dnspod/dnspod.go index bfbeee62..e42e3633 100644 --- a/providers/dns/dnspod/dnspod.go +++ b/providers/dns/dnspod/dnspod.go @@ -41,15 +41,15 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, zoneName, err := c.getHostedZone(domain) + zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return err } - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + _, _, err = d.client.Domains.CreateRecord(zoneID, *recordAttributes) if err != nil { return fmt.Errorf("dnspod API call failed: %v", err) } @@ -58,21 +58,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.findTxtRecords(domain, fqdn) + records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } - zoneID, _, err := c.getHostedZone(domain) + zoneID, _, err := d.getHostedZone(domain) if err != nil { return err } for _, rec := range records { - _, err := c.client.Domains.DeleteRecord(zoneID, rec.ID) + _, err := d.client.Domains.DeleteRecord(zoneID, rec.ID) if err != nil { return err } @@ -80,8 +80,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { - zones, _, err := c.client.Domains.List() +func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { + zones, _, err := d.client.Domains.List() if err != nil { return "", "", fmt.Errorf("dnspod API call failed: %v", err) } @@ -106,8 +106,8 @@ func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil } -func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { - name := c.extractRecordName(fqdn, zone) +func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { + name := d.extractRecordName(fqdn, zone) return &dnspod.Record{ Type: "TXT", @@ -118,19 +118,19 @@ func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Re } } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) { - zoneID, zoneName, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) { + zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return nil, err } var records []dnspod.Record - result, _, err := c.client.Domains.ListRecords(zoneID, "") + result, _, err := d.client.Domains.ListRecords(zoneID, "") if err != nil { return records, fmt.Errorf("dnspod API call has failed: %v", err) } - recordName := c.extractRecordName(fqdn, zoneName) + recordName := d.extractRecordName(fqdn, zoneName) for _, record := range result { if record.Name == recordName { @@ -141,7 +141,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro return records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] diff --git a/providers/dns/dyn/dyn.go b/providers/dns/dyn/dyn.go index 27e6f7ed..187b1b48 100644 --- a/providers/dns/dyn/dyn.go +++ b/providers/dns/dyn/dyn.go @@ -37,6 +37,7 @@ type DNSProvider struct { userName string password string token string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Dyn DNS. @@ -62,6 +63,7 @@ func NewDNSProviderCredentials(customerName, userName, password string) (*DNSPro customerName: customerName, userName: userName, password: password, + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -82,8 +84,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) req.Header.Set("Auth-Token", d.token) } - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -129,7 +130,7 @@ func (d *DNSProvider) login() error { } payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password} - dynRes, err := d.sendRequest("POST", "Session", payload) + dynRes, err := d.sendRequest(http.MethodPost, "Session", payload) if err != nil { return err } @@ -153,15 +154,14 @@ func (d *DNSProvider) logout() error { } url := fmt.Sprintf("%s/Session", dynBaseURL) - req, err := http.NewRequest("DELETE", url, nil) + req, err := http.NewRequest(http.MethodDelete, url, nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Auth-Token", d.token) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } @@ -198,7 +198,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - _, err = d.sendRequest("POST", resource, data) + _, err = d.sendRequest(http.MethodPost, resource, data) if err != nil { return err } @@ -220,7 +220,7 @@ func (d *DNSProvider) publish(zone, notes string) error { pub := &publish{Publish: true, Notes: notes} resource := fmt.Sprintf("Zone/%s/", zone) - _, err := d.sendRequest("PUT", resource, pub) + _, err := d.sendRequest(http.MethodPut, resource, pub) return err } @@ -240,15 +240,16 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - req, err := http.NewRequest("DELETE", url, nil) + + req, err := http.NewRequest(http.MethodDelete, url, nil) if err != nil { return err } + req.Header.Set("Content-Type", "application/json") req.Header.Set("Auth-Token", d.token) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } diff --git a/providers/dns/exoscale/exoscale.go b/providers/dns/exoscale/exoscale.go index 421a3b40..c5b26d1d 100644 --- a/providers/dns/exoscale/exoscale.go +++ b/providers/dns/exoscale/exoscale.go @@ -46,14 +46,14 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) + zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } - recordID, err := c.FindExistingRecordID(zone, recordName) + recordID, err := d.FindExistingRecordID(zone, recordName) if err != nil { return err } @@ -66,13 +66,13 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } if recordID == 0 { - _, err := c.client.CreateRecord(zone, record) + _, err := d.client.CreateRecord(zone, record) if err != nil { return errors.New("Error while creating DNS record: " + err.Error()) } } else { record.ID = recordID - _, err := c.client.UpdateRecord(zone, record) + _, err := d.client.UpdateRecord(zone, record) if err != nil { return errors.New("Error while updating DNS record: " + err.Error()) } @@ -82,20 +82,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) + zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } - recordID, err := c.FindExistingRecordID(zone, recordName) + recordID, err := d.FindExistingRecordID(zone, recordName) if err != nil { return err } if recordID != 0 { - err = c.client.DeleteRecord(zone, recordID) + err = d.client.DeleteRecord(zone, recordID) if err != nil { return errors.New("Error while deleting DNS record: " + err.Error()) } @@ -106,8 +106,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { // FindExistingRecordID Query Exoscale to find an existing record for this name. // Returns nil if no record could be found -func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) { - records, err := c.client.GetRecords(zone) +func (d *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) { + records, err := d.client.GetRecords(zone) if err != nil { return -1, errors.New("Error while retrievening DNS records: " + err.Error()) } @@ -120,7 +120,7 @@ func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, erro } // FindZoneAndRecordName Extract DNS zone and DNS entry name -func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { +func (d *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err diff --git a/providers/dns/fastdns/fastdns.go b/providers/dns/fastdns/fastdns.go index 78249127..4bc79400 100644 --- a/providers/dns/fastdns/fastdns.go +++ b/providers/dns/fastdns/fastdns.go @@ -51,14 +51,14 @@ func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) ( } // Present creates a TXT record to fullfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain) + zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return err } - configdns.Init(c.config) + configdns.Init(d.config) zone, err := configdns.GetZone(zoneName) if err != nil { @@ -71,35 +71,35 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { record.SetField("target", value) record.SetField("active", true) - existingRecord := c.findExistingRecord(zone, recordName) + existingRecord := d.findExistingRecord(zone, recordName) if existingRecord != nil { if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) { return nil } zone.RemoveRecord(existingRecord) - return c.createRecord(zone, record) + return d.createRecord(zone, record) } - return c.createRecord(zone, record) + return d.createRecord(zone, record) } // CleanUp removes the record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain) + zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return err } - configdns.Init(c.config) + configdns.Init(d.config) zone, err := configdns.GetZone(zoneName) if err != nil { return err } - existingRecord := c.findExistingRecord(zone, recordName) + existingRecord := d.findExistingRecord(zone, recordName) if existingRecord != nil { err := zone.RemoveRecord(existingRecord) @@ -112,7 +112,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { +func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err @@ -124,7 +124,7 @@ func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string return zone, name, nil } -func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord { +func (d *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord { for _, r := range zone.Zone.Txt { if r.Name == recordName { return r @@ -134,7 +134,7 @@ func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string return nil } -func (c *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error { +func (d *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error { err := zone.AddRecord(record) if err != nil { return err diff --git a/providers/dns/gandi/gandi.go b/providers/dns/gandi/gandi.go index fa43f581..d7243009 100644 --- a/providers/dns/gandi/gandi.go +++ b/providers/dns/gandi/gandi.go @@ -44,6 +44,7 @@ type DNSProvider struct { inProgressFQDNs map[string]inProgressInfo inProgressAuthZones map[string]struct{} inProgressMu sync.Mutex + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -61,12 +62,13 @@ func NewDNSProvider() (*DNSProvider, error) { // DNSProvider instance configured for Gandi. func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { if apiKey == "" { - return nil, fmt.Errorf("No Gandi API Key given") + return nil, fmt.Errorf("no Gandi API Key given") } return &DNSProvider{ apiKey: apiKey, inProgressFQDNs: make(map[string]inProgressInfo), inProgressAuthZones: make(map[string]struct{}), + client: &http.Client{Timeout: 60 * time.Second}, }, nil } @@ -273,10 +275,8 @@ func (e rpcError) Error() string { "Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) } -func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { - client := http.Client{Timeout: 60 * time.Second} - - resp, err := client.Post(url, bodyType, body) +func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { + resp, err := d.client.Post(url, bodyType, body) if err != nil { return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err) } @@ -294,7 +294,7 @@ func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { // marshalling the data given in the call argument to XML and sending // that via HTTP Post to Gandi. The response is then unmarshalled into // the resp argument. -func rpcCall(call *methodCall, resp response) error { +func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { // marshal b, err := xml.MarshalIndent(call, "", " ") if err != nil { @@ -303,7 +303,7 @@ func rpcCall(call *methodCall, resp response) error { // post b = append([]byte(``+"\n"), b...) - respBody, err := httpPost(endpoint, "text/xml", bytes.NewReader(b)) + respBody, err := d.httpPost(endpoint, "text/xml", bytes.NewReader(b)) if err != nil { return err } @@ -324,7 +324,7 @@ func rpcCall(call *methodCall, resp response) error { func (d *DNSProvider) getZoneID(domain string) (int, error) { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.info", Params: []param{ paramString{Value: d.apiKey}, @@ -351,7 +351,7 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) { func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.clone", Params: []param{ paramString{Value: d.apiKey}, @@ -385,7 +385,7 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { resp := &responseInt{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.new", Params: []param{ paramString{Value: d.apiKey}, @@ -404,7 +404,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.record.add", Params: []param{ paramString{Value: d.apiKey}, @@ -433,7 +433,7 @@ func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value s func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { resp := &responseBool{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.set", Params: []param{ paramString{Value: d.apiKey}, @@ -453,7 +453,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { func (d *DNSProvider) setZone(domain string, zoneID int) error { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.set", Params: []param{ paramString{Value: d.apiKey}, @@ -481,7 +481,7 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error { func (d *DNSProvider) deleteZone(zoneID int) error { resp := &responseBool{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.delete", Params: []param{ paramString{Value: d.apiKey}, diff --git a/providers/dns/gandiv5/gandiv5.go b/providers/dns/gandiv5/gandiv5.go index c0dff4d1..dea0f5f4 100644 --- a/providers/dns/gandiv5/gandiv5.go +++ b/providers/dns/gandiv5/gandiv5.go @@ -12,6 +12,7 @@ import ( "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -40,6 +41,7 @@ type DNSProvider struct { apiKey string inProgressFQDNs map[string]inProgressInfo inProgressMu sync.Mutex + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -62,6 +64,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { return &DNSProvider{ apiKey: apiKey, inProgressFQDNs: make(map[string]inProgressInfo), + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -169,8 +172,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf req.Header.Set("X-Api-Key", d.apiKey) } - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -181,7 +183,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf } var response responseStruct err = json.NewDecoder(resp.Body).Decode(&response) - if err != nil && method != "DELETE" { + if err != nil && method != http.MethodDelete { return nil, err } @@ -192,23 +194,23 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - response, err := d.sendRequest("PUT", target, addFieldRequest{ + response, err := d.sendRequest(http.MethodPut, target, addFieldRequest{ RRSetTTL: ttl, RRSetValues: []string{value}, }) if response != nil { - fmt.Printf("Gandi DNS: %s\n", response.Message) + log.Infof("Gandi DNS: %s", response.Message) } return err } func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - response, err := d.sendRequest("DELETE", target, deleteFieldRequest{ + response, err := d.sendRequest(http.MethodDelete, target, deleteFieldRequest{ Delete: true, }) if response != nil && response.Message == "" { - fmt.Printf("Gandi DNS: Zone record deleted\n") + log.Infof("Gandi DNS: Zone record deleted") } return err } diff --git a/providers/dns/gcloud/googlecloud.go b/providers/dns/gcloud/googlecloud.go index c50c0ed7..2999a79d 100644 --- a/providers/dns/gcloud/googlecloud.go +++ b/providers/dns/gcloud/googlecloud.go @@ -96,10 +96,10 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } @@ -115,7 +115,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // Look for existing records. - list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do() + list, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do() if err != nil { return err } @@ -124,7 +124,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { change.Deletions = list.Rrsets } - chg, err := c.client.Changes.Create(c.project, zone, change).Do() + chg, err := d.client.Changes.Create(d.project, zone, change).Do() if err != nil { return err } @@ -133,7 +133,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { for chg.Status == "pending" { time.Sleep(time.Second) - chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do() + chg, err = d.client.Changes.Get(d.project, zone, chg.Id).Do() if err != nil { return err } @@ -143,15 +143,15 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } - records, err := c.findTxtRecords(zone, fqdn) + records, err := d.findTxtRecords(zone, fqdn) if err != nil { return err } @@ -160,7 +160,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { change := &dns.Change{ Deletions: []*dns.ResourceRecordSet{rec}, } - _, err = c.client.Changes.Create(c.project, zone, change).Do() + _, err = d.client.Changes.Create(d.project, zone, change).Do() if err != nil { return err } @@ -170,19 +170,19 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Timeout customizes the timeout values used by the ACME package for checking // DNS record validity. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 180 * time.Second, 5 * time.Second } // getHostedZone returns the managed-zone -func (c *DNSProvider) getHostedZone(domain string) (string, error) { +func (d *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", err } - zones, err := c.client.ManagedZones. - List(c.project). + zones, err := d.client.ManagedZones. + List(d.project). DnsName(authZone). Do() if err != nil { @@ -196,9 +196,9 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { return zones.ManagedZones[0].Name, nil } -func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { +func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { - recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do() + recs, err := d.client.ResourceRecordSets.List(d.project, zone).Do() if err != nil { return nil, err } diff --git a/providers/dns/glesys/glesys.go b/providers/dns/glesys/glesys.go index 71ef461f..d6b07196 100644 --- a/providers/dns/glesys/glesys.go +++ b/providers/dns/glesys/glesys.go @@ -29,6 +29,7 @@ type DNSProvider struct { apiKey string activeRecords map[string]int inProgressMu sync.Mutex + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for GleSYS. @@ -49,10 +50,12 @@ func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, err if apiUser == "" || apiKey == "" { return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided") } + return &DNSProvider{ apiUser: apiUser, apiKey: apiKey, activeRecords: make(map[string]int), + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -121,7 +124,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // types for JSON method calls, parameters, and responses type addRecordRequest struct { - Domainname string `json:"domainname"` + DomainName string `json:"domainname"` Host string `json:"host"` Type string `json:"type"` Data string `json:"data"` @@ -129,7 +132,7 @@ type addRecordRequest struct { } type deleteRecordRequest struct { - Recordid int `json:"recordid"` + RecordID int `json:"recordid"` } type responseStruct struct { @@ -159,8 +162,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(d.apiUser, d.apiKey) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -179,26 +181,27 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf // functions to perform API actions func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) { - response, err := d.sendRequest("POST", "addrecord", addRecordRequest{ - Domainname: domain, + response, err := d.sendRequest(http.MethodPost, "addrecord", addRecordRequest{ + DomainName: domain, Host: name, Type: "TXT", Data: value, TTL: ttl, }) - if response != nil && response.Response.Status.Code == 200 { - log.Printf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid) - return response.Response.Record.Recordid, nil + + if response != nil && response.Response.Status.Code == http.StatusOK { + log.Infof("[%s] GleSYS DNS: Successfully created record id %d", fqdn, response.Response.Record.RecordID) + return response.Response.Record.RecordID, nil } return 0, err } func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error { - response, err := d.sendRequest("POST", "deleterecord", deleteRecordRequest{ - Recordid: recordid, + response, err := d.sendRequest(http.MethodPost, "deleterecord", deleteRecordRequest{ + RecordID: recordid, }) if response != nil && response.Response.Status.Code == 200 { - log.Printf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid) + log.Infof("[%s] GleSYS DNS: Successfully deleted record id %d", fqdn, recordid) } return err } diff --git a/providers/dns/godaddy/godaddy.go b/providers/dns/godaddy/godaddy.go index 75716b8a..c569adcb 100644 --- a/providers/dns/godaddy/godaddy.go +++ b/providers/dns/godaddy/godaddy.go @@ -22,6 +22,7 @@ const apiURL = "https://api.godaddy.com" type DNSProvider struct { apiKey string apiSecret string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for godaddy. @@ -43,16 +44,20 @@ func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { return nil, fmt.Errorf("GoDaddy credentials missing") } - return &DNSProvider{apiKey, apiSecret}, nil + return &DNSProvider{ + apiKey: apiKey, + apiSecret: apiSecret, + client: &http.Client{Timeout: 30 * time.Second}, + }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] @@ -61,9 +66,9 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string { } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) + domainZone, err := d.getZone(fqdn) if err != nil { return err } @@ -72,7 +77,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { ttl = 600 } - recordName := c.extractRecordName(fqdn, domainZone) + recordName := d.extractRecordName(fqdn, domainZone) rec := []DNSRecord{ { Type: "TXT", @@ -82,17 +87,17 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { }, } - return c.updateRecords(rec, domainZone, recordName) + return d.updateRecords(rec, domainZone, recordName) } -func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { +func (d *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { body, err := json.Marshal(records) if err != nil { return err } var resp *http.Response - resp, err = c.makeRequest("PUT", fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) + resp, err = d.makeRequest(http.MethodPut, fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) if err != nil { return err } @@ -107,14 +112,14 @@ func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, reco } // CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) + domainZone, err := d.getZone(fqdn) if err != nil { return err } - recordName := c.extractRecordName(fqdn, domainZone) + recordName := d.extractRecordName(fqdn, domainZone) rec := []DNSRecord{ { Type: "TXT", @@ -123,10 +128,10 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { }, } - return c.updateRecords(rec, domainZone, recordName) + return d.updateRecords(rec, domainZone, recordName) } -func (c *DNSProvider) getZone(fqdn string) (string, error) { +func (d *DNSProvider) getZone(fqdn string) (string, error) { authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return "", err @@ -135,7 +140,7 @@ func (c *DNSProvider) getZone(fqdn string) (string, error) { return acme.UnFqdn(authZone), nil } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body) if err != nil { return nil, err @@ -143,10 +148,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Res req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", c.apiKey, c.apiSecret)) + req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.apiKey, d.apiSecret)) - client := http.Client{Timeout: 30 * time.Second} - return client.Do(req) + return d.client.Do(req) } // DNSRecord a DNS record diff --git a/providers/dns/lightsail/lightsail.go b/providers/dns/lightsail/lightsail.go index a4d2efaf..2f6feddd 100644 --- a/providers/dns/lightsail/lightsail.go +++ b/providers/dns/lightsail/lightsail.go @@ -35,7 +35,7 @@ type customRetryer struct { // delay of ~400ms with an upper limit of ~30 seconds which should prevent // causing a high number of consecutive throttling errors. // For reference: Route 53 enforces an account-wide(!) 5req/s query limit. -func (d customRetryer) RetryRules(r *request.Request) time.Duration { +func (c customRetryer) RetryRules(r *request.Request) time.Duration { retryCount := r.RetryCount if retryCount > 7 { retryCount = 7 @@ -70,15 +70,15 @@ func NewDNSProvider() (*DNSProvider, error) { } // Present creates a TXT record using the specified parameters -func (r *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` - err := r.newTxtRecord(domain, fqdn, value) + err := d.newTxtRecord(domain, fqdn, value) return err } // CleanUp removes the TXT record matching the specified parameters -func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` params := &lightsail.DeleteDomainEntryInput{ @@ -89,11 +89,11 @@ func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { Target: aws.String(value), }, } - _, err := r.client.DeleteDomainEntry(params) + _, err := d.client.DeleteDomainEntry(params) return err } -func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error { +func (d *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error { params := &lightsail.CreateDomainEntryInput{ DomainName: aws.String(domain), DomainEntry: &lightsail.DomainEntry{ @@ -102,6 +102,6 @@ func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) err Type: aws.String("TXT"), }, } - _, err := r.client.CreateDomainEntry(params) + _, err := d.client.CreateDomainEntry(params) return err } diff --git a/providers/dns/linode/linode.go b/providers/dns/linode/linode.go index 22117b66..087d8b19 100644 --- a/providers/dns/linode/linode.go +++ b/providers/dns/linode/linode.go @@ -26,7 +26,7 @@ type hostedZoneInfo struct { // DNSProvider implements the acme.ChallengeProvider interface. type DNSProvider struct { - linode *dns.DNS + client *dns.DNS } // NewDNSProvider returns a DNSProvider instance configured for Linode. @@ -48,7 +48,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { } return &DNSProvider{ - linode: dns.New(apiKey), + client: dns.New(apiKey), }, nil } @@ -77,7 +77,7 @@ func (p *DNSProvider) Present(domain, token, keyAuth string) error { return err } - if _, err = p.linode.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { + if _, err = p.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { return err } @@ -93,7 +93,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { } // Get all TXT records for the specified domain. - resources, err := p.linode.GetResourcesByType(zone.domainID, "TXT") + resources, err := p.client.GetResourcesByType(zone.domainID, "TXT") if err != nil { return err } @@ -101,7 +101,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Remove the specified resource, if it exists. for _, resource := range resources { if resource.Name == zone.resourceName && resource.Target == value { - resp, err := p.linode.DeleteDomainResource(resource.DomainID, resource.ResourceID) + resp, err := p.client.DeleteDomainResource(resource.DomainID, resource.ResourceID) if err != nil { return err } @@ -124,7 +124,7 @@ func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { resourceName := strings.TrimSuffix(fqdn, "."+authZone) // Query the authority zone. - domain, err := p.linode.GetDomain(acme.UnFqdn(authZone)) + domain, err := p.client.GetDomain(acme.UnFqdn(authZone)) if err != nil { return nil, err } diff --git a/providers/dns/linode/linode_test.go b/providers/dns/linode/linode_test.go index 515cdc25..4fa287fb 100644 --- a/providers/dns/linode/linode_test.go +++ b/providers/dns/linode/linode_test.go @@ -127,7 +127,7 @@ func TestDNSProvider_Present(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.Present(domain, "", keyAuth) assert.NoError(t, err) @@ -156,7 +156,7 @@ func TestDNSProvider_PresentNoDomain(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.Present(domain, "", keyAuth) assert.EqualError(t, err, "dns: requested domain not found") @@ -193,7 +193,7 @@ func TestDNSProvider_PresentCreateFailed(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.Present(domain, "", keyAuth) assert.EqualError(t, err, "Failed to create domain resource") @@ -244,7 +244,7 @@ func TestDNSProvider_CleanUp(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.CleanUp(domain, "", keyAuth) assert.NoError(t, err) @@ -273,7 +273,7 @@ func TestDNSProvider_CleanUpNoDomain(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.CleanUp(domain, "", keyAuth) assert.EqualError(t, err, "dns: requested domain not found") @@ -322,7 +322,7 @@ func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) { mockSrv := newMockServer(t, mockResponses) defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) + p.client.ToLinode().SetEndpoint(mockSrv.URL) err = p.CleanUp(domain, "", keyAuth) assert.EqualError(t, err, "Failed to delete domain resource") diff --git a/providers/dns/namecheap/namecheap.go b/providers/dns/namecheap/namecheap.go index 35be8b4c..f0ce56a8 100644 --- a/providers/dns/namecheap/namecheap.go +++ b/providers/dns/namecheap/namecheap.go @@ -12,6 +12,7 @@ import ( "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -32,7 +33,6 @@ var ( debug = false defaultBaseURL = "https://api.namecheap.com/xml.response" getIPURL = "https://dynamicdns.park-your-domain.com/getip" - httpClient = http.Client{Timeout: 60 * time.Second} ) // DNSProvider is an implementation of the ChallengeProviderTimeout interface @@ -42,6 +42,7 @@ type DNSProvider struct { apiUser string apiKey string clientIP string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for namecheap. @@ -63,7 +64,9 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { return nil, fmt.Errorf("Namecheap credentials missing") } - clientIP, err := getClientIP() + client := &http.Client{Timeout: 60 * time.Second} + + clientIP, err := getClientIP(client) if err != nil { return nil, err } @@ -73,6 +76,7 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { apiUser: apiUser, apiKey: apiKey, clientIP: clientIP, + client: client, }, nil } @@ -102,8 +106,8 @@ type apierror struct { // getClientIP returns the client's public IP address. It uses namecheap's // IP discovery service to perform the lookup. -func getClientIP() (addr string, err error) { - resp, err := httpClient.Get(getIPURL) +func getClientIP(client *http.Client) (addr string, err error) { + resp, err := client.Get(getIPURL) if err != nil { return "", err } @@ -115,7 +119,7 @@ func getClientIP() (addr string, err error) { } if debug { - fmt.Println("Client IP:", string(clientIP)) + log.Println("Client IP:", string(clientIP)) } return string(clientIP), nil } @@ -189,7 +193,7 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { reqURL, _ := url.Parse(d.baseURL) reqURL.RawQuery = values.Encode() - resp, err := httpClient.Get(reqURL.String()) + resp, err := d.client.Get(reqURL.String()) if err != nil { return nil, err } @@ -238,7 +242,7 @@ func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { reqURL, _ := url.Parse(d.baseURL) reqURL.RawQuery = values.Encode() - resp, err := httpClient.Get(reqURL.String()) + resp, err := d.client.Get(reqURL.String()) if err != nil { return nil, err } @@ -288,7 +292,7 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { values.Add("TTL"+ind, h.TTL) } - resp, err := httpClient.PostForm(d.baseURL, values) + resp, err := d.client.PostForm(d.baseURL, values) if err != nil { return err } @@ -385,7 +389,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { if debug { for _, h := range hosts { - fmt.Printf( + log.Printf( "%-5.5s %-30.30s %-6s %-70.70s\n", h.Type, h.Name, h.TTL, h.Address) } diff --git a/providers/dns/namecheap/namecheap_test.go b/providers/dns/namecheap/namecheap_test.go index dfa07524..a213cf72 100644 --- a/providers/dns/namecheap/namecheap_test.go +++ b/providers/dns/namecheap/namecheap_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "net/url" "testing" + "time" ) var ( @@ -44,7 +45,7 @@ func assertHdr(tc *testcase, t *testing.T, values *url.Values) { func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Request) { switch r.Method { - case "GET": + case http.MethodGet: values := r.URL.Query() cmd := values.Get("Command") switch cmd { @@ -59,7 +60,7 @@ func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Reque t.Errorf("Unexpected GET command: %s", cmd) } - case "POST": + case http.MethodPost: r.ParseForm() values := r.Form cmd := values.Get("Command") @@ -90,6 +91,7 @@ func testGetHosts(tc *testcase, t *testing.T) { apiUser: fakeUser, apiKey: fakeKey, clientIP: fakeClientIP, + client: &http.Client{Timeout: 60 * time.Second}, } ch, _ := newChallenge(tc.domain, "", tlds) @@ -133,6 +135,7 @@ func mockDNSProvider(url string) *DNSProvider { apiUser: fakeUser, apiKey: fakeKey, clientIP: fakeClientIP, + client: &http.Client{Timeout: 60 * time.Second}, } } diff --git a/providers/dns/namedotcom/namedotcom.go b/providers/dns/namedotcom/namedotcom.go index 39c0cca9..15272c5f 100644 --- a/providers/dns/namedotcom/namedotcom.go +++ b/providers/dns/namedotcom/namedotcom.go @@ -49,18 +49,18 @@ func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider, } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) request := &namecom.Record{ DomainName: domain, - Host: c.extractRecordName(fqdn, domain), + Host: d.extractRecordName(fqdn, domain), Type: "TXT", TTL: uint32(ttl), Answer: value, } - _, err := c.client.CreateRecord(request) + _, err := d.client.CreateRecord(request) if err != nil { return fmt.Errorf("Name.com API call failed: %v", err) } @@ -69,10 +69,10 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.getRecords(domain) + records, err := d.getRecords(domain) if err != nil { return err } @@ -83,7 +83,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { DomainName: domain, ID: rec.ID, } - _, err := c.client.DeleteRecord(request) + _, err := d.client.DeleteRecord(request) if err != nil { return err } @@ -93,7 +93,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { +func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { var ( err error records []*namecom.Record @@ -106,7 +106,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { } for request.Page > 0 { - response, err = c.client.ListRecords(request) + response, err = d.client.ListRecords(request) if err != nil { return nil, err } @@ -118,7 +118,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { return records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] diff --git a/providers/dns/ns1/ns1.go b/providers/dns/ns1/ns1.go index e41af869..d37da4cd 100644 --- a/providers/dns/ns1/ns1.go +++ b/providers/dns/ns1/ns1.go @@ -43,16 +43,16 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } - record := c.newTxtRecord(zone, fqdn, value, ttl) - _, err = c.client.Records.Create(record) + record := d.newTxtRecord(zone, fqdn, value, ttl) + _, err = d.client.Records.Create(record) if err != nil && err != rest.ErrRecordExists { return err } @@ -61,21 +61,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } name := acme.UnFqdn(fqdn) - _, err = c.client.Records.Delete(zone.Zone, name, "TXT") + _, err = d.client.Records.Delete(zone.Zone, name, "TXT") return err } -func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { - zone, _, err := c.client.Zones.Get(domain) +func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { + zone, _, err := d.client.Zones.Get(domain) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { return zone, nil } -func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { +func (d *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { name := acme.UnFqdn(fqdn) return &dns.Record{ diff --git a/providers/dns/otc/mock.go b/providers/dns/otc/mock.go index babc2b3a..127dd5cc 100644 --- a/providers/dns/otc/mock.go +++ b/providers/dns/otc/mock.go @@ -77,7 +77,7 @@ func (m *DNSMock) HandleListZonesSuccessfully() { }]} `) - assert.Equal(m.t, r.Method, "GET") + assert.Equal(m.t, r.Method, http.MethodGet) assert.Equal(m.t, r.URL.Path, "/v2/zones") assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") @@ -92,7 +92,7 @@ func (m *DNSMock) HandleListZonesEmpty() { ]} `) - assert.Equal(m.t, r.Method, "GET") + assert.Equal(m.t, r.Method, http.MethodGet) assert.Equal(m.t, r.URL.Path, "/v2/zones") assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") @@ -108,7 +108,7 @@ func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() { }]} `) - assert.Equal(m.t, r.Method, "DELETE") + assert.Equal(m.t, r.Method, http.MethodDelete) assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") }) @@ -130,7 +130,7 @@ func (m *DNSMock) HandleListRecordsetsEmpty() { // HandleListRecordsetsSuccessfully Handle list recordsets successfully func (m *DNSMock) HandleListRecordsetsSuccessfully() { m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { + if r.Method == http.MethodGet { fmt.Fprintf(w, `{ "recordsets":[{ "id":"321321" @@ -140,7 +140,7 @@ func (m *DNSMock) HandleListRecordsetsSuccessfully() { assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets") assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.") - } else if r.Method == "POST" { + } else if r.Method == http.MethodPost { body, err := ioutil.ReadAll(r.Body) assert.Nil(m.t, err) diff --git a/providers/dns/otc/otc.go b/providers/dns/otc/otc.go index 76941c8d..30535f7e 100644 --- a/providers/dns/otc/otc.go +++ b/providers/dns/otc/otc.go @@ -170,7 +170,7 @@ func (d *DNSProvider) loginRequest() error { if err != nil { return err } - req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, d.identityEndpoint, bytes.NewReader(body)) if err != nil { return err } @@ -242,7 +242,7 @@ func (d *DNSProvider) getZoneID(zone string) (string, error) { } resource := fmt.Sprintf("zones?name=%s", zone) - resp, err := d.SendRequest("GET", resource, nil) + resp, err := d.SendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -278,7 +278,7 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) } resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) - resp, err := d.SendRequest("GET", resource, nil) + resp, err := d.SendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -307,7 +307,7 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error { resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID) - _, err := d.SendRequest("DELETE", resource, nil) + _, err := d.SendRequest(http.MethodDelete, resource, nil) return err } @@ -351,7 +351,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { TTL: ttl, Records: []string{fmt.Sprintf("\"%s\"", value)}, } - _, err = d.SendRequest("POST", resource, r1) + _, err = d.SendRequest(http.MethodPost, resource, r1) return err } diff --git a/providers/dns/ovh/ovh.go b/providers/dns/ovh/ovh.go index 98752306..e1999ea5 100644 --- a/providers/dns/ovh/ovh.go +++ b/providers/dns/ovh/ovh.go @@ -102,16 +102,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // Create TXT record err = d.client.Post(reqURL, reqData, &respData) if err != nil { - fmt.Printf("Error when call OVH api to add record : %q \n", err) - return err + return fmt.Errorf("error when call OVH api to add record: %v", err) } // Apply the change reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) err = d.client.Post(reqURL, nil, nil) if err != nil { - fmt.Printf("Error when call OVH api to refresh zone : %q \n", err) - return err + return fmt.Errorf("error when call OVH api to refresh zone: %v", err) } d.recordIDsMu.Lock() @@ -144,8 +142,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { err = d.client.Delete(reqURL, nil) if err != nil { - fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err) - return err + return fmt.Errorf("error when call OVH api to delete challenge record: %v", err) } // Delete record ID from map diff --git a/providers/dns/pdns/pdns.go b/providers/dns/pdns/pdns.go index e97de9a7..3b9915ac 100644 --- a/providers/dns/pdns/pdns.go +++ b/providers/dns/pdns/pdns.go @@ -14,6 +14,7 @@ import ( "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -22,6 +23,7 @@ type DNSProvider struct { apiKey string host *url.URL apiVersion int + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for pdns. @@ -52,25 +54,31 @@ func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) return nil, fmt.Errorf("PDNS API URL missing") } - provider := &DNSProvider{ + d := &DNSProvider{ host: host, apiKey: key, + client: &http.Client{Timeout: 30 * time.Second}, } - provider.getAPIVersion() - return provider, nil + apiVersion, err := d.getAPIVersion() + if err != nil { + log.Warnf("PDNS: failed to get API version %v", err) + } + d.apiVersion = apiVersion + + return d, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(fqdn) + zone, err := d.getHostedZone(fqdn) if err != nil { return err } @@ -78,7 +86,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { name := fqdn // pre-v1 API wants non-fqdn - if c.apiVersion == 0 { + if d.apiVersion == 0 { name = acme.UnFqdn(fqdn) } @@ -110,20 +118,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(fqdn) + zone, err := d.getHostedZone(fqdn) if err != nil { return err } - set, err := c.findTxtRecord(fqdn) + set, err := d.findTxtRecord(fqdn) if err != nil { return err } @@ -142,11 +150,11 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) return err } -func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { +func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { var zone hostedZone authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { @@ -154,12 +162,12 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { } url := "/servers/localhost/zones" - result, err := c.makeRequest("GET", url, nil) + result, err := d.makeRequest(http.MethodGet, url, nil) if err != nil { return nil, err } - zones := []hostedZone{} + var zones []hostedZone err = json.Unmarshal(result, &zones) if err != nil { return nil, err @@ -172,7 +180,7 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { } } - result, err = c.makeRequest("GET", url, nil) + result, err = d.makeRequest(http.MethodGet, url, nil) if err != nil { return nil, err } @@ -198,13 +206,13 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { return &zone, nil } -func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { - zone, err := c.getHostedZone(fqdn) +func (d *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { + zone, err := d.getHostedZone(fqdn) if err != nil { return nil, err } - _, err = c.makeRequest("GET", zone.URL, nil) + _, err = d.makeRequest(http.MethodGet, zone.URL, nil) if err != nil { return nil, err } @@ -218,21 +226,21 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { return nil, fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) getAPIVersion() { +func (d *DNSProvider) getAPIVersion() (int, error) { type APIVersion struct { URL string `json:"url"` Version int `json:"version"` } - result, err := c.makeRequest("GET", "/api", nil) + result, err := d.makeRequest(http.MethodGet, "/api", nil) if err != nil { - return + return 0, err } var versions []APIVersion err = json.Unmarshal(result, &versions) if err != nil { - return + return 0, err } latestVersion := 0 @@ -241,41 +249,45 @@ func (c *DNSProvider) getAPIVersion() { latestVersion = v.Version } } - c.apiVersion = latestVersion + + return latestVersion, err } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { type APIError struct { Error string `json:"error"` } + var path = "" - if c.host.Path != "/" { - path = c.host.Path + if d.host.Path != "/" { + path = d.host.Path } + if !strings.HasPrefix(uri, "/") { uri = "/" + uri } - if c.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { - uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri + + if d.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { + uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri } - url := c.host.Scheme + "://" + c.host.Host + path + uri + + url := d.host.Scheme + "://" + d.host.Host + path + uri req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } - req.Header.Set("X-API-Key", c.apiKey) + req.Header.Set("X-API-Key", d.apiKey) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", err) + return nil, fmt.Errorf("error talking to PDNS API -> %v", err) } defer resp.Body.Close() - if resp.StatusCode != 422 && (resp.StatusCode < 200 || resp.StatusCode >= 300) { - return nil, fmt.Errorf("Unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url) + if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) { + return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url) } var msg json.RawMessage @@ -297,7 +309,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return nil, err } if apiError.Error != "" { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", apiError.Error) + return nil, fmt.Errorf("error talking to PDNS API -> %v", apiError.Error) } } return msg, nil diff --git a/providers/dns/rackspace/rackspace.go b/providers/dns/rackspace/rackspace.go index 1ec576a9..358af69d 100644 --- a/providers/dns/rackspace/rackspace.go +++ b/providers/dns/rackspace/rackspace.go @@ -22,6 +22,7 @@ var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" type DNSProvider struct { token string cloudDNSEndpoint string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Rackspace. @@ -44,35 +45,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, fmt.Errorf("Rackspace credentials missing") } - type APIKeyCredentials struct { - Username string `json:"username"` - APIKey string `json:"apiKey"` - } - - type Auth struct { - APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` - } - - type RackspaceAuthData struct { - Auth `json:"auth"` - } - - type RackspaceIdentity struct { - Access struct { - ServiceCatalog []struct { - Endpoints []struct { - PublicURL string `json:"publicURL"` - TenantID string `json:"tenantId"` - } `json:"endpoints"` - Name string `json:"name"` - } `json:"serviceCatalog"` - Token struct { - ID string `json:"id"` - } `json:"token"` - } `json:"access"` - } - - authData := RackspaceAuthData{ + authData := AuthData{ Auth: Auth{ APIKeyCredentials: APIKeyCredentials{ Username: user, @@ -86,13 +59,13 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, err } - req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, rackspaceAPIURL, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") - client := http.Client{Timeout: 30 * time.Second} + client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("error querying Rackspace Identity API: %v", err) @@ -103,7 +76,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode) } - var rackspaceIdentity RackspaceIdentity + var rackspaceIdentity Identity err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity) if err != nil { return nil, err @@ -124,13 +97,14 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return &DNSProvider{ token: rackspaceIdentity.Access.Token.ID, cloudDNSEndpoint: dnsEndpoint, + client: client, }, nil } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } @@ -149,30 +123,30 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPost, fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - record, err := c.findTxtRecord(fqdn, zoneID) + record, err := d.findTxtRecord(fqdn, zoneID) if err != nil { return err } - _, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) + _, err = d.makeRequest(http.MethodDelete, fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) return err } // getHostedZoneID performs a lookup to get the DNS zone which needs // modifying for a given FQDN -func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (int, error) { // HostedZones represents the response when querying Rackspace DNS zones type ZoneSearchResponse struct { TotalEntries int `json:"totalEntries"` @@ -187,7 +161,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { return 0, err } - result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) if err != nil { return 0, err } @@ -207,8 +181,8 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { } // findTxtRecord searches a DNS zone for a TXT record with a specific name -func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { - result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) +func (d *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) if err != nil { return nil, err } @@ -232,14 +206,14 @@ func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { } // makeRequest is a wrapper function used for making DNS API requests -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - url := c.cloudDNSEndpoint + uri +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { + url := d.cloudDNSEndpoint + uri req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } - req.Header.Set("X-Auth-Token", c.token) + req.Header.Set("X-Auth-Token", d.token) req.Header.Set("Content-Type", "application/json") client := http.Client{Timeout: 30 * time.Second} @@ -263,6 +237,38 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return r, nil } +// APIKeyCredentials API credential +type APIKeyCredentials struct { + Username string `json:"username"` + APIKey string `json:"apiKey"` +} + +// Auth auth credentials +type Auth struct { + APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` +} + +// AuthData Auth data +type AuthData struct { + Auth `json:"auth"` +} + +// Identity Identity +type Identity struct { + Access struct { + ServiceCatalog []struct { + Endpoints []struct { + PublicURL string `json:"publicURL"` + TenantID string `json:"tenantId"` + } `json:"endpoints"` + Name string `json:"name"` + } `json:"serviceCatalog"` + Token struct { + ID string `json:"id"` + } `json:"token"` + } `json:"access"` +} + // Records is the list of records sent/received from the DNS API type Records struct { Record []Record `json:"records"` diff --git a/providers/dns/vultr/vultr.go b/providers/dns/vultr/vultr.go index 5694e292..27cfa524 100644 --- a/providers/dns/vultr/vultr.go +++ b/providers/dns/vultr/vultr.go @@ -35,25 +35,21 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { return nil, fmt.Errorf("Vultr credentials missing") } - c := &DNSProvider{ - client: vultr.NewClient(apiKey, nil), - } - - return c, nil + return &DNSProvider{client: vultr.NewClient(apiKey, nil)}, nil } // Present creates a TXT record to fulfil the DNS-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneDomain, err := c.getHostedZone(domain) + zoneDomain, err := d.getHostedZone(domain) if err != nil { return err } - name := c.extractRecordName(fqdn, zoneDomain) + name := d.extractRecordName(fqdn, zoneDomain) - err = c.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) + err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) if err != nil { return fmt.Errorf("Vultr API call failed: %v", err) } @@ -62,16 +58,16 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneDomain, records, err := c.findTxtRecords(domain, fqdn) + zoneDomain, records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } for _, rec := range records { - err := c.client.DeleteDNSRecord(zoneDomain, rec.RecordID) + err := d.client.DeleteDNSRecord(zoneDomain, rec.RecordID) if err != nil { return err } @@ -79,17 +75,17 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, error) { - domains, err := c.client.GetDNSDomains() +func (d *DNSProvider) getHostedZone(domain string) (string, error) { + domains, err := d.client.GetDNSDomains() if err != nil { return "", fmt.Errorf("Vultr API call failed: %v", err) } var hostedDomain vultr.DNSDomain - for _, d := range domains { - if strings.HasSuffix(domain, d.Domain) { - if len(d.Domain) > len(hostedDomain.Domain) { - hostedDomain = d + for _, dom := range domains { + if strings.HasSuffix(domain, dom.Domain) { + if len(dom.Domain) > len(hostedDomain.Domain) { + hostedDomain = dom } } } @@ -100,19 +96,19 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { return hostedDomain.Domain, nil } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { - zoneDomain, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { + zoneDomain, err := d.getHostedZone(domain) if err != nil { return "", nil, err } var records []vultr.DNSRecord - result, err := c.client.GetDNSRecords(zoneDomain) + result, err := d.client.GetDNSRecords(zoneDomain) if err != nil { return "", records, fmt.Errorf("Vultr API call has failed: %v", err) } - recordName := c.extractRecordName(fqdn, zoneDomain) + recordName := d.extractRecordName(fqdn, zoneDomain) for _, record := range result { if record.Type == "TXT" && record.Name == recordName { records = append(records, record) @@ -122,7 +118,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe return zoneDomain, records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx]