1
0
Fork 0

ACME DNS challenges

This commit is contained in:
Ludovic Fernandez 2018-10-10 16:28:04 +02:00 committed by Traefiker Bot
parent 7a2592b2fa
commit 5bdf8a5ea3
127 changed files with 24386 additions and 739 deletions

View file

@ -26,6 +26,9 @@ const (
// “new-reg”, “new-authz” and “new-cert” endpoints. From the documentation the
// limitation is 20 requests per second, but using 20 as value doesn't work but 18 do
overallRequestLimit = 18
statusValid = "valid"
statusInvalid = "invalid"
)
// User interface is to be implemented by users of this library.
@ -43,7 +46,7 @@ type solver interface {
// 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 {
type preSolver interface {
PreSolve(challenge challenge, domain string) error
}
@ -502,9 +505,9 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
// Start by checking to see if the certificate was based off a CSR, and
// use that if it's defined.
if len(cert.CSR) > 0 {
csr, err := pemDecodeTox509CSR(cert.CSR)
if err != nil {
return nil, err
csr, errP := pemDecodeTox509CSR(cert.CSR)
if errP != nil {
return nil, errP
}
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
return newCert, failures
@ -537,7 +540,6 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
}
func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) {
var identifiers []identifier
for _, domain := range domains {
identifiers = append(identifiers, identifier{Type: "dns", Value: domain})
@ -577,16 +579,16 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
// loop through the resources, basically through the domains. First pass just selects a solver for each authz.
for _, authz := range authorizations {
if authz.Status == "valid" {
if authz.Status == statusValid {
// Boulder might recycle recent validated authz (see issue #267)
log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
continue
}
if i, solver := c.chooseSolver(authz, authz.Identifier.Value); solver != nil {
if i, solvr := c.chooseSolver(authz, authz.Identifier.Value); solvr != nil {
authSolvers = append(authSolvers, &selectedAuthSolver{
authz: authz,
challengeIndex: i,
solver: solver,
solver: solvr,
})
} else {
failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value)
@ -597,7 +599,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
for _, item := range authSolvers {
authz := item.authz
i := item.challengeIndex
if presolver, ok := item.solver.(presolver); ok {
if presolver, ok := item.solver.(preSolver); ok {
if err := presolver.PreSolve(authz.Challenges[i], authz.Identifier.Value); err != nil {
failures[authz.Identifier.Value] = err
}
@ -607,12 +609,12 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
defer func() {
// clean all created TXT records
for _, item := range authSolvers {
if cleanup, ok := item.solver.(cleanup); ok {
if clean, 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)
err := clean.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)
}
@ -755,7 +757,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
return nil, err
}
if retOrder.Status == "invalid" {
if retOrder.Status == statusInvalid {
return nil, err
}
@ -765,7 +767,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
PrivateKey: privateKeyPem,
}
if retOrder.Status == "valid" {
if retOrder.Status == statusValid {
// if the certificate is available right away, short cut!
ok, err := c.checkCertResponse(retOrder, &certRes, bundle)
if err != nil {
@ -809,9 +811,8 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
// should already have the Domain (common name) field populated. If bundle is
// true, the certificate will be bundled with the issuer's cert.
func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) {
switch order.Status {
case "valid":
case statusValid:
resp, err := httpGet(order.Certificate)
if err != nil {
return false, err
@ -860,7 +861,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
case "processing":
return false, nil
case "invalid":
case statusInvalid:
return false, errors.New("order has invalid state: invalid")
default:
return false, nil
@ -922,12 +923,12 @@ func validate(j *jws, domain, uri string, c challenge) error {
// Repeatedly check the server for an updated status on our request.
for {
switch chlng.Status {
case "valid":
case statusValid:
log.Infof("[%s] The server validated our request", domain)
return nil
case "pending":
case "processing":
case "invalid":
case statusInvalid:
return handleChallengeError(chlng)
default:
return errors.New("the server returned an unexpected state")

View file

@ -81,20 +81,20 @@ func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
return nil, nil, errors.New("no issuing certificate URL")
}
resp, err := httpGet(issuedCert.IssuingCertificateURL[0])
if err != nil {
return nil, nil, err
resp, errC := httpGet(issuedCert.IssuingCertificateURL[0])
if errC != nil {
return nil, nil, errC
}
defer resp.Body.Close()
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
if err != nil {
return nil, nil, err
issuerBytes, errC := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
if errC != nil {
return nil, nil, errC
}
issuerCert, err := x509.ParseCertificate(issuerBytes)
if err != nil {
return nil, nil, err
issuerCert, errC := x509.ParseCertificate(issuerBytes)
if errC != nil {
return nil, nil, errC
}
// Insert it into the slice on position 0
@ -258,15 +258,6 @@ func pemDecode(data []byte) (*pem.Block, error) {
return pemBlock, nil
}
func pemDecodeTox509(pem []byte) (*x509.Certificate, error) {
pemBlock, err := pemDecode(pem)
if pemBlock == nil {
return nil, err
}
return x509.ParseCertificate(pemBlock.Bytes)
}
func pemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) {
pemBlock, err := pemDecode(pem)
if pemBlock == nil {

View file

@ -196,7 +196,7 @@ func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, erro
}
if !found {
return false, fmt.Errorf("NS %s did not return the expected TXT record", ns)
return false, fmt.Errorf("NS %s did not return the expected TXT record [fqdn: %s]", ns, fqdn)
}
}

View file

@ -148,29 +148,25 @@ func getJSON(uri string, respBody interface{}) (http.Header, error) {
func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, error) {
jsonBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, errors.New("Failed to marshal network message")
return nil, errors.New("failed to marshal network message")
}
resp, err := j.post(uri, jsonBytes)
if err != nil {
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
err := handleHTTPError(resp)
err = handleHTTPError(resp)
switch err.(type) {
case NonceError:
// Retry once if the nonce was invalidated
retryResp, err := j.post(uri, jsonBytes)
if err != nil {
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
retryResp, errP := j.post(uri, jsonBytes)
if errP != nil {
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
}
defer retryResp.Body.Close()

View file

@ -35,7 +35,7 @@ func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error {
var err error
s.listener, err = net.Listen("tcp", net.JoinHostPort(s.iface, s.port))
if err != nil {
return fmt.Errorf("Could not start HTTP server for challenge -> %v", err)
return fmt.Errorf("could not start HTTP server for challenge -> %v", err)
}
s.done = make(chan bool)
@ -62,20 +62,31 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet {
w.Header().Add("Content-Type", "text/plain")
w.Write([]byte(keyAuth))
_, err := w.Write([]byte(keyAuth))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Infof("[%s] Served key authentication", domain)
} else {
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method)
w.Write([]byte("TEST"))
_, err := w.Write([]byte("TEST"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
})
httpServer := &http.Server{
Handler: mux,
}
httpServer := &http.Server{Handler: mux}
// Once httpServer is shut down we don't want any lingering
// connections, so disable KeepAlives.
httpServer.SetKeepAlivesEnabled(false)
httpServer.Serve(s.listener)
err := httpServer.Serve(s.listener)
if err != nil {
log.Println(err)
}
s.done <- true
}

View file

@ -3,6 +3,7 @@ package acme
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
)
@ -65,7 +66,10 @@ func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error {
// Shut the server down when we're finished.
go func() {
http.Serve(t.listener, nil)
err := http.Serve(t.listener, nil)
if err != nil {
log.Println(err)
}
}()
return nil

View file

@ -3,16 +3,20 @@ package acme
import (
"fmt"
"time"
"github.com/xenolf/lego/log"
)
// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'.
func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error {
log.Infof("Wait [timeout: %s, interval: %s]", timeout, interval)
var lastErr string
timeup := time.After(timeout)
timeUp := time.After(timeout)
for {
select {
case <-timeup:
return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr)
case <-timeUp:
return fmt.Errorf("time limit exceeded: last error: %s", lastErr)
default:
}