feat: implement 'replaces' field in newOrder and draft-ietf-acme-ari-03 CertID changes (#2114)
This commit is contained in:
parent
adea063bb1
commit
6dd8d035d1
8 changed files with 79 additions and 182 deletions
|
@ -226,6 +226,6 @@ issues:
|
|||
- path: providers/dns/hosttech/internal/client_test.go
|
||||
text: 'Duplicate words \(0\) found'
|
||||
- path: cmd/cmd_renew.go
|
||||
text: 'cyclomatic complexity 15 of func `renewForDomains` is high'
|
||||
text: 'cyclomatic complexity \d+ of func `renewForDomains` is high'
|
||||
- path: providers/dns/cpanel/cpanel.go
|
||||
text: 'cyclomatic complexity 13 of func `\(\*DNSProvider\)\.CleanUp` is high'
|
||||
|
|
|
@ -16,7 +16,7 @@ Let's Encrypt client and ACME library written in Go.
|
|||
- ACME v2 [RFC 8555](https://www.rfc-editor.org/rfc/rfc8555.html)
|
||||
- Support [RFC 8737](https://www.rfc-editor.org/rfc/rfc8737.html): TLS Application‑Layer Protocol Negotiation (ALPN) Challenge Extension
|
||||
- Support [RFC 8738](https://www.rfc-editor.org/rfc/rfc8738.html): certificates for IP addresses
|
||||
- Support [draft-ietf-acme-ari-02](https://datatracker.ietf.org/doc/draft-ietf-acme-ari/): Renewal Information (ARI) Extension
|
||||
- Support [draft-ietf-acme-ari-03](https://datatracker.ietf.org/doc/draft-ietf-acme-ari/): Renewal Information (ARI) Extension
|
||||
- Register with CA
|
||||
- Obtain certificates, both from scratch or with an existing CSR
|
||||
- Renew certificates
|
||||
|
|
|
@ -13,6 +13,10 @@ import (
|
|||
type OrderOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
type OrderService service
|
||||
|
@ -45,6 +49,10 @@ func (o *OrderService) NewWithOptions(domains []string, opts *OrderOptions) (acm
|
|||
if !opts.NotBefore.IsZero() {
|
||||
orderReq.NotBefore = opts.NotBefore.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if o.core.GetDirectory().RenewalInfo != "" {
|
||||
orderReq.Replaces = opts.ReplacesCertID
|
||||
}
|
||||
}
|
||||
|
||||
var order acme.Order
|
||||
|
|
|
@ -181,6 +181,12 @@ type Order struct {
|
|||
// certificate (optional, string):
|
||||
// A URL for the certificate that has been issued in response to this order
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
|
||||
// replaces (optional, string):
|
||||
// replaces (string, optional): A string uniquely identifying a
|
||||
// previously-issued certificate which this order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
Replaces string `json:"replaces,omitempty"`
|
||||
}
|
||||
|
||||
// Authorization the ACME authorization object.
|
||||
|
@ -329,11 +335,11 @@ type RenewalInfoResponse struct {
|
|||
}
|
||||
|
||||
// RenewalInfoUpdateRequest is the JWS payload for POST requests made to the renewalInfo endpoint.
|
||||
// - (4.2. RenewalInfo Objects) https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-02#section-4.2
|
||||
// - (4.2. RenewalInfo Objects) https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-4.2
|
||||
type RenewalInfoUpdateRequest struct {
|
||||
// CertID is a composite string in the format: base64url(AKI) || '.' || base64url(Serial), where AKI is the
|
||||
// certificate's authority key identifier and Serial is the certificate's serial number. For details, see:
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-02#section-4.1
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-4.1
|
||||
CertID string `json:"certID"`
|
||||
// Replaced is required and indicates whether or not the client considers the certificate to have been replaced.
|
||||
// A certificate is considered replaced when its revocation would not disrupt any ongoing services,
|
||||
|
|
|
@ -63,6 +63,10 @@ type ObtainRequest struct {
|
|||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
// ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it.
|
||||
|
@ -79,6 +83,10 @@ type ObtainForCSRRequest struct {
|
|||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
type resolver interface {
|
||||
|
@ -124,8 +132,9 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
|||
}
|
||||
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
ReplacesCertID: request.ReplacesCertID,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
|
@ -189,8 +198,9 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
|||
}
|
||||
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
ReplacesCertID: request.ReplacesCertID,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
|
|
|
@ -2,12 +2,12 @@ package certificate
|
|||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
|
@ -65,7 +65,7 @@ func (r *RenewalInfoResponse) ShouldRenewAt(now time.Time, willingToSleep time.D
|
|||
//
|
||||
// https://datatracker.ietf.org/doc/draft-ietf-acme-ari
|
||||
func (c *Certifier) GetRenewalInfo(req RenewalInfoRequest) (*RenewalInfoResponse, error) {
|
||||
certID, err := makeARICertID(req.Cert)
|
||||
certID, err := MakeARICertID(req.Cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making certID: %w", err)
|
||||
}
|
||||
|
@ -84,39 +84,32 @@ func (c *Certifier) GetRenewalInfo(req RenewalInfoRequest) (*RenewalInfoResponse
|
|||
return &info, nil
|
||||
}
|
||||
|
||||
// UpdateRenewalInfo sends an update to the ACME server's renewal info endpoint to indicate that the client has successfully replaced a certificate.
|
||||
// A certificate is considered replaced when its revocation would not disrupt any ongoing services,
|
||||
// for instance because it has been renewed and the new certificate is in use, or because it is no longer in use.
|
||||
//
|
||||
// Note: this endpoint is part of a draft specification, not all ACME servers will implement it.
|
||||
// This method will return api.ErrNoARI if the server does not advertise a renewal info endpoint.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/draft-ietf-acme-ari
|
||||
func (c *Certifier) UpdateRenewalInfo(req RenewalInfoRequest) error {
|
||||
certID, err := makeARICertID(req.Cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making certID: %w", err)
|
||||
}
|
||||
|
||||
_, err = c.core.Certificates.UpdateRenewalInfo(acme.RenewalInfoUpdateRequest{
|
||||
CertID: certID,
|
||||
Replaced: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeARICertID constructs a certificate identifier as described in draft-ietf-acme-ari-02, section 4.1.
|
||||
func makeARICertID(leaf *x509.Certificate) (string, error) {
|
||||
// MakeARICertID constructs a certificate identifier as described in draft-ietf-acme-ari-03, section 4.1.
|
||||
func MakeARICertID(leaf *x509.Certificate) (string, error) {
|
||||
if leaf == nil {
|
||||
return "", errors.New("leaf certificate is nil")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%s",
|
||||
strings.TrimRight(base64.URLEncoding.EncodeToString(leaf.AuthorityKeyId), "="),
|
||||
strings.TrimRight(base64.URLEncoding.EncodeToString(leaf.SerialNumber.Bytes()), "="),
|
||||
), nil
|
||||
// Marshal the Serial Number into DER.
|
||||
der, err := asn1.Marshal(leaf.SerialNumber)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Check if the DER encoded bytes are sufficient (at least 3 bytes: tag,
|
||||
// length, and value).
|
||||
if len(der) < 3 {
|
||||
return "", errors.New("invalid DER encoding of serial number")
|
||||
}
|
||||
|
||||
// Extract only the integer bytes from the DER encoded Serial Number
|
||||
// Skipping the first 2 bytes (tag and length).
|
||||
serial := base64.RawURLEncoding.EncodeToString(der[2:])
|
||||
|
||||
// Convert the Authority Key Identifier to base64url encoding without
|
||||
// padding.
|
||||
aki := base64.RawURLEncoding.EncodeToString(leaf.AuthorityKeyId)
|
||||
|
||||
// Construct the final identifier by concatenating AKI and Serial Number.
|
||||
return fmt.Sprintf("%s.%s", aki, serial), nil
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package certificate
|
|||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -13,40 +11,28 @@ import (
|
|||
"github.com/go-acme/lego/v4/acme/api"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/go-jose/go-jose/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
ariLeafPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIDMDCCAhigAwIBAgIIPqNFaGVEHxwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||
AxMVbWluaWNhIHJvb3QgY2EgM2ExMzU2MB4XDTIyMDMxNzE3NTEwOVoXDTI0MDQx
|
||||
NjE3NTEwOVowFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQCgm9K/c+il2Pf0f8qhgxn9SKqXq88cOm9ov9AVRbPA
|
||||
OWAAewqX2yUAwI4LZBGEgzGzTATkiXfoJ3cN3k39cH6tBbb3iSPuEn7OZpIk9D+e
|
||||
3Q9/hX+N/jlWkaTB/FNA+7aE5IVWhmdczYilXa10V9r+RcvACJt0gsipBZVJ4jfJ
|
||||
HnWJJGRZzzxqG/xkQmpXxZO7nOPFc8SxYKWdfcgp+rjR2ogYhSz7BfKoVakGPbpX
|
||||
vZOuT9z4kkHra/WjwlkQhtHoTXdAxH3qC2UjMzO57Tx+otj0CxAv9O7CTJXISywB
|
||||
vEVcmTSZkHS3eZtvvIwPx7I30ITRkYk/tLl1MbyB3SiZAgMBAAGjeDB2MA4GA1Ud
|
||||
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T
|
||||
AQH/BAIwADAfBgNVHSMEGDAWgBQ4zzDRUaXHVKqlSTWkULGU4zGZpTAWBgNVHREE
|
||||
DzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAx0aYvmCk7JYGNEXe
|
||||
+hrOfKawkHYzWvA92cI/Oi6h+oSdHZ2UKzwFNf37cVKZ37FCrrv5pFP/xhhHvrNV
|
||||
EnOx4IaF7OrnaTu5miZiUWuvRQP7ZGmGNFYbLTEF6/dj+WqyYdVaWzxRqHFu1ptC
|
||||
TXysJCeyiGnR+KOOjOOQ9ZlO5JUK3OE4hagPLfaIpDDy6RXQt3ss0iNLuB1+IOtp
|
||||
1URpvffLZQ8xPsEgOZyPWOcabTwJrtqBwily+lwPFn2mChUx846LwQfxtsXU/lJg
|
||||
HX2RteNJx7YYNeX3Uf960mgo5an6vE8QNAsIoNHYrGyEmXDhTRe9mCHyiW2S7fZq
|
||||
o9q12g==
|
||||
MIIBQzCB66ADAgECAgUAh2VDITAKBggqhkjOPQQDAjAVMRMwEQYDVQQDEwpFeGFt
|
||||
cGxlIENBMCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMBYxFDAS
|
||||
BgNVBAMTC2V4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeBZu
|
||||
7cbpAYNXZLbbh8rNIzuOoqOOtmxA1v7cRm//AwyMwWxyHz4zfwmBhcSrf47NUAFf
|
||||
qzLQ2PPQxdTXREYEnKMjMCEwHwYDVR0jBBgwFoAUaYhba4dGQEHhs3uEe6CuLN4B
|
||||
yNQwCgYIKoZIzj0EAwIDRwAwRAIge09+S5TZAlw5tgtiVvuERV6cT4mfutXIlwTb
|
||||
+FYN/8oCIClDsqBklhB9KAelFiYt9+6FDj3z4KGVelYM5MdsO3pK
|
||||
-----END CERTIFICATE-----`
|
||||
ariLeafCertID = "OM8w0VGlx1SqpUk1pFCxlOMxmaU.PqNFaGVEHxw"
|
||||
ariLeafCertID = "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE"
|
||||
)
|
||||
|
||||
func Test_makeCertID(t *testing.T) {
|
||||
leaf, err := certcrypto.ParsePEMCertificate([]byte(ariLeafPEM))
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := makeARICertID(leaf)
|
||||
actual, err := MakeARICertID(leaf)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ariLeafCertID, actual)
|
||||
}
|
||||
|
@ -145,85 +131,6 @@ func TestCertifier_GetRenewalInfo_errors(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCertifier_UpdateRenewalInfo(t *testing.T) {
|
||||
leaf, err := certcrypto.ParsePEMCertificate([]byte(ariLeafPEM))
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err, "Could not generate test key")
|
||||
|
||||
// Test with a fake API.
|
||||
mux, apiURL := tester.SetupFakeAPI(t)
|
||||
mux.HandleFunc("/renewalInfo", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
body, rsbErr := readSignedBody(r, key)
|
||||
if rsbErr != nil {
|
||||
http.Error(w, rsbErr.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req acme.RenewalInfoUpdateRequest
|
||||
err = json.Unmarshal(body, &req)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, req.Replaced)
|
||||
assert.Equal(t, ariLeafCertID, req.CertID)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
|
||||
require.NoError(t, err)
|
||||
|
||||
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
|
||||
|
||||
err = certifier.UpdateRenewalInfo(RenewalInfoRequest{leaf})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCertifier_UpdateRenewalInfo_errors(t *testing.T) {
|
||||
leaf, err := certcrypto.ParsePEMCertificate([]byte(ariLeafPEM))
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err, "Could not generate test key")
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
request RenewalInfoRequest
|
||||
}{
|
||||
{
|
||||
desc: "API error",
|
||||
request: RenewalInfoRequest{leaf},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mux, apiURL := tester.SetupFakeAPI(t)
|
||||
|
||||
// Always returns an error.
|
||||
mux.HandleFunc("/renewalInfo", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
})
|
||||
|
||||
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
|
||||
require.NoError(t, err)
|
||||
|
||||
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
|
||||
|
||||
err = certifier.UpdateRenewalInfo(test.request)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalInfoResponse_ShouldRenew(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
|
||||
|
@ -289,26 +196,3 @@ func TestRenewalInfoResponse_ShouldRenew(t *testing.T) {
|
|||
assert.Nil(t, rt)
|
||||
})
|
||||
}
|
||||
|
||||
func readSignedBody(r *http.Request, privateKey *rsa.PrivateKey) ([]byte, error) {
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigAlgs := []jose.SignatureAlgorithm{jose.RS256}
|
||||
jws, err := jose.ParseSigned(string(reqBody), sigAlgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := jws.Verify(&jose.JSONWebKey{
|
||||
Key: privateKey.Public(),
|
||||
Algorithm: "RSA",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
|
|
@ -187,6 +187,11 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
|
|||
time.Sleep(sleepTime)
|
||||
}
|
||||
|
||||
replacesCertID, err := certificate.MakeARICertID(cert)
|
||||
if err != nil {
|
||||
log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: merge(certDomains, domains),
|
||||
PrivateKey: privateKey,
|
||||
|
@ -196,6 +201,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
|
|||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
ReplacesCertID: replacesCertID,
|
||||
}
|
||||
|
||||
certRes, err := client.Certificate.Obtain(request)
|
||||
|
@ -205,14 +211,6 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
|
|||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
if ariRenewalTime != nil {
|
||||
// Post to the renewalInfo endpoint to indicate that we have renewed and replaced the certificate.
|
||||
err := client.Certificate.UpdateRenewalInfo(certificate.RenewalInfoRequest{Cert: certificates[0]})
|
||||
if err != nil {
|
||||
log.Warnf("[%s] Failed to update renewal info: %v", domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
meta[renewEnvCertDomain] = domain
|
||||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
@ -264,6 +262,11 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
|
|||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
|
||||
|
||||
replacesCertID, err := certificate.MakeARICertID(cert)
|
||||
if err != nil {
|
||||
log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
request := certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: getTime(ctx, "not-before"),
|
||||
|
@ -271,6 +274,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
|
|||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
ReplacesCertID: replacesCertID,
|
||||
}
|
||||
|
||||
certRes, err := client.Certificate.ObtainForCSR(request)
|
||||
|
@ -280,14 +284,6 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
|
|||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
if ariRenewalTime != nil {
|
||||
// Post to the renewalInfo endpoint to indicate that we have renewed and replaced the certificate.
|
||||
err := client.Certificate.UpdateRenewalInfo(certificate.RenewalInfoRequest{Cert: certificates[0]})
|
||||
if err != nil {
|
||||
log.Warnf("[%s] Failed to update renewal info: %v", domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
meta[renewEnvCertDomain] = domain
|
||||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
|
Loading…
Reference in a new issue