diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 9fe1854fc..72bf31c65 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -175,7 +175,7 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s log.Debugf("Global configuration loaded %s", string(jsonConf)) if acme.IsEnabled() { store := acme.NewLocalStore(acme.Get().Storage) - acme.Get().Store = &store + acme.Get().Store = store } svr := server.NewServer(*globalConfiguration, configuration.NewProviderAggregator(globalConfiguration)) if acme.IsEnabled() && acme.Get().OnHostRule { diff --git a/provider/acme/challenge.go b/provider/acme/challenge.go index 0ed638131..9e19192d4 100644 --- a/provider/acme/challenge.go +++ b/provider/acme/challenge.go @@ -34,15 +34,9 @@ func getTokenValue(token, domain string, store Store) []byte { var result []byte operation := func() error { - var ok bool - httpChallenges, err := store.GetHTTPChallenges() - if err != nil { - return fmt.Errorf("HTTPChallenges not available : %s", err) - } - if result, ok = httpChallenges[token][domain]; !ok { - return fmt.Errorf("cannot find challenge for token %v", token) - } - return nil + var err error + result, err = store.GetHTTPChallengeToken(token, domain) + return err } notify := func(err error, time time.Duration) { @@ -60,40 +54,9 @@ func getTokenValue(token, domain string, store Store) []byte { } func presentHTTPChallenge(domain, token, keyAuth string, store Store) error { - httpChallenges, err := store.GetHTTPChallenges() - if err != nil { - return fmt.Errorf("unable to get HTTPChallenges : %s", err) - } - - if httpChallenges == nil { - httpChallenges = map[string]map[string][]byte{} - } - - if _, ok := httpChallenges[token]; !ok { - httpChallenges[token] = map[string][]byte{} - } - - httpChallenges[token][domain] = []byte(keyAuth) - - return store.SaveHTTPChallenges(httpChallenges) + return store.SetHTTPChallengeToken(token, domain, []byte(keyAuth)) } func cleanUpHTTPChallenge(domain, token string, store Store) error { - httpChallenges, err := store.GetHTTPChallenges() - if err != nil { - return fmt.Errorf("unable to get HTTPChallenges : %s", err) - } - - log.Debugf("Challenge CleanUp for domain %s", domain) - - if _, ok := httpChallenges[token]; ok { - if _, domainOk := httpChallenges[token][domain]; domainOk { - delete(httpChallenges[token], domain) - } - if len(httpChallenges[token]) == 0 { - delete(httpChallenges, token) - } - return store.SaveHTTPChallenges(httpChallenges) - } - return nil + return store.RemoveHTTPChallengeToken(token, domain) } diff --git a/provider/acme/local_store.go b/provider/acme/local_store.go index 66541bf3b..2dc6d29dc 100644 --- a/provider/acme/local_store.go +++ b/provider/acme/local_store.go @@ -2,9 +2,11 @@ package acme import ( "encoding/json" + "fmt" "io/ioutil" "os" "regexp" + "sync" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" @@ -17,11 +19,12 @@ type LocalStore struct { filename string storedData *StoredData SaveDataChan chan *StoredData `json:"-"` + lock sync.RWMutex } // NewLocalStore initializes a new LocalStore with a file name -func NewLocalStore(filename string) LocalStore { - store := LocalStore{filename: filename, SaveDataChan: make(chan *StoredData)} +func NewLocalStore(filename string) *LocalStore { + store := &LocalStore{filename: filename, SaveDataChan: make(chan *StoredData)} store.listenSaveAction() return store } @@ -149,13 +152,59 @@ func (s *LocalStore) SaveCertificates(certificates []*Certificate) error { return nil } -// GetHTTPChallenges returns ACME HTTP Challenges list -func (s *LocalStore) GetHTTPChallenges() (map[string]map[string][]byte, error) { - return s.storedData.HTTPChallenges, nil +// GetHTTPChallengeToken Get the http challenge token from the store +func (s *LocalStore) GetHTTPChallengeToken(token, domain string) ([]byte, error) { + s.lock.RLock() + defer s.lock.RUnlock() + + if s.storedData.HTTPChallenges == nil { + s.storedData.HTTPChallenges = map[string]map[string][]byte{} + } + + if _, ok := s.storedData.HTTPChallenges[token]; !ok { + return nil, fmt.Errorf("cannot find challenge for token %v", token) + } + + result, ok := s.storedData.HTTPChallenges[token][domain] + if !ok { + return nil, fmt.Errorf("cannot find challenge for token %v", token) + } + return result, nil } -// SaveHTTPChallenges stores ACME HTTP Challenges list -func (s *LocalStore) SaveHTTPChallenges(httpChallenges map[string]map[string][]byte) error { - s.storedData.HTTPChallenges = httpChallenges +// SetHTTPChallengeToken Set the http challenge token in the store +func (s *LocalStore) SetHTTPChallengeToken(token, domain string, keyAuth []byte) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.storedData.HTTPChallenges == nil { + s.storedData.HTTPChallenges = map[string]map[string][]byte{} + } + + if _, ok := s.storedData.HTTPChallenges[token]; !ok { + s.storedData.HTTPChallenges[token] = map[string][]byte{} + } + + s.storedData.HTTPChallenges[token][domain] = []byte(keyAuth) + return nil +} + +// RemoveHTTPChallengeToken Remove the http challenge token in the store +func (s *LocalStore) RemoveHTTPChallengeToken(token, domain string) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.storedData.HTTPChallenges == nil { + return nil + } + + if _, ok := s.storedData.HTTPChallenges[token]; ok { + if _, domainOk := s.storedData.HTTPChallenges[token][domain]; domainOk { + delete(s.storedData.HTTPChallenges[token], domain) + } + if len(s.storedData.HTTPChallenges[token]) == 0 { + delete(s.storedData.HTTPChallenges, token) + } + } return nil } diff --git a/provider/acme/store.go b/provider/acme/store.go index 77062d722..af28dcf5e 100644 --- a/provider/acme/store.go +++ b/provider/acme/store.go @@ -13,6 +13,7 @@ type Store interface { SaveAccount(*Account) error GetCertificates() ([]*Certificate, error) SaveCertificates([]*Certificate) error - GetHTTPChallenges() (map[string]map[string][]byte, error) - SaveHTTPChallenges(map[string]map[string][]byte) error + GetHTTPChallengeToken(token, domain string) ([]byte, error) + SetHTTPChallengeToken(token, domain string, keyAuth []byte) error + RemoveHTTPChallengeToken(token, domain string) error }