ACME DNS challenges
This commit is contained in:
parent
7a2592b2fa
commit
5bdf8a5ea3
127 changed files with 24386 additions and 739 deletions
37
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
37
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -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")
|
||||
|
|
27
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
27
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
|
@ -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 {
|
||||
|
|
2
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
2
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
16
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -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()
|
||||
|
|
25
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
25
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
|
@ -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
|
||||
}
|
||||
|
|
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go
generated
vendored
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go
generated
vendored
|
@ -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
|
||||
|
|
10
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
10
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
|
@ -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:
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue