summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go')
-rw-r--r--vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go425
1 files changed, 306 insertions, 119 deletions
diff --git a/vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
index 16e4cbe00..bcb844371 100644
--- a/vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
+++ b/vendor/github.com/rsc/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
@@ -11,6 +11,7 @@ import (
"io/ioutil"
"log"
"net"
+ "net/http"
"regexp"
"strconv"
"strings"
@@ -22,6 +23,16 @@ var (
Logger *log.Logger
)
+const (
+ // maxBodySize is the maximum size of body that we will read.
+ maxBodySize = 1024 * 1024
+
+ // overallRequestLimit is the overall number of request per second limited on the
+ // “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
+)
+
// logf writes a log entry. It uses Logger if not
// nil, otherwise it uses the default log.Logger.
func logf(format string, args ...interface{}) {
@@ -49,17 +60,17 @@ type validateFunc func(j *jws, domain, uri string, chlng challenge) error
// Client is the user-friendy way to ACME
type Client struct {
- directory directory
- user User
- jws *jws
- keyType KeyType
- issuerCert []byte
- solvers map[Challenge]solver
+ directory directory
+ user User
+ jws *jws
+ keyType KeyType
+ solvers map[Challenge]solver
}
// NewClient creates a new ACME client on behalf of the user. The client will depend on
-// the ACME directory located at caDirURL for the rest of its actions. It will
-// generate private keys for certificates of size keyBits.
+// the ACME directory located at caDirURL for the rest of its actions. A private
+// key of type keyType (see KeyType contants) will be generated when requesting a new
+// certificate if one isn't provided.
func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
privKey := user.GetPrivateKey()
if privKey == nil {
@@ -96,13 +107,15 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
}
-// SetChallengeProvider specifies a custom provider that will make the solution available
+// SetChallengeProvider specifies a custom provider p that can solve the given challenge type.
func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error {
switch challenge {
case HTTP01:
c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
case TLSSNI01:
c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p}
+ case DNS01:
+ c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p}
default:
return fmt.Errorf("Unknown challenge %v", challenge)
}
@@ -112,6 +125,9 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider)
// SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges.
// If this option is not used, the default port 80 and all interfaces will be used.
// To only specify a port and no interface use the ":port" notation.
+//
+// NOTE: This REPLACES any custom HTTP provider previously set by calling
+// c.SetChallengeProvider with the default HTTP challenge provider.
func (c *Client) SetHTTPAddress(iface string) error {
host, port, err := net.SplitHostPort(iface)
if err != nil {
@@ -128,6 +144,9 @@ func (c *Client) SetHTTPAddress(iface string) error {
// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges.
// If this option is not used, the default port 443 and all interfaces will be used.
// To only specify a port and no interface use the ":port" notation.
+//
+// NOTE: This REPLACES any custom TLS-SNI provider previously set by calling
+// c.SetChallengeProvider with the default TLS-SNI challenge provider.
func (c *Client) SetTLSAddress(iface string) error {
host, port, err := net.SplitHostPort(iface)
if err != nil {
@@ -165,15 +184,31 @@ func (c *Client) Register() (*RegistrationResource, error) {
}
var serverReg Registration
+ var regURI string
hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg)
if err != nil {
- return nil, err
+ remoteErr, ok := err.(RemoteError)
+ if ok && remoteErr.StatusCode == 409 {
+ regURI = hdr.Get("Location")
+ regMsg = registrationMessage{
+ Resource: "reg",
+ }
+ if hdr, err = postJSON(c.jws, regURI, regMsg, &serverReg); err != nil {
+ return nil, err
+ }
+ } else {
+ return nil, err
+ }
}
reg := &RegistrationResource{Body: serverReg}
links := parseLinks(hdr["Link"])
- reg.URI = hdr.Get("Location")
+
+ if regURI == "" {
+ regURI = hdr.Get("Location")
+ }
+ reg.URI = regURI
if links["terms-of-service"] != "" {
reg.TosURL = links["terms-of-service"]
}
@@ -187,6 +222,68 @@ func (c *Client) Register() (*RegistrationResource, error) {
return reg, nil
}
+// DeleteRegistration deletes the client's user registration from the ACME
+// server.
+func (c *Client) DeleteRegistration() error {
+ if c == nil || c.user == nil {
+ return errors.New("acme: cannot unregister a nil client or user")
+ }
+ logf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
+
+ regMsg := registrationMessage{
+ Resource: "reg",
+ Delete: true,
+ }
+
+ _, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, nil)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// QueryRegistration runs a POST request on the client's registration and
+// returns the result.
+//
+// This is similar to the Register function, but acting on an existing
+// registration link and resource.
+func (c *Client) QueryRegistration() (*RegistrationResource, error) {
+ if c == nil || c.user == nil {
+ return nil, errors.New("acme: cannot query the registration of a nil client or user")
+ }
+ // Log the URL here instead of the email as the email may not be set
+ logf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
+
+ regMsg := registrationMessage{
+ Resource: "reg",
+ }
+
+ var serverReg Registration
+ hdr, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, &serverReg)
+ if err != nil {
+ return nil, err
+ }
+
+ reg := &RegistrationResource{Body: serverReg}
+
+ links := parseLinks(hdr["Link"])
+ // Location: header is not returned so this needs to be populated off of
+ // existing URI
+ reg.URI = c.user.GetRegistration().URI
+ if links["terms-of-service"] != "" {
+ reg.TosURL = links["terms-of-service"]
+ }
+
+ if links["next"] != "" {
+ reg.NewAuthzURL = links["next"]
+ } else {
+ return nil, errors.New("acme: No new-authz link in response to registration query")
+ }
+
+ return reg, nil
+}
+
// AgreeToTOS updates the Client registration and sends the agreement to
// the server.
func (c *Client) AgreeToTOS() error {
@@ -198,6 +295,69 @@ func (c *Client) AgreeToTOS() error {
return err
}
+// ObtainCertificateForCSR tries to obtain a certificate matching the CSR passed into it.
+// The domains are inferred from the CommonName and SubjectAltNames, if any. The private key
+// for this CSR is not required.
+// If bundle is true, the []byte contains both the issuer certificate and
+// your issued certificate as a bundle.
+// This function will never return a partial certificate. If one domain in the list fails,
+// the whole certificate will fail.
+func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) {
+ // figure out what domains it concerns
+ // start with the common name
+ domains := []string{csr.Subject.CommonName}
+
+ // loop over the SubjectAltName DNS names
+DNSNames:
+ for _, sanName := range csr.DNSNames {
+ for _, existingName := range domains {
+ if existingName == sanName {
+ // duplicate; skip this name
+ continue DNSNames
+ }
+ }
+
+ // name is unique
+ domains = append(domains, sanName)
+ }
+
+ if bundle {
+ logf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
+ } else {
+ logf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
+ }
+
+ challenges, failures := c.getChallenges(domains)
+ // If any challenge fails - return. Do not generate partial SAN certificates.
+ if len(failures) > 0 {
+ for _, auth := range challenges {
+ c.disableAuthz(auth)
+ }
+
+ return CertificateResource{}, failures
+ }
+
+ errs := c.solveChallenges(challenges)
+ // If any challenge fails - return. Do not generate partial SAN certificates.
+ if len(errs) > 0 {
+ return CertificateResource{}, errs
+ }
+
+ logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
+
+ cert, err := c.requestCertificateForCsr(challenges, bundle, csr.Raw, nil)
+ if err != nil {
+ for _, chln := range challenges {
+ failures[chln.Domain] = err
+ }
+ }
+
+ // Add the CSR to the certificate so that it can be used for renewals.
+ cert.CSR = pemEncode(&csr)
+
+ return cert, failures
+}
+
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
// The first domain in domains is used for the CommonName field of the certificate, all other
// domains are added using the Subject Alternate Names extension. A new private key is generated
@@ -207,7 +367,7 @@ func (c *Client) AgreeToTOS() error {
// your issued certificate as a bundle.
// This function will never return a partial certificate. If one domain in the list fails,
// the whole certificate will fail.
-func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey) (CertificateResource, map[string]error) {
+func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) {
if bundle {
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
} else {
@@ -217,6 +377,10 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
challenges, failures := c.getChallenges(domains)
// If any challenge fails - return. Do not generate partial SAN certificates.
if len(failures) > 0 {
+ for _, auth := range challenges {
+ c.disableAuthz(auth)
+ }
+
return CertificateResource{}, failures
}
@@ -228,7 +392,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
- cert, err := c.requestCertificate(challenges, bundle, privKey)
+ cert, err := c.requestCertificate(challenges, bundle, privKey, mustStaple)
if err != nil {
for _, chln := range challenges {
failures[chln.Domain] = err
@@ -264,7 +428,7 @@ func (c *Client) RevokeCertificate(certificate []byte) error {
// If bundle is true, the []byte contains both the issuer certificate and
// your issued certificate as a bundle.
// For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil.
-func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) {
+func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple bool) (CertificateResource, error) {
// Input certificate is PEM encoded. Decode it here as we may need the decoded
// cert later on in the renewal process. The input may be a bundle or a single certificate.
certificates, err := parsePEMBundle(cert.Certificate)
@@ -281,47 +445,16 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
- // The first step of renewal is to check if we get a renewed cert
- // directly from the cert URL.
- resp, err := httpGet(cert.CertURL)
- if err != nil {
- return CertificateResource{}, err
- }
- defer resp.Body.Close()
- serverCertBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return CertificateResource{}, err
- }
-
- serverCert, err := x509.ParseCertificate(serverCertBytes)
- if err != nil {
- return CertificateResource{}, err
- }
-
- // If the server responds with a different certificate we are effectively renewed.
- // TODO: Further test if we can actually use the new certificate (Our private key works)
- if !x509Cert.Equal(serverCert) {
- logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
- issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
- // If bundle is true, we want to return a certificate bundle.
- // To do this, we need the issuer certificate.
- if bundle {
- // The issuer certificate link is always supplied via an "up" link
- // in the response headers of a new certificate.
- links := parseLinks(resp.Header["Link"])
- issuerCert, err := c.getIssuerCertificate(links["up"])
- if err != nil {
- // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
- logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
- } else {
- // Success - append the issuer cert to the issued cert.
- issuerCert = pemEncode(derCertificateBytes(issuerCert))
- issuedCert = append(issuedCert, issuerCert...)
- }
+ // We always need to request a new certificate to renew.
+ // 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 CertificateResource{}, err
}
-
- cert.Certificate = issuedCert
- return cert, nil
+ newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
+ return newCert, failures[cert.Domain]
}
var privKey crypto.PrivateKey
@@ -347,7 +480,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif
domains = append(domains, x509Cert.Subject.CommonName)
}
- newCert, failures := c.ObtainCertificate(domains, bundle, privKey)
+ newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
return newCert, failures[cert.Domain]
}
@@ -357,16 +490,23 @@ func (c *Client) solveChallenges(challenges []authorizationResource) map[string]
// loop through the resources, basically through the domains.
failures := make(map[string]error)
for _, authz := range challenges {
+ if authz.Body.Status == "valid" {
+ // Boulder might recycle recent validated authz (see issue #267)
+ logf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Domain)
+ continue
+ }
// no solvers - no solving
if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil {
for i, solver := range solvers {
// TODO: do not immediately fail if one domain fails to validate.
err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
if err != nil {
+ c.disableAuthz(authz)
failures[authz.Domain] = err
}
}
} else {
+ c.disableAuthz(authz)
failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
}
}
@@ -399,7 +539,11 @@ func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver
func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) {
resc, errc := make(chan authorizationResource), make(chan domainError)
+ delay := time.Second / overallRequestLimit
+
for _, domain := range domains {
+ time.Sleep(delay)
+
go func(domain string) {
authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}}
var authz authorization
@@ -412,6 +556,7 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s
links := parseLinks(hdr["Link"])
if links["next"] == "" {
logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain)
+ errc <- domainError{Domain: domain, Error: errors.New("Server did not provide next link to proceed")}
return
}
@@ -437,18 +582,32 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s
}
}
+ logAuthz(challenges)
+
close(resc)
close(errc)
return challenges, failures
}
-func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey) (CertificateResource, error) {
+func logAuthz(authz []authorizationResource) {
+ for _, auth := range authz {
+ logf("[INFO][%s] AuthURL: %s", auth.Domain, auth.AuthURL)
+ }
+}
+
+// cleanAuthz loops through the passed in slice and disables any auths which are not "valid"
+func (c *Client) disableAuthz(auth authorizationResource) error {
+ var disabledAuth authorization
+ _, err := postJSON(c.jws, auth.AuthURL, deactivateAuthMessage{Resource: "authz", Status: "deactivated"}, &disabledAuth)
+ return err
+}
+
+func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
if len(authz) == 0 {
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
}
- commonName := authz[0]
var err error
if privKey == nil {
privKey, err = generatePrivateKey(c.keyType)
@@ -457,19 +616,30 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
}
}
+ // determine certificate name(s) based on the authorization resources
+ commonName := authz[0]
var san []string
- var authURLs []string
for _, auth := range authz[1:] {
san = append(san, auth.Domain)
- authURLs = append(authURLs, auth.AuthURL)
}
// TODO: should the CSR be customizable?
- csr, err := generateCsr(privKey, commonName.Domain, san)
+ csr, err := generateCsr(privKey, commonName.Domain, san, mustStaple)
if err != nil {
return CertificateResource{}, err
}
+ return c.requestCertificateForCsr(authz, bundle, csr, pemEncode(privKey))
+}
+
+func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle bool, csr []byte, privateKeyPem []byte) (CertificateResource, error) {
+ commonName := authz[0]
+
+ var authURLs []string
+ for _, auth := range authz[1:] {
+ authURLs = append(authURLs, auth.AuthURL)
+ }
+
csrString := base64.URLEncoding.EncodeToString(csr)
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
if err != nil {
@@ -481,90 +651,108 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
return CertificateResource{}, err
}
- privateKeyPem := pemEncode(privKey)
- cerRes := CertificateResource{
+ certRes := CertificateResource{
Domain: commonName.Domain,
CertURL: resp.Header.Get("Location"),
- PrivateKey: privateKeyPem}
+ PrivateKey: privateKeyPem,
+ }
- for {
- switch resp.StatusCode {
- case 201, 202:
- cert, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
- resp.Body.Close()
- if err != nil {
- return CertificateResource{}, err
- }
+ maxChecks := 1000
+ for i := 0; i < maxChecks; i++ {
+ done, err := c.checkCertResponse(resp, &certRes, bundle)
+ resp.Body.Close()
+ if err != nil {
+ return CertificateResource{}, err
+ }
+ if done {
+ break
+ }
+ if i == maxChecks-1 {
+ return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i)
+ }
+ resp, err = httpGet(certRes.CertURL)
+ if err != nil {
+ return CertificateResource{}, err
+ }
+ }
- // The server returns a body with a length of zero if the
- // certificate was not ready at the time this request completed.
- // Otherwise the body is the certificate.
- if len(cert) > 0 {
+ return certRes, nil
+}
- cerRes.CertStableURL = resp.Header.Get("Content-Location")
- cerRes.AccountRef = c.user.GetRegistration().URI
+// checkCertResponse checks resp to see if a certificate is contained in the
+// response, and if so, loads it into certRes and returns true. If the cert
+// is not yet ready, it returns false. This function honors the waiting period
+// required by the Retry-After header of the response, if specified. This
+// function may read from resp.Body but does NOT close it. The certRes input
+// 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(resp *http.Response, certRes *CertificateResource, bundle bool) (bool, error) {
+ switch resp.StatusCode {
+ case 201, 202:
+ cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
+ if err != nil {
+ return false, err
+ }
- issuedCert := pemEncode(derCertificateBytes(cert))
- // If bundle is true, we want to return a certificate bundle.
- // To do this, we need the issuer certificate.
- if bundle {
- // The issuer certificate link is always supplied via an "up" link
- // in the response headers of a new certificate.
- links := parseLinks(resp.Header["Link"])
- issuerCert, err := c.getIssuerCertificate(links["up"])
- if err != nil {
- // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
- logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
- } else {
- // Success - append the issuer cert to the issued cert.
- issuerCert = pemEncode(derCertificateBytes(issuerCert))
- issuedCert = append(issuedCert, issuerCert...)
- }
- }
+ // The server returns a body with a length of zero if the
+ // certificate was not ready at the time this request completed.
+ // Otherwise the body is the certificate.
+ if len(cert) > 0 {
+ certRes.CertStableURL = resp.Header.Get("Content-Location")
+ certRes.AccountRef = c.user.GetRegistration().URI
- cerRes.Certificate = issuedCert
- logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
- return cerRes, nil
- }
+ issuedCert := pemEncode(derCertificateBytes(cert))
- // The certificate was granted but is not yet issued.
- // Check retry-after and loop.
- ra := resp.Header.Get("Retry-After")
- retryAfter, err := strconv.Atoi(ra)
+ // The issuer certificate link is always supplied via an "up" link
+ // in the response headers of a new certificate.
+ links := parseLinks(resp.Header["Link"])
+ issuerCert, err := c.getIssuerCertificate(links["up"])
if err != nil {
- return CertificateResource{}, err
- }
+ // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
+ logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
+ } else {
+ issuerCert = pemEncode(derCertificateBytes(issuerCert))
- logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", commonName.Domain, retryAfter)
- time.Sleep(time.Duration(retryAfter) * time.Second)
+ // If bundle is true, we want to return a certificate bundle.
+ // To do this, we append the issuer cert to the issued cert.
+ if bundle {
+ issuedCert = append(issuedCert, issuerCert...)
+ }
+ }
- break
- default:
- return CertificateResource{}, handleHTTPError(resp)
+ certRes.Certificate = issuedCert
+ certRes.IssuerCertificate = issuerCert
+ logf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
+ return true, nil
}
- resp, err = httpGet(cerRes.CertURL)
+ // The certificate was granted but is not yet issued.
+ // Check retry-after and loop.
+ ra := resp.Header.Get("Retry-After")
+ retryAfter, err := strconv.Atoi(ra)
if err != nil {
- return CertificateResource{}, err
+ return false, err
}
+
+ logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", certRes.Domain, retryAfter)
+ time.Sleep(time.Duration(retryAfter) * time.Second)
+
+ return false, nil
+ default:
+ return false, handleHTTPError(resp)
}
}
-// getIssuerCertificate requests the issuer certificate and caches it for
-// subsequent requests.
+// getIssuerCertificate requests the issuer certificate
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
logf("[INFO] acme: Requesting issuer cert from %s", url)
- if c.issuerCert != nil {
- return c.issuerCert, nil
- }
-
resp, err := httpGet(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
- issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
+ issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
if err != nil {
return nil, err
}
@@ -574,7 +762,6 @@ func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
return nil, err
}
- c.issuerCert = issuerBytes
return issuerBytes, err
}