diff --git a/acme/client.go b/acme/client.go index afa7f667..d36ae1f8 100644 --- a/acme/client.go +++ b/acme/client.go @@ -234,10 +234,13 @@ func (c *Client) ObtainSANCertificate(domains []string, bundle bool) (Certificat } challenges, failures := c.getChallenges(domains) - if len(challenges) == 0 { + // If any challenge fails - return. Do not generate partial SAN certificates. + if len(failures) > 0 { return CertificateResource{}, failures } + challenges = reorderAuthorizations(domains, challenges) + errs := c.solveChallenges(challenges) // If any challenge fails - return. Do not generate partial SAN certificates. if len(errs) > 0 { @@ -246,9 +249,9 @@ func (c *Client) ObtainSANCertificate(domains []string, bundle bool) (Certificat logf("[INFO] acme: Validations succeeded; requesting certificates") - cert, err := c.requestCertificate(succeededChallenges, bundle) + cert, err := c.requestCertificate(challenges, bundle) if err != nil { - for _, chln := range succeededChallenges { + for _, chln := range challenges { failures[chln.Domain] = err } } @@ -660,3 +663,20 @@ func parseLinks(links []string) map[string]string { return linkMap } + +func reorderAuthorizations(domains []string, challenges []authorizationResource) []authorizationResource { + // restore order of challenges + for i, domain := range domains { + if domain == challenges[i].Domain { + continue + } + + for j, chlng := range challenges { + if chlng.Domain == domain { + challenges[i], challenges[j] = challenges[j], challenges[i] + } + } + } + + return challenges +} diff --git a/acme/client_test.go b/acme/client_test.go index 1a29bf45..a7cd8ad8 100644 --- a/acme/client_test.go +++ b/acme/client_test.go @@ -4,6 +4,8 @@ import ( "crypto/rand" "crypto/rsa" "encoding/json" + "fmt" + mrand "math/rand" "net/http" "net/http/httptest" "testing" @@ -68,3 +70,33 @@ type mockUser struct { func (u mockUser) GetEmail() string { return u.email } func (u mockUser) GetRegistration() *RegistrationResource { return u.regres } func (u mockUser) GetPrivateKey() *rsa.PrivateKey { return u.privatekey } + +func TestReorderAuthorizations(t *testing.T) { + // generate fake domains + var domains []string + for i := 0; i < 30; i++ { + domains = append(domains, fmt.Sprintf("example%d.com", i)) + } + + // generate authorizationResources from the domains + var challenges []authorizationResource + for _, domain := range domains { + challenges = append(challenges, authorizationResource{Domain: domain}) + } + + // shuffle the challenges slice + for i := len(challenges) - 1; i > 0; i-- { + j := mrand.Intn(i + 1) + challenges[i], challenges[j] = challenges[j], challenges[i] + } + + // reorder the challenges + reordered := reorderAuthorizations(domains, challenges) + + // test if reordering was successfull + for i, domain := range domains { + if domain != reordered[i].Domain { + t.Errorf("Expected reordered[%d] to equal %s but was %s", i, domain, reordered[i].Domain) + } + } +}