Split off validation function.
This is a loop that interacts with the ACME server, not the individual challenges. Also switch to exponential back-off polling for good measure.
This commit is contained in:
parent
237689b0cf
commit
58a2fd2267
3 changed files with 81 additions and 92 deletions
|
@ -670,3 +670,82 @@ func parseLinks(links []string) map[string]string {
|
|||
|
||||
return linkMap
|
||||
}
|
||||
|
||||
// validate makes the ACME server start validating a
|
||||
// challenge response, only returning once it is done.
|
||||
func validate(j *jws, uri string, chlng challenge) error {
|
||||
var challengeResponse challenge
|
||||
|
||||
if err := postJSON(j, uri, chlng, &challengeResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
interval := 1 * time.Second
|
||||
maxInterval := 15 * time.Minute
|
||||
|
||||
// After the path is sent, the ACME server will access our server.
|
||||
// Repeatedly check the server for an updated status on our request.
|
||||
for {
|
||||
switch challengeResponse.Status {
|
||||
case "valid":
|
||||
logf("The server validated our request")
|
||||
return nil
|
||||
case "pending":
|
||||
break
|
||||
case "invalid":
|
||||
return errors.New("The server could not validate our request.")
|
||||
default:
|
||||
return errors.New("The server returned an unexpected state.")
|
||||
}
|
||||
|
||||
// Poll with exponential back-off.
|
||||
time.Sleep(interval)
|
||||
interval *= 2
|
||||
if interval > maxInterval {
|
||||
interval = maxInterval
|
||||
}
|
||||
|
||||
if err := getJSON(uri, &challengeResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getJSON performs an HTTP GET request and parses the response body
|
||||
// as JSON, into the provided respBody object.
|
||||
func getJSON(uri string, respBody interface{}) error {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get %q: %v", uri, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return handleHTTPError(resp)
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(respBody)
|
||||
}
|
||||
|
||||
// postJSON performs an HTTP POST request and parses the response body
|
||||
// as JSON, into the provided respBody object.
|
||||
func postJSON(j *jws, uri string, reqBody, respBody interface{}) error {
|
||||
jsonBytes, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return errors.New("Failed to marshal network message...")
|
||||
}
|
||||
|
||||
resp, err := j.post(uri, jsonBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to post JWS message. -> %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return handleHTTPError(resp)
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(respBody)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type httpChallenge struct {
|
||||
|
@ -47,49 +44,7 @@ func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
|||
close(s.end)
|
||||
}()
|
||||
|
||||
jsonBytes, err := json.Marshal(challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
if err != nil {
|
||||
return errors.New("Failed to marshal network message...")
|
||||
}
|
||||
|
||||
// Tell the server we handle HTTP-01
|
||||
resp, err := s.jws.post(chlng.URI, jsonBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to post JWS message. -> %v", err)
|
||||
}
|
||||
|
||||
// After the path is sent, the ACME server will access our server.
|
||||
// Repeatedly check the server for an updated status on our request.
|
||||
var challengeResponse challenge
|
||||
Loop:
|
||||
for {
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return handleHTTPError(resp)
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&challengeResponse)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch challengeResponse.Status {
|
||||
case "valid":
|
||||
logf("The server validated our request")
|
||||
break Loop
|
||||
case "pending":
|
||||
break
|
||||
case "invalid":
|
||||
return errors.New("The server could not validate our request.")
|
||||
default:
|
||||
return errors.New("The server returned an unexpected state.")
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
resp, err = http.Get(chlng.URI)
|
||||
}
|
||||
|
||||
return nil
|
||||
return validate(s.jws, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
||||
|
||||
func (s *httpChallenge) startHTTPServer(domain string, token string, keyAuth string) {
|
||||
|
|
|
@ -5,12 +5,9 @@ import (
|
|||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tlsSNIChallenge struct {
|
||||
|
@ -57,49 +54,7 @@ func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
|||
close(t.end)
|
||||
}()
|
||||
|
||||
jsonBytes, err := json.Marshal(challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
if err != nil {
|
||||
return errors.New("Failed to marshal network message...")
|
||||
}
|
||||
|
||||
// Tell the server we handle TLS-SNI-01
|
||||
resp, err := t.jws.post(chlng.URI, jsonBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to post JWS message. -> %v", err)
|
||||
}
|
||||
|
||||
// After the path is sent, the ACME server will access our server.
|
||||
// Repeatedly check the server for an updated status on our request.
|
||||
var challengeResponse challenge
|
||||
Loop:
|
||||
for {
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return handleHTTPError(resp)
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&challengeResponse)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch challengeResponse.Status {
|
||||
case "valid":
|
||||
logf("The server validated our request")
|
||||
break Loop
|
||||
case "pending":
|
||||
break
|
||||
case "invalid":
|
||||
return errors.New("The server could not validate our request.")
|
||||
default:
|
||||
return errors.New("The server returned an unexpected state.")
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
resp, err = http.Get(chlng.URI)
|
||||
}
|
||||
|
||||
return nil
|
||||
return validate(t.jws, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
||||
|
||||
func (t *tlsSNIChallenge) generateCertificate(keyAuth string) (tls.Certificate, error) {
|
||||
|
|
Loading…
Reference in a new issue