acme/autocert: surface details of acme.AuthorizationError
Fixes golang/go#19800
@@ -658,6 +658,8 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
}
}()
+ // errs accumulates challenge failure errors, printed if all fail
+ errs := make(map[*acme.Challenge]error)
var nextTyp int // challengeType index of the next challenge type to try
for {
// Start domain authorization and get the challenge.
@@ -683,24 +685,32 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
nextTyp++
}
if chal == nil {
- return fmt.Errorf("acme/autocert: unable to authorize %q; tried %q", domain, challengeTypes)
+ errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
+ for chal, err := range errs {
+ errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
+ }
+ return errors.New(errorMsg)
}
cleanup, err := m.fulfill(ctx, client, chal)
if err != nil {
+ errs[chal] = err
continue
}
defer cleanup()
if _, err := client.Accept(ctx, chal); err != nil {
+ errs[chal] = err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
- if _, err := client.WaitAuthorization(ctx, authz.URI); err == nil {
+ if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
+ errs[chal] = err
+ continue
+ }
delete(pendingAuthzs, authz.URI)
return nil
}
}
-}
// fulfill provisions a response to the challenge chal.
// The cleanup is non-nil only if provisioning succeeded.
