forked from TrueCloudLab/lego
feat: support custom duration for certificate (#1925)
This commit is contained in:
parent
8bf0cee70e
commit
c341e6a381
8 changed files with 268 additions and 40 deletions
|
@ -4,14 +4,26 @@ import (
|
|||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
)
|
||||
|
||||
// OrderOptions used to create an order (optional).
|
||||
type OrderOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
}
|
||||
|
||||
type OrderService service
|
||||
|
||||
// New Creates a new order.
|
||||
func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) {
|
||||
return o.NewWithOptions(domains, nil)
|
||||
}
|
||||
|
||||
// NewWithOptions Creates a new order.
|
||||
func (o *OrderService) NewWithOptions(domains []string, opts *OrderOptions) (acme.ExtendedOrder, error) {
|
||||
var identifiers []acme.Identifier
|
||||
for _, domain := range domains {
|
||||
ident := acme.Identifier{Value: domain, Type: "dns"}
|
||||
|
@ -25,6 +37,16 @@ func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) {
|
|||
|
||||
orderReq := acme.Order{Identifiers: identifiers}
|
||||
|
||||
if opts != nil {
|
||||
if !opts.NotAfter.IsZero() {
|
||||
orderReq.NotAfter = opts.NotAfter.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if !opts.NotBefore.IsZero() {
|
||||
orderReq.NotBefore = opts.NotBefore.Format(time.RFC3339)
|
||||
}
|
||||
}
|
||||
|
||||
var order acme.Order
|
||||
resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOrderService_New(t *testing.T) {
|
||||
func TestOrderService_NewWithOptions(t *testing.T) {
|
||||
mux, apiURL := tester.SetupFakeAPI(t)
|
||||
|
||||
// small value keeps test fast
|
||||
|
@ -42,8 +43,15 @@ func TestOrderService_New(t *testing.T) {
|
|||
}
|
||||
|
||||
err = tester.WriteJSONResponse(w, acme.Order{
|
||||
Status: acme.StatusValid,
|
||||
Identifiers: order.Identifiers,
|
||||
Status: acme.StatusValid,
|
||||
Expires: order.Expires,
|
||||
Identifiers: order.Identifiers,
|
||||
NotBefore: order.NotBefore,
|
||||
NotAfter: order.NotAfter,
|
||||
Error: order.Error,
|
||||
Authorizations: order.Authorizations,
|
||||
Finalize: order.Finalize,
|
||||
Certificate: order.Certificate,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -54,16 +62,48 @@ func TestOrderService_New(t *testing.T) {
|
|||
core, err := New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
order, err := core.Orders.New([]string{"example.com"})
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := acme.ExtendedOrder{
|
||||
Order: acme.Order{
|
||||
Status: "valid",
|
||||
Identifiers: []acme.Identifier{{Type: "dns", Value: "example.com"}},
|
||||
testCases := []struct {
|
||||
desc string
|
||||
opts *OrderOptions
|
||||
expected acme.ExtendedOrder
|
||||
}{
|
||||
{
|
||||
desc: "simple",
|
||||
expected: acme.ExtendedOrder{
|
||||
Order: acme.Order{
|
||||
Status: "valid",
|
||||
Identifiers: []acme.Identifier{{Type: "dns", Value: "example.com"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with options",
|
||||
opts: &OrderOptions{
|
||||
NotBefore: time.Date(2023, 1, 1, 1, 0, 0, 0, time.UTC),
|
||||
NotAfter: time.Date(2023, 1, 2, 1, 0, 0, 0, time.UTC),
|
||||
},
|
||||
expected: acme.ExtendedOrder{
|
||||
Order: acme.Order{
|
||||
Status: "valid",
|
||||
Identifiers: []acme.Identifier{{Type: "dns", Value: "example.com"}},
|
||||
NotBefore: "2023-01-01T01:00:00Z",
|
||||
NotAfter: "2023-01-02T01:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, order)
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
order, err := core.Orders.NewWithOptions([]string{"example.com"}, test.opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, order)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func readSignedBody(r *http.Request, privateKey *rsa.PrivateKey) ([]byte, error) {
|
||||
|
|
|
@ -54,10 +54,13 @@ type Resource struct {
|
|||
// 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
|
||||
Domains []string
|
||||
PrivateKey crypto.PrivateKey
|
||||
MustStaple bool
|
||||
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
Bundle bool
|
||||
PrivateKey crypto.PrivateKey
|
||||
MustStaple bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
}
|
||||
|
@ -69,7 +72,10 @@ type ObtainRequest struct {
|
|||
// 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
|
||||
CSR *x509.CertificateRequest
|
||||
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
|
@ -117,7 +123,12 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
|||
log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.New(domains)
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -182,7 +193,12 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
|||
log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.New(domains)
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -388,6 +404,18 @@ func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) error {
|
|||
return c.core.Certificates.Revoke(revokeMsg)
|
||||
}
|
||||
|
||||
// RenewOptions options used by Certifier.RenewWithOptions.
|
||||
type RenewOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
// If true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// Not supported for CSR request.
|
||||
MustStaple bool
|
||||
}
|
||||
|
||||
// Renew takes a Resource and tries to renew the certificate.
|
||||
//
|
||||
// If the renewal process succeeds, the new certificate will be returned in a new CertResource.
|
||||
|
@ -398,7 +426,26 @@ func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) 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 Resource should be non-nil.
|
||||
// Deprecated: use RenewWithOptions instead.
|
||||
func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredChain string) (*Resource, error) {
|
||||
return c.RenewWithOptions(certRes, &RenewOptions{
|
||||
Bundle: bundle,
|
||||
PreferredChain: preferredChain,
|
||||
MustStaple: mustStaple,
|
||||
})
|
||||
}
|
||||
|
||||
// RenewWithOptions takes a Resource and tries to renew the certificate.
|
||||
//
|
||||
// If the renewal process succeeds, the new certificate will be returned in a new CertResource.
|
||||
// Please be aware that this function will return a new certificate in ANY case that is not an error.
|
||||
// If the server does not provide us with a new cert on a GET request to the CertURL
|
||||
// this function will start a new-cert flow where a new certificate gets generated.
|
||||
//
|
||||
// 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 Resource should be non-nil.
|
||||
func (c *Certifier) RenewWithOptions(certRes Resource, options *RenewOptions) (*Resource, 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.
|
||||
|
@ -425,11 +472,17 @@ func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredCh
|
|||
return nil, errP
|
||||
}
|
||||
|
||||
return c.ObtainForCSR(ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
Bundle: bundle,
|
||||
PreferredChain: preferredChain,
|
||||
})
|
||||
request := ObtainForCSRRequest{CSR: csr}
|
||||
|
||||
if options != nil {
|
||||
request.NotBefore = options.NotBefore
|
||||
request.NotAfter = options.NotAfter
|
||||
request.Bundle = options.Bundle
|
||||
request.PreferredChain = options.PreferredChain
|
||||
request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
|
||||
}
|
||||
|
||||
return c.ObtainForCSR(request)
|
||||
}
|
||||
|
||||
var privateKey crypto.PrivateKey
|
||||
|
@ -440,14 +493,21 @@ func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredCh
|
|||
}
|
||||
}
|
||||
|
||||
query := ObtainRequest{
|
||||
Domains: certcrypto.ExtractDomains(x509Cert),
|
||||
Bundle: bundle,
|
||||
PrivateKey: privateKey,
|
||||
MustStaple: mustStaple,
|
||||
PreferredChain: preferredChain,
|
||||
request := ObtainRequest{
|
||||
Domains: certcrypto.ExtractDomains(x509Cert),
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
return c.Obtain(query)
|
||||
|
||||
if options != nil {
|
||||
request.MustStaple = options.MustStaple
|
||||
request.NotBefore = options.NotBefore
|
||||
request.NotAfter = options.NotAfter
|
||||
request.Bundle = options.Bundle
|
||||
request.PreferredChain = options.PreferredChain
|
||||
request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
|
||||
}
|
||||
|
||||
return c.Obtain(request)
|
||||
}
|
||||
|
||||
// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response,
|
||||
|
|
|
@ -75,9 +75,15 @@ func createRenew() *cli.Command {
|
|||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
|
||||
" Only works if the CSR is generated by lego.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "renew-hook",
|
||||
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
|
||||
&cli.TimestampFlag{
|
||||
Name: "not-before",
|
||||
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
|
||||
Layout: time.RFC3339,
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: "not-after",
|
||||
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
|
||||
Layout: time.RFC3339,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "preferred-chain",
|
||||
|
@ -88,6 +94,10 @@ func createRenew() *cli.Command {
|
|||
Name: "always-deactivate-authorizations",
|
||||
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "renew-hook",
|
||||
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "no-random-sleep",
|
||||
Usage: "Do not add a random sleep before the renewal." +
|
||||
|
@ -188,12 +198,15 @@ 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"),
|
||||
NotBefore: getTime(ctx, "not-before"),
|
||||
NotAfter: getTime(ctx, "not-after"),
|
||||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
}
|
||||
|
||||
certRes, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -265,12 +278,16 @@ 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()))
|
||||
|
||||
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
|
||||
request := certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: getTime(ctx, "not-before"),
|
||||
NotAfter: getTime(ctx, "not-after"),
|
||||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
})
|
||||
}
|
||||
|
||||
certRes, err := client.Certificate.ObtainForCSR(request)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
|
@ -40,9 +41,15 @@ func createRun() *cli.Command {
|
|||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
|
||||
" Only works if the CSR is generated by lego.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "run-hook",
|
||||
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
|
||||
&cli.TimestampFlag{
|
||||
Name: "not-before",
|
||||
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
|
||||
Layout: time.RFC3339,
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: "not-after",
|
||||
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
|
||||
Layout: time.RFC3339,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "preferred-chain",
|
||||
|
@ -53,6 +60,10 @@ func createRun() *cli.Command {
|
|||
Name: "always-deactivate-authorizations",
|
||||
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "run-hook",
|
||||
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +188,17 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
|
|||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
}
|
||||
|
||||
notBefore := ctx.Timestamp("not-before")
|
||||
if notBefore != nil {
|
||||
request.NotBefore = *notBefore
|
||||
}
|
||||
|
||||
notAfter := ctx.Timestamp("not-after")
|
||||
if notAfter != nil {
|
||||
request.NotAfter = *notAfter
|
||||
}
|
||||
|
||||
return client.Certificate.Obtain(request)
|
||||
}
|
||||
|
||||
|
@ -187,10 +209,14 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
|
|||
}
|
||||
|
||||
// obtain a certificate for this CSR
|
||||
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
|
||||
request := certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: getTime(ctx, "not-before"),
|
||||
NotAfter: getTime(ctx, "not-after"),
|
||||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
|
||||
})
|
||||
}
|
||||
|
||||
return client.Certificate.ObtainForCSR(request)
|
||||
}
|
||||
|
|
10
cmd/flags.go
10
cmd/flags.go
|
@ -1,6 +1,8 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/urfave/cli/v2"
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
|
@ -142,3 +144,11 @@ func CreateFlags(defaultPath string) []cli.Flag {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getTime(ctx *cli.Context, name string) time.Time {
|
||||
value := ctx.Timestamp(name)
|
||||
if value == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ OPTIONS:
|
|||
--always-deactivate-authorizations value Force the authorizations to be relinquished even if the certificate request was successful.
|
||||
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego. (default: false)
|
||||
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate. (default: false)
|
||||
--not-after value Set the notAfter field in the certificate
|
||||
--not-before value Set the notBefore field in the certificate
|
||||
--preferred-chain value 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.
|
||||
--run-hook value Define a hook. The hook is executed when the certificates are effectively created.
|
||||
"""
|
||||
|
@ -85,6 +87,8 @@ OPTIONS:
|
|||
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego. (default: false)
|
||||
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate. (default: false)
|
||||
--no-random-sleep Do not add a random sleep before the renewal. We do not recommend using this flag if you are doing your renewals in an automated way. (default: false)
|
||||
--not-after value Set the notAfter field in the certificate
|
||||
--not-before value Set the notBefore field in the certificate
|
||||
--preferred-chain value 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.
|
||||
--renew-hook value Define a hook. The hook is executed only when the certificates are effectively renewed.
|
||||
--reuse-key Used to indicate you want to reuse your current private key for the new certificate. (default: false)
|
||||
|
|
|
@ -8,7 +8,9 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
|
@ -255,6 +257,53 @@ func TestChallengeHTTP_Client_Obtain(t *testing.T) {
|
|||
assert.Empty(t, resource.CSR)
|
||||
}
|
||||
|
||||
func TestChallengeHTTP_Client_Obtain_notBefore_notAfter(t *testing.T) {
|
||||
err := os.Setenv("LEGO_CA_CERTIFICATES", "./fixtures/certs/pebble.minica.pem")
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = os.Unsetenv("LEGO_CA_CERTIFICATES") }()
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err, "Could not generate test key")
|
||||
|
||||
user := &fakeUser{privateKey: privateKey}
|
||||
config := lego.NewConfig(user)
|
||||
config.CADirURL = load.PebbleOptions.HealthCheckURL
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
|
||||
require.NoError(t, err)
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
require.NoError(t, err)
|
||||
user.registration = reg
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{"acme.wtf"},
|
||||
NotBefore: now.Add(1 * time.Hour),
|
||||
NotAfter: now.Add(2 * time.Hour),
|
||||
Bundle: true,
|
||||
}
|
||||
resource, err := client.Certificate.Obtain(request)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, resource)
|
||||
assert.Equal(t, "acme.wtf", resource.Domain)
|
||||
assert.Regexp(t, `https://localhost:14000/certZ/[\w\d]{14,}`, resource.CertURL)
|
||||
assert.Regexp(t, `https://localhost:14000/certZ/[\w\d]{14,}`, resource.CertStableURL)
|
||||
assert.NotEmpty(t, resource.Certificate)
|
||||
assert.NotEmpty(t, resource.IssuerCertificate)
|
||||
assert.Empty(t, resource.CSR)
|
||||
|
||||
cert, err := certcrypto.ParsePEMCertificate(resource.Certificate)
|
||||
require.NoError(t, err)
|
||||
assert.WithinDuration(t, now.Add(1*time.Hour), cert.NotBefore, 1*time.Second)
|
||||
assert.WithinDuration(t, now.Add(2*time.Hour), cert.NotAfter, 1*time.Second)
|
||||
}
|
||||
|
||||
func TestChallengeHTTP_Client_Registration_QueryRegistration(t *testing.T) {
|
||||
err := os.Setenv("LEGO_CA_CERTIFICATES", "./fixtures/certs/pebble.minica.pem")
|
||||
require.NoError(t, err)
|
||||
|
|
Loading…
Reference in a new issue