mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
refactor: isolate flag usages (#2824)
This commit is contained in:
parent
ab4e321904
commit
d2f3cc457b
18 changed files with 558 additions and 567 deletions
|
|
@ -11,14 +11,10 @@ import (
|
|||
|
||||
"github.com/go-acme/lego/v5/certcrypto"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
flgAccounts = "accounts"
|
||||
flgNames = "names"
|
||||
)
|
||||
|
||||
func createList() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "list",
|
||||
|
|
@ -28,22 +24,6 @@ func createList() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func createListFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: flgAccounts,
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Display accounts.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNames,
|
||||
Aliases: []string{"n"},
|
||||
Usage: "Display certificate names only.",
|
||||
},
|
||||
CreatePathFlag(false),
|
||||
}
|
||||
}
|
||||
|
||||
func list(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.Bool(flgAccounts) && !cmd.Bool(flgNames) {
|
||||
if err := listAccount(ctx, cmd); err != nil {
|
||||
|
|
@ -111,7 +91,10 @@ func listCertificates(_ context.Context, cmd *cli.Command) error {
|
|||
}
|
||||
|
||||
func listAccount(_ context.Context, cmd *cli.Command) error {
|
||||
accountsStorage := newAccountsStorage(cmd)
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
|
||||
if err != nil {
|
||||
|
|
|
|||
82
cmd/cmd_register.go
Normal file
82
cmd/cmd_register.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/lego"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/go-acme/lego/v5/registration"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// TODO(ldez): add register command.
|
||||
|
||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||
|
||||
Your account credentials have been saved in your
|
||||
configuration directory at "%s".
|
||||
|
||||
You should make a secure backup of this folder now. This
|
||||
configuration directory will also contain private keys
|
||||
generated by lego and certificates obtained from the ACME
|
||||
server. Making regular backups of this folder is ideal.
|
||||
`
|
||||
|
||||
func registerAccount(ctx context.Context, cmd *cli.Command, client *lego.Client) (*registration.Resource, error) {
|
||||
accepted := handleTOS(cmd, client)
|
||||
if !accepted {
|
||||
log.Fatal("You did not accept the TOS. Unable to proceed.")
|
||||
}
|
||||
|
||||
if cmd.Bool(flgEAB) {
|
||||
kid := cmd.String(flgKID)
|
||||
hmacEncoded := cmd.String(flgHMAC)
|
||||
|
||||
if kid == "" || hmacEncoded == "" {
|
||||
log.Fatal(fmt.Sprintf("Requires arguments --%s and --%s.", flgKID, flgHMAC))
|
||||
}
|
||||
|
||||
return client.Registration.RegisterWithExternalAccountBinding(ctx, registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: accepted,
|
||||
Kid: kid,
|
||||
HmacEncoded: hmacEncoded,
|
||||
})
|
||||
}
|
||||
|
||||
return client.Registration.Register(ctx, registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
}
|
||||
|
||||
func handleTOS(cmd *cli.Command, client *lego.Client) bool {
|
||||
// Check for a global accept override
|
||||
if cmd.Bool(flgAcceptTOS) {
|
||||
return true
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
log.Warn("Please review the TOS", slog.String("url", client.GetToSURL()))
|
||||
|
||||
for {
|
||||
fmt.Println("Do you accept the TOS? Y/n")
|
||||
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal("Could not read from the console", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
text = strings.Trim(text, "\r\n")
|
||||
switch text {
|
||||
case "", "y", "Y":
|
||||
return true
|
||||
case "n", "N":
|
||||
return false
|
||||
default:
|
||||
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
|
||||
}
|
||||
}
|
||||
}
|
||||
174
cmd/cmd_renew.go
174
cmd/cmd_renew.go
|
|
@ -25,19 +25,6 @@ import (
|
|||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// Flag names.
|
||||
const (
|
||||
flgRenewDays = "days"
|
||||
flgRenewDynamic = "dynamic"
|
||||
flgARIDisable = "ari-disable"
|
||||
flgARIWaitToRenewDuration = "ari-wait-to-renew-duration"
|
||||
flgReuseKey = "reuse-key"
|
||||
flgRenewHook = "renew-hook"
|
||||
flgRenewHookTimeout = "renew-hook-timeout"
|
||||
flgNoRandomSleep = "no-random-sleep"
|
||||
flgForceCertDomains = "force-cert-domains"
|
||||
)
|
||||
|
||||
func createRenew() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "renew",
|
||||
|
|
@ -66,102 +53,24 @@ func createRenew() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func createRenewFlags() []cli.Flag {
|
||||
flags := CreateFlags()
|
||||
|
||||
flags = append(flags,
|
||||
&cli.IntFlag{
|
||||
Name: flgRenewDays,
|
||||
Value: 30,
|
||||
Usage: "The number of days left on a certificate to renew it.",
|
||||
},
|
||||
// TODO(ldez): in v5, remove this flag, use this behavior as default.
|
||||
&cli.BoolFlag{
|
||||
Name: flgRenewDynamic,
|
||||
Value: false,
|
||||
Usage: "Compute dynamically, based on the lifetime of the certificate(s), when to renew: use 1/3rd of the lifetime left, or 1/2 of the lifetime for short-lived certificates). This supersedes --days and will be the default behavior in Lego v5.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgARIDisable,
|
||||
Usage: "Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: flgARIWaitToRenewDuration,
|
||||
Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgReuseKey,
|
||||
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNoBundle,
|
||||
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgMustStaple,
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
|
||||
" Only works if the CSR is generated by lego.",
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotBefore,
|
||||
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotAfter,
|
||||
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgPreferredChain,
|
||||
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: flgProfile,
|
||||
Usage: "If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgAlwaysDeactivateAuthorizations,
|
||||
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgRenewHook,
|
||||
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: flgRenewHookTimeout,
|
||||
Usage: "Define the timeout for the hook execution.",
|
||||
Value: 2 * time.Minute,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNoRandomSleep,
|
||||
Usage: "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.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgForceCertDomains,
|
||||
Usage: "Check and ensure that the cert's domain list matches those passed in the domains argument.",
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func renew(ctx context.Context, cmd *cli.Command) error {
|
||||
account, keyType := setupAccount(ctx, cmd, newAccountsStorage(cmd))
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
keyType := getKeyType(cmd)
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Fatal("The account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
|
||||
}
|
||||
|
||||
certsStorage := newCertificatesStorage(cmd)
|
||||
|
||||
bundle := !cmd.Bool(flgNoBundle)
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
meta := map[string]string{
|
||||
hook.EnvAccountEmail: account.Email,
|
||||
|
|
@ -169,14 +78,14 @@ func renew(ctx context.Context, cmd *cli.Command) error {
|
|||
|
||||
// CSR
|
||||
if cmd.IsSet(flgCSR) {
|
||||
return renewForCSR(ctx, cmd, account, keyType, certsStorage, bundle, meta)
|
||||
return renewForCSR(ctx, cmd, account, keyType, certsStorage, meta)
|
||||
}
|
||||
|
||||
// Domains
|
||||
return renewForDomains(ctx, cmd, account, keyType, certsStorage, bundle, meta)
|
||||
return renewForDomains(ctx, cmd, account, keyType, certsStorage, meta)
|
||||
}
|
||||
|
||||
func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
|
||||
func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *storage.CertificatesStorage, meta map[string]string) error {
|
||||
domains := cmd.StringSlice(flgDomains)
|
||||
domain := domains[0]
|
||||
|
||||
|
|
@ -185,7 +94,10 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, storage.ExtCert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while loading the certificate.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
log.Fatal("Error while loading the certificate.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
|
@ -200,7 +112,9 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
if !cmd.Bool(flgARIDisable) {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
|
||||
ariRenewalTime = getARIRenewalTime(ctx, cmd, cert, domain, client)
|
||||
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
|
||||
|
||||
ariRenewalTime = getARIRenewalTime(ctx, willingToSleep, cert, domain, client)
|
||||
if ariRenewalTime != nil {
|
||||
now := time.Now().UTC()
|
||||
|
||||
|
|
@ -276,17 +190,9 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
renewalDomains = merge(certDomains, domains)
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: renewalDomains,
|
||||
PrivateKey: privateKey,
|
||||
MustStaple: cmd.Bool(flgMustStaple),
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: bundle,
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
request := newObtainRequest(cmd, renewalDomains)
|
||||
|
||||
request.PrivateKey = privateKey
|
||||
|
||||
if replacesCertID != "" {
|
||||
request.ReplacesCertID = replacesCertID
|
||||
|
|
@ -301,17 +207,16 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
addPathToMetadata(meta, domain, certRes, certsStorage)
|
||||
hook.AddPathToMetadata(meta, certRes.Domain, certRes, certsStorage)
|
||||
|
||||
return hook.Launch(ctx, cmd.String(flgRenewHook), cmd.Duration(flgRenewHookTimeout), meta)
|
||||
}
|
||||
|
||||
func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
|
||||
func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *storage.CertificatesStorage, meta map[string]string) error {
|
||||
csr, err := readCSRFile(cmd.String(flgCSR))
|
||||
if err != nil {
|
||||
log.Fatal("Could not read CSR file.",
|
||||
slog.String("flag", flgCSR),
|
||||
slog.String("filepath", cmd.String(flgCSR)),
|
||||
slog.String(flgCSR, cmd.String(flgCSR)),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
}
|
||||
|
|
@ -344,7 +249,9 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
if !cmd.Bool(flgARIDisable) {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
|
||||
ariRenewalTime = getARIRenewalTime(ctx, cmd, cert, domain, client)
|
||||
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
|
||||
|
||||
ariRenewalTime = getARIRenewalTime(ctx, willingToSleep, cert, domain, client)
|
||||
if ariRenewalTime != nil {
|
||||
now := time.Now().UTC()
|
||||
|
||||
|
|
@ -353,7 +260,8 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
log.Info("Sleeping until renewal time",
|
||||
log.DomainAttr(domain),
|
||||
slog.Duration("sleep", ariRenewalTime.Sub(now)),
|
||||
slog.Time("renewalTime", *ariRenewalTime))
|
||||
slog.Time("renewalTime", *ariRenewalTime),
|
||||
)
|
||||
time.Sleep(ariRenewalTime.Sub(now))
|
||||
}
|
||||
}
|
||||
|
|
@ -379,15 +287,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
slog.Int("hoursRemaining", int(timeLeft.Hours())),
|
||||
)
|
||||
|
||||
request := certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: bundle,
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
request := newObtainForCSRRequest(cmd, csr)
|
||||
|
||||
if replacesCertID != "" {
|
||||
request.ReplacesCertID = replacesCertID
|
||||
|
|
@ -400,7 +300,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
addPathToMetadata(meta, domain, certRes, certsStorage)
|
||||
hook.AddPathToMetadata(meta, domain, certRes, certsStorage)
|
||||
|
||||
return hook.Launch(ctx, cmd.String(flgRenewHook), cmd.Duration(flgRenewHookTimeout), meta)
|
||||
}
|
||||
|
|
@ -453,7 +353,7 @@ func needRenewalDynamic(x509Cert *x509.Certificate, domain string, now time.Time
|
|||
}
|
||||
|
||||
// getARIRenewalTime checks if the certificate needs to be renewed using the renewalInfo endpoint.
|
||||
func getARIRenewalTime(ctx context.Context, cmd *cli.Command, cert *x509.Certificate, domain string, client *lego.Client) *time.Time {
|
||||
func getARIRenewalTime(ctx context.Context, willingToSleep time.Duration, cert *x509.Certificate, domain string, client *lego.Client) *time.Time {
|
||||
if cert.IsCA {
|
||||
log.Fatal("Certificate bundle starts with a CA certificate.", log.DomainAttr(domain))
|
||||
}
|
||||
|
|
@ -479,7 +379,7 @@ func getARIRenewalTime(ctx context.Context, cmd *cli.Command, cert *x509.Certifi
|
|||
|
||||
now := time.Now().UTC()
|
||||
|
||||
renewalTime := renewalInfo.ShouldRenewAt(now, cmd.Duration(flgARIWaitToRenewDuration))
|
||||
renewalTime := renewalInfo.ShouldRenewAt(now, willingToSleep)
|
||||
if renewalTime == nil {
|
||||
log.Info("acme: renewalInfo endpoint indicates that renewal is not needed.", log.DomainAttr(domain))
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -4,18 +4,11 @@ import (
|
|||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-acme/lego/v5/acme"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// Flag names.
|
||||
const (
|
||||
flgKeep = "keep"
|
||||
flgReason = "reason"
|
||||
)
|
||||
|
||||
func createRevoke() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "revoke",
|
||||
|
|
@ -25,32 +18,15 @@ func createRevoke() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func createRevokeFlags() []cli.Flag {
|
||||
flags := CreateFlags()
|
||||
|
||||
flags = append(flags,
|
||||
&cli.BoolFlag{
|
||||
Name: flgKeep,
|
||||
Aliases: []string{"k"},
|
||||
Usage: "Keep the certificates after the revocation instead of archiving them.",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flgReason,
|
||||
Usage: "Identifies the reason for the certificate revocation." +
|
||||
" See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." +
|
||||
" Valid values are:" +
|
||||
" 0 (unspecified), 1 (keyCompromise), 2 (cACompromise), 3 (affiliationChanged)," +
|
||||
" 4 (superseded), 5 (cessationOfOperation), 6 (certificateHold), 8 (removeFromCRL)," +
|
||||
" 9 (privilegeWithdrawn), or 10 (aACompromise).",
|
||||
Value: acme.CRLReasonUnspecified,
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func revoke(ctx context.Context, cmd *cli.Command) error {
|
||||
account, keyType := setupAccount(ctx, cmd, newAccountsStorage(cmd))
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
keyType := getKeyType(cmd)
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Fatal("Account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
|
||||
|
|
@ -58,7 +34,11 @@ func revoke(ctx context.Context, cmd *cli.Command) error {
|
|||
|
||||
client := newClient(cmd, account, keyType)
|
||||
|
||||
certsStorage := newCertificatesStorage(cmd)
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
certsStorage.CreateRootFolder()
|
||||
|
||||
for _, domain := range cmd.StringSlice(flgDomains) {
|
||||
|
|
|
|||
215
cmd/cmd_run.go
215
cmd/cmd_run.go
|
|
@ -1,13 +1,9 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/hook"
|
||||
|
|
@ -18,20 +14,6 @@ import (
|
|||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// Flag names.
|
||||
const (
|
||||
flgNoBundle = "no-bundle"
|
||||
flgMustStaple = "must-staple"
|
||||
flgNotBefore = "not-before"
|
||||
flgNotAfter = "not-after"
|
||||
flgPrivateKey = "private-key"
|
||||
flgPreferredChain = "preferred-chain"
|
||||
flgProfile = "profile"
|
||||
flgAlwaysDeactivateAuthorizations = "always-deactivate-authorizations"
|
||||
flgRunHook = "run-hook"
|
||||
flgRunHookTimeout = "run-hook-timeout"
|
||||
)
|
||||
|
||||
func createRun() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "run",
|
||||
|
|
@ -56,84 +38,22 @@ func createRun() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func createRunFlags() []cli.Flag {
|
||||
flags := CreateFlags()
|
||||
|
||||
flags = append(flags,
|
||||
&cli.BoolFlag{
|
||||
Name: flgNoBundle,
|
||||
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgMustStaple,
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
|
||||
" Only works if the CSR is generated by lego.",
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotBefore,
|
||||
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotAfter,
|
||||
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgPrivateKey,
|
||||
Usage: "Path to private key (in PEM encoding) for the certificate. By default, the private key is generated.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgPreferredChain,
|
||||
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: flgProfile,
|
||||
Usage: "If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgAlwaysDeactivateAuthorizations,
|
||||
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgRunHook,
|
||||
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: flgRunHookTimeout,
|
||||
Usage: "Define the timeout for the hook execution.",
|
||||
Value: 2 * time.Minute,
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||
|
||||
Your account credentials have been saved in your
|
||||
configuration directory at "%s".
|
||||
|
||||
You should make a secure backup of this folder now. This
|
||||
configuration directory will also contain private keys
|
||||
generated by lego and certificates obtained from the ACME
|
||||
server. Making regular backups of this folder is ideal.
|
||||
`
|
||||
|
||||
func run(ctx context.Context, cmd *cli.Command) error {
|
||||
accountsStorage := newAccountsStorage(cmd)
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
account, keyType := setupAccount(ctx, cmd, accountsStorage)
|
||||
keyType := getKeyType(cmd)
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
|
||||
client := setupClient(cmd, account, keyType)
|
||||
|
||||
if account.Registration == nil {
|
||||
reg, err := register(ctx, cmd, client)
|
||||
var reg *registration.Resource
|
||||
|
||||
reg, err = registerAccount(ctx, cmd, client)
|
||||
if err != nil {
|
||||
log.Fatal("Could not complete registration.", log.ErrorAttr(err))
|
||||
}
|
||||
|
|
@ -146,7 +66,11 @@ func run(ctx context.Context, cmd *cli.Command) error {
|
|||
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
|
||||
}
|
||||
|
||||
certsStorage := newCertificatesStorage(cmd)
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
certsStorage.CreateRootFolder()
|
||||
|
||||
cert, err := obtainCertificate(ctx, cmd, client)
|
||||
|
|
@ -162,82 +86,19 @@ func run(ctx context.Context, cmd *cli.Command) error {
|
|||
hook.EnvAccountEmail: account.Email,
|
||||
}
|
||||
|
||||
addPathToMetadata(meta, cert.Domain, cert, certsStorage)
|
||||
hook.AddPathToMetadata(meta, cert.Domain, cert, certsStorage)
|
||||
|
||||
return hook.Launch(ctx, cmd.String(flgRunHook), cmd.Duration(flgRunHookTimeout), meta)
|
||||
}
|
||||
|
||||
func handleTOS(cmd *cli.Command, client *lego.Client) bool {
|
||||
// Check for a global accept override
|
||||
if cmd.Bool(flgAcceptTOS) {
|
||||
return true
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
log.Warn("Please review the TOS", slog.String("url", client.GetToSURL()))
|
||||
|
||||
for {
|
||||
fmt.Println("Do you accept the TOS? Y/n")
|
||||
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal("Could not read from the console", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
text = strings.Trim(text, "\r\n")
|
||||
switch text {
|
||||
case "", "y", "Y":
|
||||
return true
|
||||
case "n", "N":
|
||||
return false
|
||||
default:
|
||||
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func register(ctx context.Context, cmd *cli.Command, client *lego.Client) (*registration.Resource, error) {
|
||||
accepted := handleTOS(cmd, client)
|
||||
if !accepted {
|
||||
log.Fatal("You did not accept the TOS. Unable to proceed.")
|
||||
}
|
||||
|
||||
if cmd.Bool(flgEAB) {
|
||||
kid := cmd.String(flgKID)
|
||||
hmacEncoded := cmd.String(flgHMAC)
|
||||
|
||||
if kid == "" || hmacEncoded == "" {
|
||||
log.Fatal(fmt.Sprintf("Requires arguments --%s and --%s.", flgKID, flgHMAC))
|
||||
}
|
||||
|
||||
return client.Registration.RegisterWithExternalAccountBinding(ctx, registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: accepted,
|
||||
Kid: kid,
|
||||
HmacEncoded: hmacEncoded,
|
||||
})
|
||||
}
|
||||
|
||||
return client.Registration.Register(ctx, registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
}
|
||||
|
||||
func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Client) (*certificate.Resource, error) {
|
||||
bundle := !cmd.Bool(flgNoBundle)
|
||||
|
||||
domains := cmd.StringSlice(flgDomains)
|
||||
|
||||
if len(domains) > 0 {
|
||||
// obtain a certificate, generating a new private key
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
MustStaple: cmd.Bool(flgMustStaple),
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: bundle,
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
request := newObtainRequest(cmd, domains)
|
||||
|
||||
// TODO(ldez): factorize?
|
||||
if cmd.IsSet(flgPrivateKey) {
|
||||
var err error
|
||||
|
||||
|
|
@ -257,16 +118,9 @@ func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Clien
|
|||
}
|
||||
|
||||
// obtain a certificate for this CSR
|
||||
request := certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: bundle,
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
request := newObtainForCSRRequest(cmd, csr)
|
||||
|
||||
// TODO(ldez): factorize?
|
||||
if cmd.IsSet(flgPrivateKey) {
|
||||
var err error
|
||||
|
||||
|
|
@ -278,3 +132,28 @@ func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Clien
|
|||
|
||||
return client.Certificate.ObtainForCSR(ctx, request)
|
||||
}
|
||||
|
||||
func newObtainRequest(cmd *cli.Command, domains []string) certificate.ObtainRequest {
|
||||
return certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
MustStaple: cmd.Bool(flgMustStaple),
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: !cmd.Bool(flgNoBundle),
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
}
|
||||
|
||||
func newObtainForCSRRequest(cmd *cli.Command, csr *x509.CertificateRequest) certificate.ObtainForCSRRequest {
|
||||
return certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
NotBefore: cmd.Timestamp(flgNotBefore),
|
||||
NotAfter: cmd.Timestamp(flgNotAfter),
|
||||
Bundle: !cmd.Bool(flgNoBundle),
|
||||
PreferredChain: cmd.String(flgPreferredChain),
|
||||
Profile: cmd.String(flgProfile),
|
||||
AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
294
cmd/flags.go
294
cmd/flags.go
|
|
@ -4,7 +4,9 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v5/acme"
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/lego"
|
||||
|
|
@ -17,15 +19,25 @@ const (
|
|||
flgDomains = "domains"
|
||||
flgAcceptTOS = "accept-tos"
|
||||
flgEmail = "email"
|
||||
flgCSR = "csr"
|
||||
flgEAB = "eab"
|
||||
flgKID = "kid"
|
||||
flgHMAC = "hmac"
|
||||
)
|
||||
|
||||
// Flag names related to Obtain certificates.
|
||||
const (
|
||||
flgCSR = "csr"
|
||||
flgNoBundle = "no-bundle"
|
||||
flgMustStaple = "must-staple"
|
||||
flgNotBefore = "not-before"
|
||||
flgNotAfter = "not-after"
|
||||
flgPreferredChain = "preferred-chain"
|
||||
flgProfile = "profile"
|
||||
flgAlwaysDeactivateAuthorizations = "always-deactivate-authorizations"
|
||||
)
|
||||
|
||||
// Flag names related to the output.
|
||||
const (
|
||||
flgFilename = "filename"
|
||||
flgPath = "path"
|
||||
flgPEM = "pem"
|
||||
flgPFX = "pfx"
|
||||
|
|
@ -74,6 +86,43 @@ const (
|
|||
flgDNSTimeout = "dns-timeout"
|
||||
)
|
||||
|
||||
// Flags names related to hooks.
|
||||
const (
|
||||
flgRenewHook = "renew-hook"
|
||||
flgRenewHookTimeout = "renew-hook-timeout"
|
||||
|
||||
flgRunHook = "run-hook"
|
||||
flgRunHookTimeout = "run-hook-timeout"
|
||||
)
|
||||
|
||||
// Flag names related to the specific run command.
|
||||
const (
|
||||
flgPrivateKey = "private-key"
|
||||
)
|
||||
|
||||
// Flag names related to the specific renew command.
|
||||
const (
|
||||
flgRenewDays = "days"
|
||||
flgRenewDynamic = "dynamic"
|
||||
flgARIDisable = "ari-disable"
|
||||
flgARIWaitToRenewDuration = "ari-wait-to-renew-duration"
|
||||
flgReuseKey = "reuse-key"
|
||||
flgNoRandomSleep = "no-random-sleep"
|
||||
flgForceCertDomains = "force-cert-domains"
|
||||
)
|
||||
|
||||
// Flag names related to the specific revoke command.
|
||||
const (
|
||||
flgKeep = "keep"
|
||||
flgReason = "reason"
|
||||
)
|
||||
|
||||
// Flag names related to the list command.
|
||||
const (
|
||||
flgAccounts = "accounts"
|
||||
flgNames = "names"
|
||||
)
|
||||
|
||||
// Environment variable names.
|
||||
const (
|
||||
envEAB = "LEGO_EAB"
|
||||
|
|
@ -132,6 +181,16 @@ func CreateACMEClientFlags() []cli.Flag {
|
|||
}
|
||||
}
|
||||
|
||||
func CreateChallengesFlags() []cli.Flag {
|
||||
var flags []cli.Flag
|
||||
|
||||
flags = append(flags, CreateHTTPChallengeFlags()...)
|
||||
flags = append(flags, CreateTLSChallengeFlags()...)
|
||||
flags = append(flags, CreateDNSChallengeFlags()...)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func CreateHTTPChallengeFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
|
|
@ -227,10 +286,6 @@ func CreateDNSChallengeFlags() []cli.Flag {
|
|||
|
||||
func CreateOutputFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flgFilename,
|
||||
Usage: "(deprecated) Filename of the generated certificate.",
|
||||
},
|
||||
CreatePathFlag(true),
|
||||
&cli.BoolFlag{
|
||||
Name: flgPEM,
|
||||
|
|
@ -256,27 +311,6 @@ func CreateOutputFlags() []cli.Flag {
|
|||
}
|
||||
}
|
||||
|
||||
func CreatePathFlag(forceCreation bool) cli.Flag {
|
||||
return &cli.StringFlag{
|
||||
Name: flgPath,
|
||||
Sources: cli.NewValueSourceChain(cli.EnvVar(envPath), &defaultPathValueSource{}),
|
||||
Usage: "Directory to use for storing the data.",
|
||||
Validator: func(s string) error {
|
||||
if !forceCreation {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := storage.CreateNonExistingFolder(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check/create the path %q: %w", s, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Required: true,
|
||||
}
|
||||
}
|
||||
|
||||
func CreateAccountFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
|
|
@ -290,11 +324,6 @@ func CreateAccountFlags() []cli.Flag {
|
|||
Sources: cli.EnvVars(envEmail),
|
||||
Usage: "Email used for registration and recovery contact.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgCSR,
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Certificate signing request filename, if an external CSR is to be used.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgEAB,
|
||||
Sources: cli.EnvVars(envEAB),
|
||||
|
|
@ -313,25 +342,208 @@ func CreateAccountFlags() []cli.Flag {
|
|||
}
|
||||
}
|
||||
|
||||
func CreateFlags() []cli.Flag {
|
||||
flags := []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: flgDomains,
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Add a domain to the process. Can be specified multiple times.",
|
||||
func CreateObtainFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flgCSR,
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Certificate signing request filename, if an external CSR is to be used.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNoBundle,
|
||||
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgMustStaple,
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
|
||||
" Only works if the CSR is generated by lego.",
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotBefore,
|
||||
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: flgNotAfter,
|
||||
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{time.RFC3339},
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgPreferredChain,
|
||||
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: flgProfile,
|
||||
Usage: "If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flgAlwaysDeactivateAuthorizations,
|
||||
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateHookFlags(name, timeoutName string) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: name,
|
||||
Usage: "Define a hook. The hook is executed only when the certificates are effectively created/renewed.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: timeoutName,
|
||||
Usage: "Define the timeout for the hook execution.",
|
||||
Value: 2 * time.Minute,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateBaseFlags() []cli.Flag {
|
||||
var flags []cli.Flag
|
||||
|
||||
flags = append(flags, CreateDomainFlag())
|
||||
flags = append(flags, CreateAccountFlags()...)
|
||||
flags = append(flags, CreateACMEClientFlags()...)
|
||||
flags = append(flags, CreateOutputFlags()...)
|
||||
flags = append(flags, CreateHTTPChallengeFlags()...)
|
||||
flags = append(flags, CreateTLSChallengeFlags()...)
|
||||
flags = append(flags, CreateDNSChallengeFlags()...)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func createRunFlags() []cli.Flag {
|
||||
flags := CreateBaseFlags()
|
||||
|
||||
flags = append(flags, CreateChallengesFlags()...)
|
||||
flags = append(flags, CreateObtainFlags()...)
|
||||
flags = append(flags, CreateHookFlags(flgRunHook, flgRunHookTimeout)...)
|
||||
|
||||
flags = append(flags,
|
||||
&cli.StringFlag{
|
||||
Name: flgPrivateKey,
|
||||
Usage: "Path to private key (in PEM encoding) for the certificate. By default, the private key is generated.",
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func createRenewFlags() []cli.Flag {
|
||||
flags := CreateBaseFlags()
|
||||
|
||||
flags = append(flags, CreateChallengesFlags()...)
|
||||
flags = append(flags, CreateObtainFlags()...)
|
||||
flags = append(flags, CreateHookFlags(flgRenewHook, flgRenewHookTimeout)...)
|
||||
|
||||
flags = append(flags,
|
||||
&cli.IntFlag{
|
||||
Name: flgRenewDays,
|
||||
Value: 30,
|
||||
Usage: "The number of days left on a certificate to renew it.",
|
||||
},
|
||||
// TODO(ldez): in v5, remove this flag, use this behavior as default.
|
||||
&cli.BoolFlag{
|
||||
Name: flgRenewDynamic,
|
||||
Value: false,
|
||||
Usage: "Compute dynamically, based on the lifetime of the certificate(s), when to renew: use 1/3rd of the lifetime left, or 1/2 of the lifetime for short-lived certificates). This supersedes --days and will be the default behavior in Lego v5.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgARIDisable,
|
||||
Usage: "Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: flgARIWaitToRenewDuration,
|
||||
Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgReuseKey,
|
||||
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNoRandomSleep,
|
||||
Usage: "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.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgForceCertDomains,
|
||||
Usage: "Check and ensure that the cert's domain list matches those passed in the domains argument.",
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func createRevokeFlags() []cli.Flag {
|
||||
flags := CreateBaseFlags()
|
||||
|
||||
flags = append(flags,
|
||||
&cli.BoolFlag{
|
||||
Name: flgKeep,
|
||||
Aliases: []string{"k"},
|
||||
Usage: "Keep the certificates after the revocation instead of archiving them.",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flgReason,
|
||||
Usage: "Identifies the reason for the certificate revocation." +
|
||||
" See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." +
|
||||
" Valid values are:" +
|
||||
" 0 (unspecified), 1 (keyCompromise), 2 (cACompromise), 3 (affiliationChanged)," +
|
||||
" 4 (superseded), 5 (cessationOfOperation), 6 (certificateHold), 8 (removeFromCRL)," +
|
||||
" 9 (privilegeWithdrawn), or 10 (aACompromise).",
|
||||
Value: acme.CRLReasonUnspecified,
|
||||
},
|
||||
)
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func createListFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: flgAccounts,
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Display accounts.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNames,
|
||||
Aliases: []string{"n"},
|
||||
Usage: "Display certificate names only.",
|
||||
},
|
||||
CreatePathFlag(false),
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDomainFlag() cli.Flag {
|
||||
return &cli.StringSliceFlag{
|
||||
Name: flgDomains,
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Add a domain to the process. Can be specified multiple times or use comma as a separator.",
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePathFlag(forceCreation bool) cli.Flag {
|
||||
return &cli.StringFlag{
|
||||
Name: flgPath,
|
||||
Sources: cli.NewValueSourceChain(cli.EnvVar(envPath), &defaultPathValueSource{}),
|
||||
Usage: "Directory to use for storing the data.",
|
||||
Validator: func(s string) error {
|
||||
if !forceCreation {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := storage.CreateNonExistingFolder(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check/create the path %q: %w", s, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Required: true,
|
||||
}
|
||||
}
|
||||
|
||||
// defaultPathValueSource gets the default path based on the current working directory.
|
||||
// The field value is only here because clihelp/generator.
|
||||
type defaultPathValueSource struct{}
|
||||
|
|
|
|||
25
cmd/hook.go
25
cmd/hook.go
|
|
@ -1,25 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/hook"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
)
|
||||
|
||||
func addPathToMetadata(meta map[string]string, domain string, certRes *certificate.Resource, certsStorage *CertificatesStorage) {
|
||||
meta[hook.EnvCertDomain] = domain
|
||||
meta[hook.EnvCertPath] = certsStorage.GetFileName(domain, storage.ExtCert)
|
||||
meta[hook.EnvCertKeyPath] = certsStorage.GetFileName(domain, storage.ExtKey)
|
||||
|
||||
if certRes.IssuerCertificate != nil {
|
||||
meta[hook.EnvIssuerCertKeyPath] = certsStorage.GetFileName(domain, storage.ExtIssuer)
|
||||
}
|
||||
|
||||
if certsStorage.IsPEM() {
|
||||
meta[hook.EnvCertPEMPath] = certsStorage.GetFileName(domain, storage.ExtPEM)
|
||||
}
|
||||
|
||||
if certsStorage.IsPFX() {
|
||||
meta[hook.EnvCertPFXPath] = certsStorage.GetFileName(domain, storage.ExtPFX)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@ import (
|
|||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -82,3 +85,22 @@ func metaToEnv(meta map[string]string) []string {
|
|||
|
||||
return envs
|
||||
}
|
||||
|
||||
// AddPathToMetadata adds information about the certificate to the metadata map.
|
||||
func AddPathToMetadata(meta map[string]string, domain string, certRes *certificate.Resource, certsStorage *storage.CertificatesStorage) {
|
||||
meta[EnvCertDomain] = domain
|
||||
meta[EnvCertPath] = certsStorage.GetFileName(domain, storage.ExtCert)
|
||||
meta[EnvCertKeyPath] = certsStorage.GetFileName(domain, storage.ExtKey)
|
||||
|
||||
if certRes.IssuerCertificate != nil {
|
||||
meta[EnvIssuerCertKeyPath] = certsStorage.GetFileName(domain, storage.ExtIssuer)
|
||||
}
|
||||
|
||||
if certsStorage.IsPEM() {
|
||||
meta[EnvCertPEMPath] = certsStorage.GetFileName(domain, storage.ExtPEM)
|
||||
}
|
||||
|
||||
if certsStorage.IsPFX() {
|
||||
meta[EnvCertPFXPath] = certsStorage.GetFileName(domain, storage.ExtPFX)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ func (a *Account) GetEmail() string {
|
|||
return a.Email
|
||||
}
|
||||
|
||||
// GetPrivateKey returns the private RSA account key.
|
||||
// GetPrivateKey returns the private account key.
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
return a.key
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -19,12 +24,23 @@ const (
|
|||
baseArchivesFolderName = "archives"
|
||||
)
|
||||
|
||||
func getCertificatesRootPath(basePath string) string {
|
||||
return filepath.Join(basePath, baseCertificatesFolderName)
|
||||
// CertificatesStorage a certificates' storage.
|
||||
type CertificatesStorage struct {
|
||||
*CertificatesWriter
|
||||
*CertificatesReader
|
||||
}
|
||||
|
||||
func getCertificatesArchivePath(basePath string) string {
|
||||
return filepath.Join(basePath, baseArchivesFolderName)
|
||||
// NewCertificatesStorage create a new certificates storage.
|
||||
func NewCertificatesStorage(config CertificatesWriterConfig) (*CertificatesStorage, error) {
|
||||
writer, err := NewCertificatesWriter(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificates storage writer: %w", err)
|
||||
}
|
||||
|
||||
return &CertificatesStorage{
|
||||
CertificatesWriter: writer,
|
||||
CertificatesReader: NewCertificatesReader(config.BasePath),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func CreateNonExistingFolder(path string) error {
|
||||
|
|
@ -36,3 +52,24 @@ func CreateNonExistingFolder(path string) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCertificatesRootPath(basePath string) string {
|
||||
return filepath.Join(basePath, baseCertificatesFolderName)
|
||||
}
|
||||
|
||||
func getCertificatesArchivePath(basePath string) string {
|
||||
return filepath.Join(basePath, baseArchivesFolderName)
|
||||
}
|
||||
|
||||
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).
|
||||
func sanitizedDomain(domain string) string {
|
||||
safe, err := idna.ToASCII(strings.NewReplacer(":", "-", "*", "_").Replace(domain))
|
||||
if err != nil {
|
||||
log.Fatal("Could not sanitize the domain.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
}
|
||||
|
||||
return safe
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ import (
|
|||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/certcrypto"
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
type CertificatesReader struct {
|
||||
|
|
@ -78,16 +76,3 @@ func (s *CertificatesReader) ReadCertificate(domain, extension string) ([]*x509.
|
|||
// The input may be a bundle or a single certificate.
|
||||
return certcrypto.ParsePEMBundle(content)
|
||||
}
|
||||
|
||||
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).
|
||||
func sanitizedDomain(domain string) string {
|
||||
safe, err := idna.ToASCII(strings.NewReplacer(":", "-", "*", "_").Replace(domain))
|
||||
if err != nil {
|
||||
log.Fatal("Could not sanitize the domain.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
}
|
||||
|
||||
return safe
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ type CertificatesWriterConfig struct {
|
|||
PFX bool
|
||||
PFXFormat string
|
||||
PFXPassword string
|
||||
|
||||
Filename string // TODO(ldez): remove
|
||||
}
|
||||
|
||||
// CertificatesWriter a writer of certificate files.
|
||||
|
|
@ -55,8 +53,6 @@ type CertificatesWriter struct {
|
|||
pfx bool
|
||||
pfxFormat string
|
||||
pfxPassword string
|
||||
|
||||
filename string // TODO(ldez): remove
|
||||
}
|
||||
|
||||
// NewCertificatesWriter create a new certificates storage writer.
|
||||
|
|
@ -76,7 +72,6 @@ func NewCertificatesWriter(config CertificatesWriterConfig) (*CertificatesWriter
|
|||
pfx: config.PFX,
|
||||
pfxPassword: config.PFXPassword,
|
||||
pfxFormat: config.PFXFormat,
|
||||
filename: config.Filename,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -247,14 +242,7 @@ func (s *CertificatesWriter) writePFXFile(domain string, certRes *certificate.Re
|
|||
}
|
||||
|
||||
func (s *CertificatesWriter) writeFile(domain, extension string, data []byte) error {
|
||||
var baseFileName string
|
||||
if s.filename != "" {
|
||||
baseFileName = s.filename
|
||||
} else {
|
||||
baseFileName = sanitizedDomain(domain)
|
||||
}
|
||||
|
||||
filePath := filepath.Join(s.rootPath, baseFileName+extension)
|
||||
filePath := filepath.Join(s.rootPath, sanitizedDomain(domain)+extension)
|
||||
|
||||
log.Info("Writing file.",
|
||||
slog.String("filepath", filePath))
|
||||
|
|
|
|||
34
cmd/setup.go
34
cmd/setup.go
|
|
@ -32,21 +32,30 @@ func setupClient(cmd *cli.Command, account *storage.Account, keyType certcrypto.
|
|||
return client
|
||||
}
|
||||
|
||||
func setupAccount(ctx context.Context, cmd *cli.Command, accountsStorage *storage.AccountsStorage) (*storage.Account, certcrypto.KeyType) {
|
||||
keyType := getKeyType(cmd)
|
||||
func setupAccount(ctx context.Context, keyType certcrypto.KeyType, accountsStorage *storage.AccountsStorage) *storage.Account {
|
||||
privateKey := accountsStorage.GetPrivateKey(keyType)
|
||||
|
||||
var account *storage.Account
|
||||
if accountsStorage.ExistsAccountFilePath() {
|
||||
account = accountsStorage.LoadAccount(ctx, privateKey)
|
||||
} else {
|
||||
account = storage.NewAccount(accountsStorage.GetEmail(), privateKey)
|
||||
return accountsStorage.LoadAccount(ctx, privateKey)
|
||||
}
|
||||
|
||||
return account, keyType
|
||||
return storage.NewAccount(accountsStorage.GetEmail(), privateKey)
|
||||
}
|
||||
|
||||
func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
|
||||
client, err := lego.NewClient(newClientConfig(cmd, acc, keyType))
|
||||
if err != nil {
|
||||
log.Fatal("Could not create client.", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
if client.GetExternalAccountRequired() && !cmd.IsSet(flgEAB) { // TODO(ldez): handle this flag.
|
||||
log.Fatal(fmt.Sprintf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC))
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func newClientConfig(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Config {
|
||||
config := lego.NewConfig(acc)
|
||||
config.CADirURL = cmd.String(flgServer)
|
||||
|
||||
|
|
@ -83,16 +92,7 @@ func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyTy
|
|||
|
||||
config.HTTPClient = retryClient.StandardClient()
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Fatal("Could not create client.", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
if client.GetExternalAccountRequired() && !cmd.IsSet(flgEAB) {
|
||||
log.Fatal(fmt.Sprintf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC))
|
||||
}
|
||||
|
||||
return client
|
||||
return config
|
||||
}
|
||||
|
||||
// getKeyType the type from which private keys should be generated.
|
||||
|
|
|
|||
|
|
@ -2,51 +2,24 @@ package cmd
|
|||
|
||||
import (
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CertificatesStorage a certificates' storage.
|
||||
type CertificatesStorage struct {
|
||||
*storage.CertificatesWriter
|
||||
*storage.CertificatesReader
|
||||
}
|
||||
|
||||
// newCertificatesStorage create a new certificates storage.
|
||||
func newCertificatesStorage(cmd *cli.Command) *CertificatesStorage {
|
||||
basePath := cmd.String(flgPath)
|
||||
|
||||
config := storage.CertificatesWriterConfig{
|
||||
BasePath: basePath,
|
||||
func newCertificatesWriterConfig(cmd *cli.Command) storage.CertificatesWriterConfig {
|
||||
return storage.CertificatesWriterConfig{
|
||||
BasePath: cmd.String(flgPath),
|
||||
PEM: cmd.Bool(flgPEM),
|
||||
PFX: cmd.Bool(flgPFX),
|
||||
PFXFormat: cmd.String(flgPFXPass),
|
||||
PFXPassword: cmd.String(flgPFXFormat),
|
||||
Filename: cmd.String(flgFilename),
|
||||
}
|
||||
|
||||
writer, err := storage.NewCertificatesWriter(config)
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
return &CertificatesStorage{
|
||||
CertificatesWriter: writer,
|
||||
CertificatesReader: storage.NewCertificatesReader(basePath),
|
||||
}
|
||||
}
|
||||
|
||||
// newAccountsStorage Creates a new AccountsStorage.
|
||||
func newAccountsStorage(cmd *cli.Command) *storage.AccountsStorage {
|
||||
accountsStorage, err := storage.NewAccountsStorage(storage.AccountsStorageConfig{
|
||||
func newAccountsStorageConfig(cmd *cli.Command) storage.AccountsStorageConfig {
|
||||
return storage.AccountsStorageConfig{
|
||||
Email: cmd.String(flgEmail),
|
||||
BasePath: cmd.String(flgPath),
|
||||
Server: cmd.String(flgServer),
|
||||
UserAgent: getUserAgent(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
return accountsStorage
|
||||
}
|
||||
|
|
|
|||
89
docs/data/zz_cli_help.toml
generated
89
docs/data/zz_cli_help.toml
generated
|
|
@ -32,10 +32,9 @@ USAGE:
|
|||
lego run
|
||||
|
||||
OPTIONS:
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times.
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times or use comma as a separator.
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email string, -m string Email used for registration and recovery contact. [$LEGO_EMAIL]
|
||||
--csr string, -c string Certificate signing request filename, if an external CSR is to be used.
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac. [$LEGO_EAB]
|
||||
--kid string Key identifier from External CA. Used for External Account Binding. [$LEGO_EAB_KID]
|
||||
--hmac string MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. [$LEGO_EAB_HMAC]
|
||||
|
|
@ -47,7 +46,6 @@ OPTIONS:
|
|||
--cert.timeout int Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
|
||||
--overall-request-limit int ACME overall requests limit. (default: 18)
|
||||
--user-agent string Add to the user-agent sent to the CA to identify an application embedding lego-cli
|
||||
--filename string (deprecated) Filename of the generated certificate.
|
||||
--path string Directory to use for storing the data. [$LEGO_PATH]
|
||||
--pem Generate an additional .pem (base64) file by concatenating the .key and .crt files together.
|
||||
--pfx Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together. [$LEGO_PFX]
|
||||
|
|
@ -70,16 +68,17 @@ OPTIONS:
|
|||
--dns.propagation-wait duration By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead. (default: 0s)
|
||||
--dns.resolvers string [ --dns.resolvers string ] Set the resolvers to use for performing (recursive) CNAME resolving and apex domain determination. For DNS-01 challenge verification, the authoritative DNS server is queried directly. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
|
||||
--dns-timeout int Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries. (default: 10)
|
||||
--csr string, -c string Certificate signing request filename, if an external CSR is to be used.
|
||||
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate.
|
||||
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.
|
||||
--not-before time Set the notBefore field in the certificate (RFC3339 format)
|
||||
--not-after time Set the notAfter field in the certificate (RFC3339 format)
|
||||
--private-key string Path to private key (in PEM encoding) for the certificate. By default, the private key is generated.
|
||||
--preferred-chain string 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.
|
||||
--profile string If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.
|
||||
--always-deactivate-authorizations string Force the authorizations to be relinquished even if the certificate request was successful.
|
||||
--run-hook string Define a hook. The hook is executed when the certificates are effectively created.
|
||||
--run-hook string Define a hook. The hook is executed only when the certificates are effectively created/renewed.
|
||||
--run-hook-timeout duration Define the timeout for the hook execution. (default: 2m0s)
|
||||
--private-key string Path to private key (in PEM encoding) for the certificate. By default, the private key is generated.
|
||||
--help, -h show help
|
||||
"""
|
||||
|
||||
|
|
@ -93,10 +92,9 @@ USAGE:
|
|||
lego renew
|
||||
|
||||
OPTIONS:
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times.
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times or use comma as a separator.
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email string, -m string Email used for registration and recovery contact. [$LEGO_EMAIL]
|
||||
--csr string, -c string Certificate signing request filename, if an external CSR is to be used.
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac. [$LEGO_EAB]
|
||||
--kid string Key identifier from External CA. Used for External Account Binding. [$LEGO_EAB_KID]
|
||||
--hmac string MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. [$LEGO_EAB_HMAC]
|
||||
|
|
@ -108,7 +106,6 @@ OPTIONS:
|
|||
--cert.timeout int Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
|
||||
--overall-request-limit int ACME overall requests limit. (default: 18)
|
||||
--user-agent string Add to the user-agent sent to the CA to identify an application embedding lego-cli
|
||||
--filename string (deprecated) Filename of the generated certificate.
|
||||
--path string Directory to use for storing the data. [$LEGO_PATH]
|
||||
--pem Generate an additional .pem (base64) file by concatenating the .key and .crt files together.
|
||||
--pfx Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together. [$LEGO_PFX]
|
||||
|
|
@ -131,11 +128,7 @@ OPTIONS:
|
|||
--dns.propagation-wait duration By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead. (default: 0s)
|
||||
--dns.resolvers string [ --dns.resolvers string ] Set the resolvers to use for performing (recursive) CNAME resolving and apex domain determination. For DNS-01 challenge verification, the authoritative DNS server is queried directly. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
|
||||
--dns-timeout int Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries. (default: 10)
|
||||
--days int The number of days left on a certificate to renew it. (default: 30)
|
||||
--dynamic Compute dynamically, based on the lifetime of the certificate(s), when to renew: use 1/3rd of the lifetime left, or 1/2 of the lifetime for short-lived certificates). This supersedes --days and will be the default behavior in Lego v5.
|
||||
--ari-disable Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.
|
||||
--ari-wait-to-renew-duration duration The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint. (default: 0s)
|
||||
--reuse-key Used to indicate you want to reuse your current private key for the new certificate.
|
||||
--csr string, -c string Certificate signing request filename, if an external CSR is to be used.
|
||||
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate.
|
||||
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.
|
||||
--not-before time Set the notBefore field in the certificate (RFC3339 format)
|
||||
|
|
@ -143,8 +136,13 @@ OPTIONS:
|
|||
--preferred-chain string 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.
|
||||
--profile string If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.
|
||||
--always-deactivate-authorizations string Force the authorizations to be relinquished even if the certificate request was successful.
|
||||
--renew-hook string Define a hook. The hook is executed only when the certificates are effectively renewed.
|
||||
--renew-hook string Define a hook. The hook is executed only when the certificates are effectively created/renewed.
|
||||
--renew-hook-timeout duration Define the timeout for the hook execution. (default: 2m0s)
|
||||
--days int The number of days left on a certificate to renew it. (default: 30)
|
||||
--dynamic Compute dynamically, based on the lifetime of the certificate(s), when to renew: use 1/3rd of the lifetime left, or 1/2 of the lifetime for short-lived certificates). This supersedes --days and will be the default behavior in Lego v5.
|
||||
--ari-disable Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.
|
||||
--ari-wait-to-renew-duration duration The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint. (default: 0s)
|
||||
--reuse-key Used to indicate you want to reuse your current private key for the new certificate.
|
||||
--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.
|
||||
--force-cert-domains Check and ensure that the cert's domain list matches those passed in the domains argument.
|
||||
--help, -h show help
|
||||
|
|
@ -160,47 +158,28 @@ USAGE:
|
|||
lego revoke
|
||||
|
||||
OPTIONS:
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times.
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email string, -m string Email used for registration and recovery contact. [$LEGO_EMAIL]
|
||||
--csr string, -c string Certificate signing request filename, if an external CSR is to be used.
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac. [$LEGO_EAB]
|
||||
--kid string Key identifier from External CA. Used for External Account Binding. [$LEGO_EAB_KID]
|
||||
--hmac string MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. [$LEGO_EAB_HMAC]
|
||||
--server string, -s string CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. [$LEGO_SERVER]
|
||||
--disable-cn Disable the use of the common name in the CSR.
|
||||
--key-type string, -k string Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384. (default: "ec256")
|
||||
--http-timeout int Set the HTTP timeout value to a specific value in seconds. (default: 0)
|
||||
--tls-skip-verify Skip the TLS verification of the ACME server.
|
||||
--cert.timeout int Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
|
||||
--overall-request-limit int ACME overall requests limit. (default: 18)
|
||||
--user-agent string Add to the user-agent sent to the CA to identify an application embedding lego-cli
|
||||
--filename string (deprecated) Filename of the generated certificate.
|
||||
--path string Directory to use for storing the data. [$LEGO_PATH]
|
||||
--pem Generate an additional .pem (base64) file by concatenating the .key and .crt files together.
|
||||
--pfx Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together. [$LEGO_PFX]
|
||||
--pfx.pass string The password used to encrypt the .pfx (PCKS#12) file. (default: "changeit") [$LEGO_PFX_PASSWORD]
|
||||
--pfx.format string The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256. (default: "RC2") [$LEGO_PFX_FORMAT]
|
||||
--http Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--http.port string Set the port and interface to use for HTTP-01 based challenges to listen on. Supported: interface:port or :port. (default: ":80")
|
||||
--http.delay duration Delay between the starts of the HTTP server (use for HTTP-01 based challenges) and the validation of the challenge. (default: 0s)
|
||||
--http.proxy-header string Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy. (default: "Host")
|
||||
--http.webroot string Set the webroot folder to use for HTTP-01 based challenges to write directly to the .well-known/acme-challenge file. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge
|
||||
--http.memcached-host string [ --http.memcached-host string ] Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.
|
||||
--http.s3-bucket string Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.
|
||||
--tls Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--tls.port string Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port. (default: ":443")
|
||||
--tls.delay duration Delay between the start of the TLS listener (use for TLSALPN-01 based challenges) and the validation of the challenge. (default: 0s)
|
||||
--dns string Solve a DNS-01 challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
|
||||
--dns.disable-cp (deprecated) use dns.propagation-disable-ans instead.
|
||||
--dns.propagation-disable-ans By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers.
|
||||
--dns.propagation-rns By setting this flag to true, use all the recursive nameservers to check the propagation of the TXT record.
|
||||
--dns.propagation-wait duration By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead. (default: 0s)
|
||||
--dns.resolvers string [ --dns.resolvers string ] Set the resolvers to use for performing (recursive) CNAME resolving and apex domain determination. For DNS-01 challenge verification, the authoritative DNS server is queried directly. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
|
||||
--dns-timeout int Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries. (default: 10)
|
||||
--keep, -k Keep the certificates after the revocation instead of archiving them.
|
||||
--reason uint Identifies the reason for the certificate revocation. See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1. Valid values are: 0 (unspecified), 1 (keyCompromise), 2 (cACompromise), 3 (affiliationChanged), 4 (superseded), 5 (cessationOfOperation), 6 (certificateHold), 8 (removeFromCRL), 9 (privilegeWithdrawn), or 10 (aACompromise). (default: 0)
|
||||
--help, -h show help
|
||||
--domains string, -d string [ --domains string, -d string ] Add a domain to the process. Can be specified multiple times or use comma as a separator.
|
||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
||||
--email string, -m string Email used for registration and recovery contact. [$LEGO_EMAIL]
|
||||
--eab Use External Account Binding for account registration. Requires --kid and --hmac. [$LEGO_EAB]
|
||||
--kid string Key identifier from External CA. Used for External Account Binding. [$LEGO_EAB_KID]
|
||||
--hmac string MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. [$LEGO_EAB_HMAC]
|
||||
--server string, -s string CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. [$LEGO_SERVER]
|
||||
--disable-cn Disable the use of the common name in the CSR.
|
||||
--key-type string, -k string Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384. (default: "ec256")
|
||||
--http-timeout int Set the HTTP timeout value to a specific value in seconds. (default: 0)
|
||||
--tls-skip-verify Skip the TLS verification of the ACME server.
|
||||
--cert.timeout int Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
|
||||
--overall-request-limit int ACME overall requests limit. (default: 18)
|
||||
--user-agent string Add to the user-agent sent to the CA to identify an application embedding lego-cli
|
||||
--path string Directory to use for storing the data. [$LEGO_PATH]
|
||||
--pem Generate an additional .pem (base64) file by concatenating the .key and .crt files together.
|
||||
--pfx Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together. [$LEGO_PFX]
|
||||
--pfx.pass string The password used to encrypt the .pfx (PCKS#12) file. (default: "changeit") [$LEGO_PFX_PASSWORD]
|
||||
--pfx.format string The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256. (default: "RC2") [$LEGO_PFX_FORMAT]
|
||||
--keep, -k Keep the certificates after the revocation instead of archiving them.
|
||||
--reason uint Identifies the reason for the certificate revocation. See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1. Valid values are: 0 (unspecified), 1 (keyCompromise), 2 (cACompromise), 3 (affiliationChanged), 4 (superseded), 5 (cessationOfOperation), 6 (certificateHold), 8 (removeFromCRL), 9 (privilegeWithdrawn), or 10 (aACompromise). (default: 0)
|
||||
--help, -h show help
|
||||
"""
|
||||
|
||||
[[command]]
|
||||
|
|
|
|||
|
|
@ -174,9 +174,7 @@ func TestChallengeTLS_Run_Revoke(t *testing.T) {
|
|||
"--accept-tos",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", testDomain2,
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
"revoke")
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -204,8 +202,6 @@ func TestChallengeTLS_Run_Revoke_Non_ASCII(t *testing.T) {
|
|||
"--accept-tos",
|
||||
"-s", "https://localhost:14000/dir",
|
||||
"-d", testDomain4,
|
||||
"--tls",
|
||||
"--tls.port", ":5001",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue