diff --git a/acme/client.go b/acme/client.go index 9f837af366..eee6ff954d 100644 --- a/acme/client.go +++ b/acme/client.go @@ -104,14 +104,18 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p} case TLSSNI01: c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p} - case DNS01: - c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p} default: return fmt.Errorf("Unknown challenge %v", challenge) } return nil } +// SetChallengeProvidersDNS specifies one or more provider p that can solve the DNS challenge type. +func (c *Client) SetChallengeProvidersDNS(challenge Challenge, p []ChallengeProvider) error { + c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, providers: p} + return nil +} + // SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges. // If this option is not used, the default port 80 and all interfaces will be used. // To only specify a port and no interface use the ":port" notation. diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index 30f2170ff5..3bb4612717 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -9,7 +9,6 @@ import ( "net" "strings" "time" - "github.com/miekg/dns" "golang.org/x/net/publicsuffix" ) @@ -69,13 +68,13 @@ func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) { type dnsChallenge struct { jws *jws validate validateFunc - provider ChallengeProvider + providers []ChallengeProvider } func (s *dnsChallenge) Solve(chlng challenge, domain string) error { logf("[INFO][%s] acme: Trying to solve DNS-01", domain) - if s.provider == nil { + if len(s.providers) == 0 { return errors.New("No DNS Provider configured") } @@ -85,14 +84,22 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { return err } - err = s.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("Error presenting token: %s", err) + for _, v := range s.providers { + err = v.Present(domain, chlng.Token, keyAuth) + + if err != nil { + 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) + + for _, v := range s.providers { + err := v.CleanUp(domain, chlng.Token, keyAuth) + + if err != nil { + log.Printf("Error cleaning up %s: %v ", domain, err) + } } }() @@ -101,16 +108,30 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) var timeout, interval time.Duration - switch provider := s.provider.(type) { - case ChallengeProviderTimeout: - timeout, interval = provider.Timeout() - default: - timeout, interval = 60*time.Second, 2*time.Second + + for _, v := range s.providers { + var tiou, inter time.Duration + + switch provider := v.(type) { + case ChallengeProviderTimeout: + tiou, inter = provider.Timeout() + default: + tiou, inter = 60*time.Second, 2*time.Second + } + + // Check if interval or timeout is greater, we take the highest number + if timeout < tiou { + timeout = tiou + } + if interval < inter { + interval = inter + } } err = WaitFor(timeout, interval, func() (bool, error) { return PreCheckDNS(fqdn, value) }) + if err != nil { return err } diff --git a/cli.go b/cli.go index 61c08d455c..659380b6c2 100644 --- a/cli.go +++ b/cli.go @@ -196,6 +196,9 @@ Here is an example bash command using the CloudFlare DNS provider: CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ lego --dns cloudflare --domains www.example.com --email me@bar.com run +Here is an example bash command using multiple DNS provider: + $ lego --dns gcloud,route53 --domains www.example.com --email me@bar.com run + `) w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) diff --git a/cli_handlers.go b/cli_handlers.go index 858d710006..48122e539f 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -114,12 +114,20 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { } if c.GlobalIsSet("dns") { - provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) - if err != nil { - logger().Fatal(err) + // Get providers + var providers []acme.ChallengeProvider + + for _, v := range strings.Split(c.GlobalString("dns"), ",") { + provider, err := dns.NewDNSChallengeProviderByName(v) + + if err != nil { + logger().Fatal(err) + } + + providers = append(providers, provider) } - client.SetChallengeProvider(acme.DNS01, provider) + client.SetChallengeProvidersDNS(acme.DNS01, providers) // --dns=foo indicates that the user specifically want to do a DNS challenge // infer that the user also wants to exclude all other challenges