From 4ed2da7845550e7d7748834057db8e08037bf2ea Mon Sep 17 00:00:00 2001 From: Martin Weindel Date: Fri, 3 Sep 2021 09:25:41 +0200 Subject: [PATCH 1/4] add AlwaysDeactivateAuthorizations flag to ObtainRequest --- certificate/authorization.go | 20 ++++++++++-------- certificate/certificates.go | 40 +++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/certificate/authorization.go b/certificate/authorization.go index 9161d75499..677a6eee3d 100644 --- a/certificate/authorization.go +++ b/certificate/authorization.go @@ -60,17 +60,19 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz return responses, nil } -func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) { +func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, skipValid bool) { for _, authzURL := range order.Authorizations { - auth, err := c.core.Authorizations.Get(authzURL) - if err != nil { - log.Infof("Unable to get the authorization for: %s", authzURL) - continue - } + if skipValid { + auth, err := c.core.Authorizations.Get(authzURL) + if err != nil { + log.Infof("Unable to get the authorization for: %s", authzURL) + continue + } - if auth.Status == acme.StatusValid { - log.Infof("Skipping deactivating of valid auth: %s", authzURL) - continue + if auth.Status == acme.StatusValid { + log.Infof("Skipping deactivating of valid auth: %s", authzURL) + continue + } } log.Infof("Deactivating auth: %s", authzURL) diff --git a/certificate/certificates.go b/certificate/certificates.go index 596f9512fa..11f90ba8bf 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -50,21 +50,29 @@ type Resource struct { // 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 `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 { - Domains []string - Bundle bool - PrivateKey crypto.PrivateKey - MustStaple bool - PreferredChain string + Domains []string + Bundle bool + PrivateKey crypto.PrivateKey + MustStaple bool + PreferredChain string + AlwaysDeactivateAuthorizations bool } // 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 `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 { - CSR *x509.CertificateRequest - Bundle bool - PreferredChain string + CSR *x509.CertificateRequest + Bundle bool + PreferredChain string + AlwaysDeactivateAuthorizations bool } type resolver interface { @@ -117,14 +125,14 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { authz, err := c.getAuthorizations(order) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) + c.deactivateAuthorizations(order, true) return nil, err } err = c.resolver.Solve(authz) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) + c.deactivateAuthorizations(order, true) return nil, err } @@ -138,6 +146,10 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { } } + if request.AlwaysDeactivateAuthorizations { + c.deactivateAuthorizations(order, false) + } + // Do not return an empty failures map, because // it would still be a non-nil error value if len(failures) > 0 { @@ -178,14 +190,14 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) authz, err := c.getAuthorizations(order) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) + c.deactivateAuthorizations(order, true) return nil, err } err = c.resolver.Solve(authz) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) + c.deactivateAuthorizations(order, true) return nil, err } @@ -199,6 +211,10 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) } } + if request.AlwaysDeactivateAuthorizations { + c.deactivateAuthorizations(order, false) + } + if cert != nil { // Add the CSR to the certificate so that it can be used for renewals. cert.CSR = certcrypto.PEMEncode(request.CSR) From 8a3d03e9072f95d587463ac931ad670eac86f821 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 8 Sep 2021 22:28:52 +0200 Subject: [PATCH 2/4] review --- certificate/authorization.go | 20 +++++++++----------- certificate/certificates.go | 16 ++++++++-------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/certificate/authorization.go b/certificate/authorization.go index 677a6eee3d..452db0d99b 100644 --- a/certificate/authorization.go +++ b/certificate/authorization.go @@ -60,19 +60,17 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz return responses, nil } -func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, skipValid bool) { +func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) { for _, authzURL := range order.Authorizations { - if skipValid { - auth, err := c.core.Authorizations.Get(authzURL) - if err != nil { - log.Infof("Unable to get the authorization for: %s", authzURL) - continue - } + auth, err := c.core.Authorizations.Get(authzURL) + if err != nil { + log.Infof("Unable to get the authorization for: %s", authzURL) + continue + } - if auth.Status == acme.StatusValid { - log.Infof("Skipping deactivating of valid auth: %s", authzURL) - continue - } + if auth.Status == acme.StatusValid && !force { + log.Infof("Skipping deactivating of valid auth: %s", authzURL) + continue } log.Infof("Deactivating auth: %s", authzURL) diff --git a/certificate/certificates.go b/certificate/certificates.go index 11f90ba8bf..b2814ffad1 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -49,10 +49,10 @@ type Resource struct { // 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 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. +// 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 { Domains []string Bundle bool @@ -64,10 +64,10 @@ type ObtainRequest struct { // 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. +// 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 { CSR *x509.CertificateRequest Bundle bool @@ -147,7 +147,7 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { } if request.AlwaysDeactivateAuthorizations { - c.deactivateAuthorizations(order, false) + c.deactivateAuthorizations(order, true) } // Do not return an empty failures map, because @@ -212,7 +212,7 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) } if request.AlwaysDeactivateAuthorizations { - c.deactivateAuthorizations(order, false) + c.deactivateAuthorizations(order, true) } if cert != nil { From 82c2d6063214c4f22c637f4f6ffa8991acd9cc2c Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 8 Sep 2021 22:43:18 +0200 Subject: [PATCH 3/4] review --- certificate/certificates.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/certificate/certificates.go b/certificate/certificates.go index b2814ffad1..93952b4c02 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -125,14 +125,14 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { authz, err := c.getAuthorizations(order) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order, true) + c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations) return nil, err } err = c.resolver.Solve(authz) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order, true) + c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations) return nil, err } @@ -190,14 +190,14 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) authz, err := c.getAuthorizations(order) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order, true) + c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations) return nil, err } err = c.resolver.Solve(authz) if err != nil { // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order, true) + c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations) return nil, err } From af505613b7f078c26b28080cc78f27047939de45 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 8 Sep 2021 22:54:43 +0200 Subject: [PATCH 4/4] feat: add CLI option --- cmd/cmd_renew.go | 22 ++++++++++++++-------- cmd/cmd_run.go | 20 +++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/cmd/cmd_renew.go b/cmd/cmd_renew.go index 06d65be8ae..ac7897ff49 100644 --- a/cmd/cmd_renew.go +++ b/cmd/cmd_renew.go @@ -62,6 +62,10 @@ func createRenew() cli.Command { 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.", }, + 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{ - Domains: merge(certDomains, domains), - Bundle: bundle, - PrivateKey: privateKey, - MustStaple: ctx.Bool("must-staple"), - PreferredChain: ctx.String("preferred-chain"), + Domains: merge(certDomains, domains), + Bundle: bundle, + PrivateKey: privateKey, + MustStaple: ctx.Bool("must-staple"), + PreferredChain: ctx.String("preferred-chain"), + AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), } certRes, err := client.Certificate.Obtain(request) 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())) certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ - CSR: csr, - Bundle: bundle, - PreferredChain: ctx.String("preferred-chain"), + CSR: csr, + Bundle: bundle, + PreferredChain: ctx.String("preferred-chain"), + AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), }) if err != nil { log.Fatal(err) diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index 342f909f35..db368cdf5a 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -47,6 +47,10 @@ func createRun() cli.Command { 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.", }, + 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 { // obtain a certificate, generating a new private key request := certificate.ObtainRequest{ - Domains: domains, - Bundle: bundle, - MustStaple: ctx.Bool("must-staple"), - PreferredChain: ctx.String("preferred-chain"), + Domains: domains, + Bundle: bundle, + MustStaple: ctx.Bool("must-staple"), + PreferredChain: ctx.String("preferred-chain"), + AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), } 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 return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ - CSR: csr, - Bundle: bundle, - PreferredChain: ctx.String("preferred-chain"), + CSR: csr, + Bundle: bundle, + PreferredChain: ctx.String("preferred-chain"), + AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), }) }