Submit all dns records up front, then validate serially (#607)
This commit is contained in:
parent
6588bc6fa5
commit
de3accf531
2 changed files with 91 additions and 17 deletions
|
@ -41,6 +41,17 @@ type solver interface {
|
||||||
Solve(challenge challenge, domain string) error
|
Solve(challenge challenge, domain string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interface for challenges like dns, where we can set a record in advance for ALL challenges.
|
||||||
|
// This saves quite a bit of time vs creating the records and solving them serially.
|
||||||
|
type presolver interface {
|
||||||
|
PreSolve(challenge challenge, domain string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface for challenges like dns, where we can solve all the challenges before to delete them.
|
||||||
|
type cleanup interface {
|
||||||
|
CleanUp(challenge challenge, domain string) error
|
||||||
|
}
|
||||||
|
|
||||||
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
|
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
|
||||||
|
|
||||||
// Client is the user-friendy way to ACME
|
// Client is the user-friendy way to ACME
|
||||||
|
@ -548,29 +559,75 @@ func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, err
|
||||||
return orderRes, nil
|
return orderRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// an authz with the solver we have chosen and the index of the challenge associated with it
|
||||||
|
type selectedAuthSolver struct {
|
||||||
|
authz authorization
|
||||||
|
challengeIndex int
|
||||||
|
solver solver
|
||||||
|
}
|
||||||
|
|
||||||
// Looks through the challenge combinations to find a solvable match.
|
// Looks through the challenge combinations to find a solvable match.
|
||||||
// Then solves the challenges in series and returns.
|
// Then solves the challenges in series and returns.
|
||||||
func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
||||||
failures := make(ObtainError)
|
failures := make(ObtainError)
|
||||||
|
|
||||||
// loop through the resources, basically through the domains.
|
authSolvers := []*selectedAuthSolver{}
|
||||||
|
|
||||||
|
// loop through the resources, basically through the domains. First pass just selects a solver for each authz.
|
||||||
for _, authz := range authorizations {
|
for _, authz := range authorizations {
|
||||||
if authz.Status == "valid" {
|
if authz.Status == "valid" {
|
||||||
// Boulder might recycle recent validated authz (see issue #267)
|
// Boulder might recycle recent validated authz (see issue #267)
|
||||||
log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// no solvers - no solving
|
|
||||||
if i, solver := c.chooseSolver(authz, authz.Identifier.Value); solver != nil {
|
if i, solver := c.chooseSolver(authz, authz.Identifier.Value); solver != nil {
|
||||||
err := solver.Solve(authz.Challenges[i], authz.Identifier.Value)
|
authSolvers = append(authSolvers, &selectedAuthSolver{
|
||||||
if err != nil {
|
authz: authz,
|
||||||
//c.disableAuthz(authz.Identifier)
|
challengeIndex: i,
|
||||||
|
solver: solver,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for all valid presolvers, first submit the challenges so they have max time to propigate
|
||||||
|
for _, item := range authSolvers {
|
||||||
|
authz := item.authz
|
||||||
|
i := item.challengeIndex
|
||||||
|
if presolver, ok := item.solver.(presolver); ok {
|
||||||
|
if err := presolver.PreSolve(authz.Challenges[i], authz.Identifier.Value); err != nil {
|
||||||
failures[authz.Identifier.Value] = err
|
failures[authz.Identifier.Value] = err
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
//c.disableAuthz(authz)
|
}
|
||||||
failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value)
|
|
||||||
|
defer func() {
|
||||||
|
// clean all created TXT records
|
||||||
|
for _, item := range authSolvers {
|
||||||
|
if cleanup, ok := item.solver.(cleanup); ok {
|
||||||
|
if failures[item.authz.Identifier.Value] != nil {
|
||||||
|
// already failed in previous loop
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := cleanup.CleanUp(item.authz.Challenges[item.challengeIndex], item.authz.Identifier.Value)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Error cleaning up %s: %v ", item.authz.Identifier.Value, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// finally solve all challenges for real
|
||||||
|
for _, item := range authSolvers {
|
||||||
|
authz := item.authz
|
||||||
|
i := item.challengeIndex
|
||||||
|
if failures[authz.Identifier.Value] != nil {
|
||||||
|
// already failed in previous loop
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := item.solver.Solve(authz.Challenges[i], authz.Identifier.Value); err != nil {
|
||||||
|
failures[authz.Identifier.Value] = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,8 +71,10 @@ type dnsChallenge struct {
|
||||||
provider ChallengeProvider
|
provider ChallengeProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
// PreSolve just submits the txt record to the dns provider. It does not validate record propagation, or
|
||||||
log.Infof("[%s] acme: Trying to solve DNS-01", domain)
|
// do anything at all with the acme server.
|
||||||
|
func (s *dnsChallenge) PreSolve(chlng challenge, domain string) error {
|
||||||
|
log.Infof("[%s] acme: Preparing to solve DNS-01", domain)
|
||||||
|
|
||||||
if s.provider == nil {
|
if s.provider == nil {
|
||||||
return errors.New("no DNS Provider configured")
|
return errors.New("no DNS Provider configured")
|
||||||
|
@ -88,12 +90,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error presenting token: %s", err)
|
return fmt.Errorf("error presenting token: %s", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
return nil
|
||||||
if err != nil {
|
}
|
||||||
log.Warnf("Error cleaning up %s: %v ", domain, err)
|
|
||||||
}
|
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||||
}()
|
log.Infof("[%s] acme: Trying to solve DNS-01", domain)
|
||||||
|
|
||||||
|
// Generate the Key Authorization for the challenge
|
||||||
|
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
@ -117,6 +125,15 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||||
return s.validate(s.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
return s.validate(s.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CleanUp cleans the challenge
|
||||||
|
func (s *dnsChallenge) CleanUp(chlng challenge, domain string) error {
|
||||||
|
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
|
}
|
||||||
|
|
||||||
// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers.
|
// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers.
|
||||||
func checkDNSPropagation(fqdn, value string) (bool, error) {
|
func checkDNSPropagation(fqdn, value string) (bool, error) {
|
||||||
// Initial attempt to resolve at the recursive NS
|
// Initial attempt to resolve at the recursive NS
|
||||||
|
|
Loading…
Reference in a new issue