diff --git a/acme/acme.go b/acme/acme.go index cee66924d..acbd946de 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -117,14 +117,18 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl if err != nil { return err } + if len(a.Storage) == 0 { return errors.New("Empty Store, please provide a key for certs storage") } + a.checkOnDemandDomain = checkOnDemandDomain a.dynamicCerts = certs + tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate) tlsConfig.GetCertificate = a.getCertificate a.TLSConfig = tlsConfig + listener := func(object cluster.Object) error { account := object.(*Account) account.Init() @@ -404,6 +408,7 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { if len(a.CAServer) > 0 { caServer = a.CAServer } + client, err := acme.NewClient(caServer, account, account.KeyType) if err != nil { return nil, err @@ -425,19 +430,19 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { client.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) err = client.SetChallengeProvider(acme.DNS01, provider) - } else if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 { + return client, err + } + + if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 { log.Debug("Using HTTP Challenge provider.") + client.ExcludeChallenges([]acme.Challenge{acme.DNS01}) a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store} err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider) - } else { - return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") + return client, err } - if err != nil { - return nil, err - } - return client, nil + return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") } func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { diff --git a/acme/challenge_http_provider.go b/acme/challenge_http_provider.go index 901fa5594..333221569 100644 --- a/acme/challenge_http_provider.go +++ b/acme/challenge_http_provider.go @@ -23,10 +23,12 @@ func (c *challengeHTTPProvider) getTokenValue(token, domain string) []byte { log.Debugf("Looking for an existing ACME challenge for token %v...", token) c.lock.RLock() defer c.lock.RUnlock() + account := c.store.Get().(*Account) if account.HTTPChallenge == nil { return []byte{} } + var result []byte operation := func() error { var ok bool @@ -35,9 +37,11 @@ func (c *challengeHTTPProvider) getTokenValue(token, domain string) []byte { } return nil } + notify := func(err error, time time.Duration) { log.Errorf("Error getting challenge for token retrying in %s", time) } + ebo := backoff.NewExponentialBackOff() ebo.MaxElapsedTime = 60 * time.Second err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify) @@ -52,18 +56,23 @@ func (c *challengeHTTPProvider) Present(domain, token, keyAuth string) error { log.Debugf("Challenge Present %s", domain) c.lock.Lock() defer c.lock.Unlock() + transaction, object, err := c.store.Begin() if err != nil { return err } + account := object.(*Account) if account.HTTPChallenge == nil { account.HTTPChallenge = map[string]map[string][]byte{} } + if _, ok := account.HTTPChallenge[token]; !ok { account.HTTPChallenge[token] = map[string][]byte{} } + account.HTTPChallenge[token][domain] = []byte(keyAuth) + return transaction.Commit(account) } @@ -71,10 +80,12 @@ func (c *challengeHTTPProvider) CleanUp(domain, token, keyAuth string) error { log.Debugf("Challenge CleanUp %s", domain) c.lock.Lock() defer c.lock.Unlock() + transaction, object, err := c.store.Begin() if err != nil { return err } + account := object.(*Account) if _, ok := account.HTTPChallenge[token]; ok { if _, domainOk := account.HTTPChallenge[token][domain]; domainOk { @@ -84,6 +95,7 @@ func (c *challengeHTTPProvider) CleanUp(domain, token, keyAuth string) error { delete(account.HTTPChallenge, token) } } + return transaction.Commit(account) } diff --git a/provider/acme/provider.go b/provider/acme/provider.go index d322628cc..a6600cf02 100644 --- a/provider/acme/provider.go +++ b/provider/acme/provider.go @@ -211,6 +211,7 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati } log.Debugf("Loading ACME certificates %+v...", uncheckedDomains) + client, err := p.getClient() if err != nil { return nil, fmt.Errorf("cannot get ACME client %v", err) @@ -226,6 +227,7 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 { return nil, fmt.Errorf("domains %v generate certificate with no value: %v", uncheckedDomains, certificate) } + log.Debugf("Certificates obtained for domains %+v", uncheckedDomains) if len(uncheckedDomains) > 1 { @@ -241,72 +243,82 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati func (p *Provider) getClient() (*acme.Client, error) { p.clientMutex.Lock() defer p.clientMutex.Unlock() - var account *Account - if p.client == nil { - var err error - account, err = p.initAccount() - if err != nil { - return nil, err - } - log.Debug("Building ACME client...") - caServer := "https://acme-v02.api.letsencrypt.org/directory" - if len(p.CAServer) > 0 { - caServer = p.CAServer - } - log.Debugf(caServer) - client, err := acme.NewClient(caServer, account, account.KeyType) - if err != nil { - return nil, err - } - if account.GetRegistration() == nil { - // New users will need to register; be sure to save it - log.Info("Register...") - reg, err := client.Register(true) - if err != nil { - return nil, err - } - account.Registration = reg - } - - // Save the account once before all the certificates generation/storing - // No certificate can be generated if account is not initialized - err = p.Store.SaveAccount(account) - if err != nil { - return nil, err - } - - if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 { - log.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider) - - err = dnsOverrideDelay(p.DNSChallenge.DelayBeforeCheck) - if err != nil { - return nil, err - } - - var provider acme.ChallengeProvider - provider, err = dns.NewDNSChallengeProviderByName(p.DNSChallenge.Provider) - if err != nil { - return nil, err - } - - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) - err = client.SetChallengeProvider(acme.DNS01, provider) - if err != nil { - return nil, err - } - } else if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 { - log.Debug("Using HTTP Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.DNS01}) - err = client.SetChallengeProvider(acme.HTTP01, p) - if err != nil { - return nil, err - } - } else { - return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") - } - p.client = client + if p.client != nil { + return p.client, nil } + + account, err := p.initAccount() + if err != nil { + return nil, err + } + + log.Debug("Building ACME client...") + + caServer := "https://acme-v02.api.letsencrypt.org/directory" + if len(p.CAServer) > 0 { + caServer = p.CAServer + } + log.Debug(caServer) + + client, err := acme.NewClient(caServer, account, account.KeyType) + if err != nil { + return nil, err + } + + // New users will need to register; be sure to save it + if account.GetRegistration() == nil { + log.Info("Register...") + + reg, err := client.Register(true) + if err != nil { + return nil, err + } + + account.Registration = reg + } + + // Save the account once before all the certificates generation/storing + // No certificate can be generated if account is not initialized + err = p.Store.SaveAccount(account) + if err != nil { + return nil, err + } + + if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 { + log.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider) + + err = dnsOverrideDelay(p.DNSChallenge.DelayBeforeCheck) + if err != nil { + return nil, err + } + + var provider acme.ChallengeProvider + provider, err = dns.NewDNSChallengeProviderByName(p.DNSChallenge.Provider) + if err != nil { + return nil, err + } + + client.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) + + err = client.SetChallengeProvider(acme.DNS01, provider) + if err != nil { + return nil, err + } + } else if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 { + log.Debug("Using HTTP Challenge provider.") + + client.ExcludeChallenges([]acme.Challenge{acme.DNS01}) + + err = client.SetChallengeProvider(acme.HTTP01, p) + if err != nil { + return nil, err + } + } else { + return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") + } + + p.client = client return p.client, nil }