Add AlwaysDeactivateAuthorizations flag to ObtainRequest (#1480)

Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
Martin Weindel 2021-09-08 23:14:21 +02:00 committed by GitHub
parent d2e526e8dd
commit 6d0e0e16b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 31 deletions

View file

@ -60,7 +60,7 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
return responses, nil return responses, nil
} }
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) { func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) {
for _, authzURL := range order.Authorizations { for _, authzURL := range order.Authorizations {
auth, err := c.core.Authorizations.Get(authzURL) auth, err := c.core.Authorizations.Get(authzURL)
if err != nil { if err != nil {
@ -68,7 +68,7 @@ func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) {
continue continue
} }
if auth.Status == acme.StatusValid { if auth.Status == acme.StatusValid && !force {
log.Infof("Skipping deactivating of valid auth: %s", authzURL) log.Infof("Skipping deactivating of valid auth: %s", authzURL)
continue continue
} }

View file

@ -49,22 +49,30 @@ type Resource struct {
// If you do not want that you can supply your own private key in the privateKey parameter. // If you do not want that you can supply your own private key in the privateKey parameter.
// If this parameter is non-nil it will be used instead of generating a new one. // If this parameter is non-nil it will be used instead of generating a new one.
// //
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. // If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
//
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
type ObtainRequest struct { type ObtainRequest struct {
Domains []string Domains []string
Bundle bool Bundle bool
PrivateKey crypto.PrivateKey PrivateKey crypto.PrivateKey
MustStaple bool MustStaple bool
PreferredChain string PreferredChain string
AlwaysDeactivateAuthorizations bool
} }
// ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it. // ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it.
// //
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. // If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
//
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
type ObtainForCSRRequest struct { type ObtainForCSRRequest struct {
CSR *x509.CertificateRequest CSR *x509.CertificateRequest
Bundle bool Bundle bool
PreferredChain string PreferredChain string
AlwaysDeactivateAuthorizations bool
} }
type resolver interface { type resolver interface {
@ -117,14 +125,14 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
authz, err := c.getAuthorizations(order) authz, err := c.getAuthorizations(order)
if err != nil { if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates. // If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order) c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err return nil, err
} }
err = c.resolver.Solve(authz) err = c.resolver.Solve(authz)
if err != nil { if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates. // If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order) c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err return nil, err
} }
@ -138,6 +146,10 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
} }
} }
if request.AlwaysDeactivateAuthorizations {
c.deactivateAuthorizations(order, true)
}
// Do not return an empty failures map, because // Do not return an empty failures map, because
// it would still be a non-nil error value // it would still be a non-nil error value
if len(failures) > 0 { if len(failures) > 0 {
@ -178,14 +190,14 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
authz, err := c.getAuthorizations(order) authz, err := c.getAuthorizations(order)
if err != nil { if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates. // If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order) c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err return nil, err
} }
err = c.resolver.Solve(authz) err = c.resolver.Solve(authz)
if err != nil { if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates. // If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order) c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err return nil, err
} }
@ -199,6 +211,10 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
} }
} }
if request.AlwaysDeactivateAuthorizations {
c.deactivateAuthorizations(order, true)
}
if cert != nil { if cert != nil {
// 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 = certcrypto.PEMEncode(request.CSR) cert.CSR = certcrypto.PEMEncode(request.CSR)

View file

@ -62,6 +62,10 @@ func createRenew() cli.Command {
Name: "preferred-chain", Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.", Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
}, },
cli.StringFlag{
Name: "always-deactivate-authorizations",
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
}, },
} }
} }
@ -127,11 +131,12 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
} }
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: merge(certDomains, domains), Domains: merge(certDomains, domains),
Bundle: bundle, Bundle: bundle,
PrivateKey: privateKey, PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"), MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
} }
certRes, err := client.Certificate.Obtain(request) certRes, err := client.Certificate.Obtain(request)
if err != nil { if err != nil {
@ -174,9 +179,10 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours())) log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr, CSR: csr,
Bundle: bundle, Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
}) })
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View file

@ -47,6 +47,10 @@ func createRun() cli.Command {
Name: "preferred-chain", Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.", Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
}, },
cli.StringFlag{
Name: "always-deactivate-authorizations",
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
}, },
} }
} }
@ -163,10 +167,11 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
if len(domains) > 0 { if len(domains) > 0 {
// obtain a certificate, generating a new private key // obtain a certificate, generating a new private key
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: domains, Domains: domains,
Bundle: bundle, Bundle: bundle,
MustStaple: ctx.Bool("must-staple"), MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
} }
return client.Certificate.Obtain(request) return client.Certificate.Obtain(request)
} }
@ -179,8 +184,9 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
// obtain a certificate for this CSR // obtain a certificate for this CSR
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr, CSR: csr,
Bundle: bundle, Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
}) })
} }