From f9734c4a9349b0c9727b6f5cd4dc81f1b9e44da3 Mon Sep 17 00:00:00 2001 From: Manuel Gysin Date: Mon, 12 Dec 2016 00:23:48 +0100 Subject: [PATCH 1/4] Added multiple DNS provider support --- acme/client.go | 7 +++++-- acme/dns_challenge.go | 48 +++++++++++++++++++++++++++++++------------ cli.go | 3 +++ cli_handlers.go | 16 +++++++++++---- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/acme/client.go b/acme/client.go index 9f837af366..b75ffe221b 100644 --- a/acme/client.go +++ b/acme/client.go @@ -104,14 +104,17 @@ 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 } +func (c *Client) SetChallengeProviderDNS(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..f06c7c03f9 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -69,13 +69,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 s.providers == nil { return errors.New("No DNS Provider configured") } @@ -85,14 +85,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 +109,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..2385f7a2a0 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.SetChallengeProviderDNS(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 From 4db00bc3a239665114a3b68918b13e4e7375352c Mon Sep 17 00:00:00 2001 From: Manuel Gysin Date: Tue, 13 Dec 2016 17:25:04 +0100 Subject: [PATCH 2/4] Update client.go --- acme/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acme/client.go b/acme/client.go index b75ffe221b..eee6ff954d 100644 --- a/acme/client.go +++ b/acme/client.go @@ -110,7 +110,8 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) return nil } -func (c *Client) SetChallengeProviderDNS(challenge Challenge, p []ChallengeProvider) error { +// 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 } From c55ffa20feb1f1ff767c6552000ec079a01b72e4 Mon Sep 17 00:00:00 2001 From: Manuel Gysin Date: Tue, 13 Dec 2016 17:32:33 +0100 Subject: [PATCH 3/4] Update dns_challenge.go --- acme/dns_challenge.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index f06c7c03f9..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" ) @@ -75,7 +74,7 @@ type dnsChallenge struct { func (s *dnsChallenge) Solve(chlng challenge, domain string) error { logf("[INFO][%s] acme: Trying to solve DNS-01", domain) - if s.providers == nil { + if len(s.providers) == 0 { return errors.New("No DNS Provider configured") } From ff94c948eefabfd19a10cee1e116895db1e6d546 Mon Sep 17 00:00:00 2001 From: Manuel Gysin Date: Tue, 13 Dec 2016 17:33:09 +0100 Subject: [PATCH 4/4] Update cli_handlers.go --- cli_handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli_handlers.go b/cli_handlers.go index 2385f7a2a0..48122e539f 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -127,7 +127,7 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { providers = append(providers, provider) } - client.SetChallengeProviderDNS(acme.DNS01, providers) + 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