fix: acme errors management.
This commit is contained in:
parent
1e8df9f245
commit
7a1feb3c51
5 changed files with 82 additions and 51 deletions
2
Gopkg.lock
generated
2
Gopkg.lock
generated
|
@ -1278,7 +1278,7 @@
|
||||||
"providers/dns/route53",
|
"providers/dns/route53",
|
||||||
"providers/dns/vultr"
|
"providers/dns/vultr"
|
||||||
]
|
]
|
||||||
revision = "2817d2131186742bc98830c73a5d9c255b3f4537"
|
revision = "3d653ee2ee38f1d71beb5f09b37b23344eff0ab3"
|
||||||
source = "github.com/containous/lego"
|
source = "github.com/containous/lego"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
|
10
acme/acme.go
10
acme/acme.go
|
@ -611,11 +611,13 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||||
log.Debugf("Loading ACME certificates %s...", domains)
|
log.Debugf("Loading ACME certificates %s...", domains)
|
||||||
bundle := true
|
bundle := true
|
||||||
certificate, failures := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
|
||||||
if len(failures) > 0 {
|
certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||||
log.Error(failures)
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot obtain certificates %+v", failures)
|
log.Error(err)
|
||||||
|
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Loaded ACME certificates %s", domains)
|
log.Debugf("Loaded ACME certificates %s", domains)
|
||||||
return &Certificate{
|
return &Certificate{
|
||||||
Domain: certificate.Domain,
|
Domain: certificate.Domain,
|
||||||
|
|
|
@ -226,9 +226,9 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati
|
||||||
|
|
||||||
bundle := true
|
bundle := true
|
||||||
|
|
||||||
certificate, failures := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple)
|
certificate, err := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple)
|
||||||
if len(failures) > 0 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot obtain certificates %+v", failures)
|
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 {
|
if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 {
|
||||||
|
|
94
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
94
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
|
@ -189,7 +189,7 @@ func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
|
||||||
logf("[INFO] acme: Trying to resolve account by key")
|
logf("[INFO] acme: Trying to resolve account by key")
|
||||||
|
|
||||||
acc := accountMessage{OnlyReturnExisting: true}
|
acc := accountMessage{OnlyReturnExisting: true}
|
||||||
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, &acc)
|
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
||||||
// your issued certificate as a bundle.
|
// your issued certificate as a bundle.
|
||||||
// This function will never return a partial certificate. If one domain in the list fails,
|
// This function will never return a partial certificate. If one domain in the list fails,
|
||||||
// the whole certificate will fail.
|
// the whole certificate will fail.
|
||||||
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) {
|
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, error) {
|
||||||
// figure out what domains it concerns
|
// figure out what domains it concerns
|
||||||
// start with the common name
|
// start with the common name
|
||||||
domains := []string{csr.Subject.CommonName}
|
domains := []string{csr.Subject.CommonName}
|
||||||
|
@ -292,30 +292,26 @@ DNSNames:
|
||||||
|
|
||||||
order, err := c.createOrderForIdentifiers(domains)
|
order, err := c.createOrderForIdentifiers(domains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
identErrors := make(map[string]error)
|
return CertificateResource{}, err
|
||||||
for _, auth := range order.Identifiers {
|
|
||||||
identErrors[auth.Value] = err
|
|
||||||
}
|
}
|
||||||
return CertificateResource{}, identErrors
|
authz, err := c.getAuthzForOrder(order)
|
||||||
}
|
if err != nil {
|
||||||
authz, failures := c.getAuthzForOrder(order)
|
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
|
||||||
if len(failures) > 0 {
|
|
||||||
/*for _, auth := range authz {
|
/*for _, auth := range authz {
|
||||||
c.disableAuthz(auth)
|
c.disableAuthz(auth)
|
||||||
}*/
|
}*/
|
||||||
|
return CertificateResource{}, err
|
||||||
return CertificateResource{}, failures
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := c.solveChallengeForAuthz(authz)
|
err = c.solveChallengeForAuthz(authz)
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
if err != nil {
|
||||||
if len(errs) > 0 {
|
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||||
return CertificateResource{}, errs
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
|
failures := make(ObtainError)
|
||||||
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, chln := range authz {
|
for _, chln := range authz {
|
||||||
|
@ -326,7 +322,12 @@ DNSNames:
|
||||||
// Add the CSR to the certificate so that it can be used for renewals.
|
// Add the CSR to the certificate so that it can be used for renewals.
|
||||||
cert.CSR = pemEncode(&csr)
|
cert.CSR = pemEncode(&csr)
|
||||||
|
|
||||||
|
// do not return an empty failures map, because
|
||||||
|
// it would still be a non-nil error value
|
||||||
|
if len(failures) > 0 {
|
||||||
return cert, failures
|
return cert, failures
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
||||||
|
@ -338,7 +339,11 @@ DNSNames:
|
||||||
// your issued certificate as a bundle.
|
// your issued certificate as a bundle.
|
||||||
// This function will never return a partial certificate. If one domain in the list fails,
|
// This function will never return a partial certificate. If one domain in the list fails,
|
||||||
// the whole certificate will fail.
|
// the whole certificate will fail.
|
||||||
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) {
|
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
|
||||||
|
if len(domains) == 0 {
|
||||||
|
return CertificateResource{}, errors.New("No domains to obtain a certificate for")
|
||||||
|
}
|
||||||
|
|
||||||
if bundle {
|
if bundle {
|
||||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||||
} else {
|
} else {
|
||||||
|
@ -347,30 +352,26 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||||
|
|
||||||
order, err := c.createOrderForIdentifiers(domains)
|
order, err := c.createOrderForIdentifiers(domains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
identErrors := make(map[string]error)
|
return CertificateResource{}, err
|
||||||
for _, auth := range order.Identifiers {
|
|
||||||
identErrors[auth.Value] = err
|
|
||||||
}
|
}
|
||||||
return CertificateResource{}, identErrors
|
authz, err := c.getAuthzForOrder(order)
|
||||||
}
|
if err != nil {
|
||||||
authz, failures := c.getAuthzForOrder(order)
|
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
|
||||||
if len(failures) > 0 {
|
|
||||||
/*for _, auth := range authz {
|
/*for _, auth := range authz {
|
||||||
c.disableAuthz(auth)
|
c.disableAuthz(auth)
|
||||||
}*/
|
}*/
|
||||||
|
return CertificateResource{}, err
|
||||||
return CertificateResource{}, failures
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := c.solveChallengeForAuthz(authz)
|
err = c.solveChallengeForAuthz(authz)
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
if err != nil {
|
||||||
if len(errs) > 0 {
|
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||||
return CertificateResource{}, errs
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
|
failures := make(ObtainError)
|
||||||
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, auth := range authz {
|
for _, auth := range authz {
|
||||||
|
@ -378,7 +379,12 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do not return an empty failures map, because
|
||||||
|
// it would still be a non-nil error value
|
||||||
|
if len(failures) > 0 {
|
||||||
return cert, failures
|
return cert, failures
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||||
|
@ -433,7 +439,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||||
return CertificateResource{}, err
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
|
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
|
||||||
return newCert, failures[cert.Domain]
|
return newCert, failures
|
||||||
}
|
}
|
||||||
|
|
||||||
var privKey crypto.PrivateKey
|
var privKey crypto.PrivateKey
|
||||||
|
@ -445,7 +451,6 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||||
}
|
}
|
||||||
|
|
||||||
var domains []string
|
var domains []string
|
||||||
var failures map[string]error
|
|
||||||
// check for SAN certificate
|
// check for SAN certificate
|
||||||
if len(x509Cert.DNSNames) > 1 {
|
if len(x509Cert.DNSNames) > 1 {
|
||||||
domains = append(domains, x509Cert.Subject.CommonName)
|
domains = append(domains, x509Cert.Subject.CommonName)
|
||||||
|
@ -459,8 +464,8 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||||
domains = append(domains, x509Cert.Subject.CommonName)
|
domains = append(domains, x509Cert.Subject.CommonName)
|
||||||
}
|
}
|
||||||
|
|
||||||
newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
|
newCert, err := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
|
||||||
return newCert, failures[cert.Domain]
|
return newCert, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) {
|
func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) {
|
||||||
|
@ -490,9 +495,10 @@ func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, err
|
||||||
|
|
||||||
// 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) map[string]error {
|
func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
||||||
|
failures := make(ObtainError)
|
||||||
|
|
||||||
// loop through the resources, basically through the domains.
|
// loop through the resources, basically through the domains.
|
||||||
failures := make(map[string]error)
|
|
||||||
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)
|
||||||
|
@ -513,7 +519,12 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) map[stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// be careful not to return an empty failures map, for
|
||||||
|
// even an empty ObtainError is a non-nil error value
|
||||||
|
if len(failures) > 0 {
|
||||||
return failures
|
return failures
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks all challenges from the server in order and returns the first matching solver.
|
// Checks all challenges from the server in order and returns the first matching solver.
|
||||||
|
@ -528,7 +539,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the challenges needed to proof our identifier to the ACME server.
|
// Get the challenges needed to proof our identifier to the ACME server.
|
||||||
func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[string]error) {
|
func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) {
|
||||||
resc, errc := make(chan authorization), make(chan domainError)
|
resc, errc := make(chan authorization), make(chan domainError)
|
||||||
|
|
||||||
delay := time.Second / overallRequestLimit
|
delay := time.Second / overallRequestLimit
|
||||||
|
@ -549,7 +560,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[str
|
||||||
}
|
}
|
||||||
|
|
||||||
var responses []authorization
|
var responses []authorization
|
||||||
failures := make(map[string]error)
|
failures := make(ObtainError)
|
||||||
for i := 0; i < len(order.Authorizations); i++ {
|
for i := 0; i < len(order.Authorizations); i++ {
|
||||||
select {
|
select {
|
||||||
case res := <-resc:
|
case res := <-resc:
|
||||||
|
@ -564,7 +575,12 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[str
|
||||||
close(resc)
|
close(resc)
|
||||||
close(errc)
|
close(errc)
|
||||||
|
|
||||||
|
// be careful to not return an empty failures map;
|
||||||
|
// even if empty, they become non-nil error values
|
||||||
|
if len(failures) > 0 {
|
||||||
return responses, failures
|
return responses, failures
|
||||||
|
}
|
||||||
|
return responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logAuthz(order orderResource) {
|
func logAuthz(order orderResource) {
|
||||||
|
|
13
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
13
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
|
@ -1,6 +1,7 @@
|
||||||
package acmev2
|
package acmev2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -13,6 +14,18 @@ const (
|
||||||
invalidNonceError = "urn:ietf:params:acme:error:badNonce"
|
invalidNonceError = "urn:ietf:params:acme:error:badNonce"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ObtainError is returned when there are specific errors available
|
||||||
|
// per domain. For example in ObtainCertificate
|
||||||
|
type ObtainError map[string]error
|
||||||
|
|
||||||
|
func (e ObtainError) Error() string {
|
||||||
|
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
|
||||||
|
for dom, err := range e {
|
||||||
|
buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err))
|
||||||
|
}
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
// RemoteError is the base type for all errors specific to the ACME protocol.
|
// RemoteError is the base type for all errors specific to the ACME protocol.
|
||||||
type RemoteError struct {
|
type RemoteError struct {
|
||||||
StatusCode int `json:"status,omitempty"`
|
StatusCode int `json:"status,omitempty"`
|
||||||
|
|
Loading…
Reference in a new issue