diff --git a/cmd/cmd_list_accounts.go b/cmd/cmd_list_accounts.go index fd7cc154b..2addb1455 100644 --- a/cmd/cmd_list_accounts.go +++ b/cmd/cmd_list_accounts.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/log" "github.com/mattn/go-zglob" @@ -26,12 +27,12 @@ func createListAccounts() *cli.Command { Name: "accounts", Usage: "Display information about accounts.", Action: listAccounts, - Flags: createListFlags(), + Flags: flags.CreateListFlags(), } } func listAccounts(ctx context.Context, cmd *cli.Command) error { - if cmd.Bool(flgFormatJSON) { + if cmd.Bool(flags.FlgFormatJSON) { return listAccountsJSON(ctx, cmd) } diff --git a/cmd/cmd_list_certificates.go b/cmd/cmd_list_certificates.go index afc7add1b..2b7d6b458 100644 --- a/cmd/cmd_list_certificates.go +++ b/cmd/cmd_list_certificates.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-acme/lego/v5/certcrypto" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/mattn/go-zglob" "github.com/urfave/cli/v3" @@ -30,12 +31,12 @@ func createListCertificates() *cli.Command { Name: "certificates", Usage: "Display information about certificates.", Action: listCertificates, - Flags: createListFlags(), + Flags: flags.CreateListFlags(), } } func listCertificates(ctx context.Context, cmd *cli.Command) error { - if cmd.Bool(flgFormatJSON) { + if cmd.Bool(flags.FlgFormatJSON) { return listCertificatesJSON(ctx, cmd) } @@ -90,7 +91,7 @@ func listCertificatesJSON(_ context.Context, cmd *cli.Command) error { } func readCertificates(cmd *cli.Command) ([]ListCertificate, error) { - certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath)) + certsStorage := storage.NewCertificatesStorage(cmd.String(flags.FlgPath)) matches, err := zglob.Glob(filepath.Join(certsStorage.GetRootPath(), "**", "*.json")) if err != nil { diff --git a/cmd/cmd_migrate.go b/cmd/cmd_migrate.go index a52c184e0..9a99d91a3 100644 --- a/cmd/cmd_migrate.go +++ b/cmd/cmd_migrate.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/migrate" "github.com/go-acme/lego/v5/log" "github.com/urfave/cli/v3" @@ -21,18 +22,18 @@ func createMigrate() *cli.Command { return nil } - err := migrate.Accounts(cmd.String(flgPath)) + err := migrate.Accounts(cmd.String(flags.FlgPath)) if err != nil { return err } - if cmd.Bool(flgAccountOnly) { + if cmd.Bool(flags.FlgAccountOnly) { return nil } - return migrate.Certificates(cmd.String(flgPath)) + return migrate.Certificates(cmd.String(flags.FlgPath)) }, - Flags: createMigrateFlags(), + Flags: flags.CreateMigrateFlags(), } } @@ -40,8 +41,8 @@ func confirmMigration(cmd *cli.Command) bool { reader := bufio.NewReader(os.Stdin) log.Warnf(log.LazySprintf("The migration will not work if the certificates have been generated with the '--filename' flag."+ - " Use the flag '--%s' to only migrate accounts.", flgAccountOnly)) - log.Warnf(log.LazySprintf("Please create a backup of %q before the migration.", cmd.String(flgPath))) + " Use the flag '--%s' to only migrate accounts.", flags.FlgAccountOnly)) + log.Warnf(log.LazySprintf("Please create a backup of %q before the migration.", cmd.String(flags.FlgPath))) for { fmt.Println("Continue? Y/n") diff --git a/cmd/cmd_register.go b/cmd/cmd_register.go index 20657be54..b496b2f13 100644 --- a/cmd/cmd_register.go +++ b/cmd/cmd_register.go @@ -10,6 +10,7 @@ import ( "github.com/go-acme/lego/v5/acme" "github.com/go-acme/lego/v5/certcrypto" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/lego" "github.com/go-acme/lego/v5/log" @@ -34,12 +35,12 @@ func createRegister() *cli.Command { Name: "register", Usage: "Register an account.", Action: register, - Flags: createRegisterFlags(), + Flags: flags.CreateRegisterFlags(), } } func register(ctx context.Context, cmd *cli.Command) error { - keyType, err := certcrypto.GetKeyType(cmd.String(flgKeyType)) + keyType, err := certcrypto.GetKeyType(cmd.String(flags.FlgKeyType)) if err != nil { return fmt.Errorf("get the key type: %w", err) } @@ -49,7 +50,7 @@ func register(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("accounts storage initialization: %w", err) } - account, err := accountsStorage.Get(ctx, keyType, cmd.String(flgEmail), cmd.String(flgAccountID)) + account, err := accountsStorage.Get(ctx, keyType, cmd.String(flags.FlgEmail), cmd.String(flags.FlgAccountID)) if err != nil { return fmt.Errorf("set up account: %w", err) } @@ -84,12 +85,12 @@ func registerAccount(ctx context.Context, cmd *cli.Command, client *lego.Client) log.Fatal("You did not accept the TOS. Unable to proceed.") } - if cmd.Bool(flgEAB) { - kid := cmd.String(flgEABKID) - hmacEncoded := cmd.String(flgEABHMAC) + if cmd.Bool(flags.FlgEAB) { + kid := cmd.String(flags.FlgEABKID) + hmacEncoded := cmd.String(flags.FlgEABHMAC) if kid == "" || hmacEncoded == "" { - log.Fatal(fmt.Sprintf("Requires arguments --%s and --%s.", flgEABKID, flgEABHMAC)) + log.Fatal(fmt.Sprintf("Requires arguments --%s and --%s.", flags.FlgEABKID, flags.FlgEABHMAC)) } return client.Registration.RegisterWithExternalAccountBinding(ctx, registration.RegisterEABOptions{ @@ -97,8 +98,8 @@ func registerAccount(ctx context.Context, cmd *cli.Command, client *lego.Client) Kid: kid, HmacEncoded: hmacEncoded, }) - } else if zerossl.IsZeroSSL(cmd.String(flgServer)) { - return registration.RegisterWithZeroSSL(ctx, client.Registration, cmd.String(flgEmail)) + } else if zerossl.IsZeroSSL(cmd.String(flags.FlgServer)) { + return registration.RegisterWithZeroSSL(ctx, client.Registration, cmd.String(flags.FlgEmail)) } return client.Registration.Register(ctx, registration.RegisterOptions{TermsOfServiceAgreed: true}) @@ -112,7 +113,7 @@ func handleTOS(cmd *cli.Command, client *lego.Client) bool { } // Check for a global accept override - if cmd.Bool(flgAcceptTOS) { + if cmd.Bool(flags.FlgAcceptTOS) { return true } diff --git a/cmd/cmd_renew.go b/cmd/cmd_renew.go index c36e4be6a..3d60242a7 100644 --- a/cmd/cmd_renew.go +++ b/cmd/cmd_renew.go @@ -17,6 +17,7 @@ import ( "github.com/go-acme/lego/v5/acme/api" "github.com/go-acme/lego/v5/certcrypto" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/hook" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/lego" @@ -34,13 +35,13 @@ func createRenew() *cli.Command { Name: "renew", Usage: "Renew a certificate", Action: renew, - Before: renewFlagsValidation, - Flags: createRenewFlags(), + Before: flags.RenewFlagsValidation, + Flags: flags.CreateRenewFlags(), } } func renew(ctx context.Context, cmd *cli.Command) error { - keyType, err := certcrypto.GetKeyType(cmd.String(flgKeyType)) + keyType, err := certcrypto.GetKeyType(cmd.String(flags.FlgKeyType)) if err != nil { return fmt.Errorf("get the key type: %w", err) } @@ -50,7 +51,7 @@ func renew(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("accounts storage initialization: %w", err) } - account, err := accountsStorage.Get(ctx, keyType, cmd.String(flgEmail), cmd.String(flgAccountID)) + account, err := accountsStorage.Get(ctx, keyType, cmd.String(flags.FlgEmail), cmd.String(flags.FlgAccountID)) if err != nil { return fmt.Errorf("set up account: %w", err) } @@ -59,7 +60,7 @@ func renew(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("the account %s is not registered", account.GetID()) } - certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath)) + certsStorage := storage.NewCertificatesStorage(cmd.String(flags.FlgPath)) lazyClient := sync.OnceValues(func() (*lego.Client, error) { client, err := newClient(cmd, account, keyType) @@ -75,7 +76,7 @@ func renew(ctx context.Context, cmd *cli.Command) error { hookManager := newHookManager(cmd, certsStorage, account) // CSR - if cmd.IsSet(flgCSR) { + if cmd.IsSet(flags.FlgCSR) { return renewForCSR(ctx, cmd, lazyClient, certsStorage, hookManager) } @@ -84,9 +85,9 @@ func renew(ctx context.Context, cmd *cli.Command) error { } func renewForDomains(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, certsStorage *storage.CertificatesStorage, hookManager *hook.Manager) error { - domains := cmd.StringSlice(flgDomains) + domains := cmd.StringSlice(flags.FlgDomains) - certID := cmd.String(flgCertName) + certID := cmd.String(flags.FlgCertName) switch { case certID == "" && len(domains) > 0: @@ -129,11 +130,11 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, certDomains := certcrypto.ExtractDomains(cert) renewalDomains := slices.Clone(domains) - if !cmd.Bool(flgForceCertDomains) { + if !cmd.Bool(flags.FlgForceCertDomains) { renewalDomains = merge(certDomains, domains) } - if ariRenewalTime == nil && !cmd.Bool(flgRenewForce) && sameDomains(certDomains, renewalDomains) && + if ariRenewalTime == nil && !cmd.Bool(flags.FlgRenewForce) && sameDomains(certDomains, renewalDomains) && !isInRenewalPeriod(cert, certID, getFlagRenewDays(cmd), time.Now()) { return nil } @@ -160,7 +161,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, request := newObtainRequest(cmd, renewalDomains) - if cmd.Bool(flgReuseKey) { + if cmd.Bool(flags.FlgReuseKey) { request.PrivateKey, err = certsStorage.ReadPrivateKey(certID) if err != nil { return err @@ -189,12 +190,12 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, } func renewForCSR(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, certsStorage *storage.CertificatesStorage, hookManager *hook.Manager) error { - csr, err := storage.ReadCSRFile(cmd.String(flgCSR)) + csr, err := storage.ReadCSRFile(cmd.String(flags.FlgCSR)) if err != nil { - return fmt.Errorf("could not read CSR file %q: %w", cmd.String(flgCSR), err) + return fmt.Errorf("could not read CSR file %q: %w", cmd.String(flags.FlgCSR), err) } - certID := cmd.String(flgCertName) + certID := cmd.String(flags.FlgCertName) if certID == "" { certID, err = certcrypto.GetCSRMainDomain(csr) if err != nil { @@ -222,7 +223,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, cert return fmt.Errorf("CSR: %w", err) } - if ariRenewalTime == nil && !cmd.Bool(flgRenewForce) && sameDomainsCertificate(cert, csr) && + if ariRenewalTime == nil && !cmd.Bool(flags.FlgRenewForce) && sameDomainsCertificate(cert, csr) && !isInRenewalPeriod(cert, certID, getFlagRenewDays(cmd), time.Now()) { return nil } @@ -269,8 +270,8 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, cert } func getFlagRenewDays(cmd *cli.Command) int { - if cmd.IsSet(flgRenewDays) { - return cmd.Int(flgRenewDays) + if cmd.IsSet(flags.FlgRenewDays) { + return cmd.Int(flags.FlgRenewDays) } return noDays @@ -315,7 +316,7 @@ func getDueDate(x509Cert *x509.Certificate, days int, now time.Time) time.Time { } func getARIInfo(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, certID string, cert *x509.Certificate) (*time.Time, string, error) { - if cmd.Bool(flgARIDisable) { + if cmd.Bool(flags.FlgARIDisable) { return nil, "", nil } @@ -324,7 +325,7 @@ func getARIInfo(ctx context.Context, cmd *cli.Command, lazyClient lzSetUp, certI return nil, "", fmt.Errorf("set up client: %w", err) } - willingToSleep := cmd.Duration(flgARIWaitToRenewDuration) + willingToSleep := cmd.Duration(flags.FlgARIWaitToRenewDuration) ariRenewalTime := getARIRenewalTime(ctx, willingToSleep, cert, certID, client) if ariRenewalTime != nil { @@ -394,7 +395,7 @@ func getARIRenewalTime(ctx context.Context, willingToSleep time.Duration, cert * func randomSleep(cmd *cli.Command) { // https://github.com/go-acme/lego/issues/1656 // https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L435-L440 - if !isatty.IsTerminal(os.Stdout.Fd()) && !cmd.Bool(flgNoRandomSleep) { + if !isatty.IsTerminal(os.Stdout.Fd()) && !cmd.Bool(flags.FlgNoRandomSleep) { // https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L472 const jitter = 8 * time.Minute diff --git a/cmd/cmd_revoke.go b/cmd/cmd_revoke.go index 1cf7cab74..59bc13bf3 100644 --- a/cmd/cmd_revoke.go +++ b/cmd/cmd_revoke.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/go-acme/lego/v5/certcrypto" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/lego" "github.com/go-acme/lego/v5/log" @@ -16,12 +17,12 @@ func createRevoke() *cli.Command { Name: "revoke", Usage: "Revoke a certificate", Action: revoke, - Flags: createRevokeFlags(), + Flags: flags.CreateRevokeFlags(), } } func revoke(ctx context.Context, cmd *cli.Command) error { - keyType, err := certcrypto.GetKeyType(cmd.String(flgKeyType)) + keyType, err := certcrypto.GetKeyType(cmd.String(flags.FlgKeyType)) if err != nil { return fmt.Errorf("get the key type: %w", err) } @@ -31,7 +32,7 @@ func revoke(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("accounts storage initialization: %w", err) } - account, err := accountsStorage.Get(ctx, keyType, cmd.String(flgEmail), cmd.String(flgAccountID)) + account, err := accountsStorage.Get(ctx, keyType, cmd.String(flags.FlgEmail), cmd.String(flags.FlgAccountID)) if err != nil { return fmt.Errorf("set up account: %w", err) } @@ -45,12 +46,12 @@ func revoke(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("new client: %w", err) } - certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath)) + certsStorage := storage.NewCertificatesStorage(cmd.String(flags.FlgPath)) - reason := cmd.Uint(flgReason) - keep := cmd.Bool(flgKeep) + reason := cmd.Uint(flags.FlgReason) + keep := cmd.Bool(flags.FlgKeep) - for _, certID := range cmd.StringSlice(flgCertName) { + for _, certID := range cmd.StringSlice(flags.FlgCertName) { err := revokeCertificate(ctx, client, certsStorage, certID, reason, keep) if err != nil { return err diff --git a/cmd/cmd_root.go b/cmd/cmd_root.go index eae09b303..955c7b1a0 100644 --- a/cmd/cmd_root.go +++ b/cmd/cmd_root.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/log" "github.com/mattn/go-isatty" "github.com/urfave/cli/v3" @@ -25,7 +26,7 @@ func CreateRootCommand() *cli.Command { return ctx, nil }, - Flags: CreateLogFlags(), + Flags: flags.CreateLogFlags(), Commands: CreateCommands(), } } @@ -46,24 +47,26 @@ func CreateCommands() []*cli.Command { func setUpLogger(cmd *cli.Command) { var logger *slog.Logger - switch cmd.String(flgLogFormat) { + level := getLogLeveler(cmd.String(flags.FlgLogLevel)) + + switch cmd.String(flags.FlgLogFormat) { case "json": opts := &slog.HandlerOptions{ - Level: getLogLeveler(cmd.String(flgLogLevel)), + Level: level, } logger = slog.New(slog.NewJSONHandler(os.Stdout, opts)) case "text": opts := &slog.HandlerOptions{ - Level: getLogLeveler(cmd.String(flgLogLevel)), + Level: level, } logger = slog.New(slog.NewTextHandler(os.Stdout, opts)) default: opts := []slogor.OptionFn{ - slogor.SetLevel(getLogLeveler(cmd.String(flgLogLevel))), + slogor.SetLevel(level), slogor.SetTimeFormat(rfc3339NanoNatural), } diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index e57d9023b..7a102a0b5 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -7,6 +7,7 @@ import ( "github.com/go-acme/lego/v5/acme" "github.com/go-acme/lego/v5/certcrypto" "github.com/go-acme/lego/v5/certificate" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/hook" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/lego" @@ -17,14 +18,14 @@ func createRun() *cli.Command { return &cli.Command{ Name: "run", Usage: "Register an account, then create and install a certificate", - Before: runFlagsValidation, + Before: flags.RunFlagsValidation, Action: run, - Flags: createRunFlags(), + Flags: flags.CreateRunFlags(), } } func run(ctx context.Context, cmd *cli.Command) error { - keyType, err := certcrypto.GetKeyType(cmd.String(flgKeyType)) + keyType, err := certcrypto.GetKeyType(cmd.String(flags.FlgKeyType)) if err != nil { return fmt.Errorf("get the key type: %w", err) } @@ -34,12 +35,12 @@ func run(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("accounts storage initialization: %w", err) } - account, err := accountsStorage.Get(ctx, keyType, cmd.String(flgEmail), cmd.String(flgAccountID)) + account, err := accountsStorage.Get(ctx, keyType, cmd.String(flags.FlgEmail), cmd.String(flags.FlgAccountID)) if err != nil { return fmt.Errorf("set up account: %w", err) } - certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath)) + certsStorage := storage.NewCertificatesStorage(cmd.String(flags.FlgPath)) hookManager := newHookManager(cmd, certsStorage, account) @@ -73,7 +74,7 @@ func run(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("obtain certificate: %w", err) } - certID := cmd.String(flgCertName) + certID := cmd.String(flags.FlgCertName) if certID != "" { certRes.ID = certID } @@ -89,10 +90,10 @@ func run(ctx context.Context, cmd *cli.Command) error { } func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Client, hookManager *hook.Manager) (*certificate.Resource, error) { - domains := cmd.StringSlice(flgDomains) + domains := cmd.StringSlice(flags.FlgDomains) if len(domains) > 0 { - err := hookManager.Pre(ctx, cmd.String(flgCertName), domains) + err := hookManager.Pre(ctx, cmd.String(flags.FlgCertName), domains) if err != nil { return nil, err } @@ -103,10 +104,10 @@ func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Clien request := newObtainRequest(cmd, domains) // TODO(ldez): factorize? - if cmd.IsSet(flgPrivateKey) { + if cmd.IsSet(flags.FlgPrivateKey) { var err error - request.PrivateKey, err = storage.ReadPrivateKeyFile(cmd.String(flgPrivateKey)) + request.PrivateKey, err = storage.ReadPrivateKeyFile(cmd.String(flags.FlgPrivateKey)) if err != nil { return nil, fmt.Errorf("load private key: %w", err) } @@ -116,12 +117,12 @@ func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Clien } // read the CSR - csr, err := storage.ReadCSRFile(cmd.String(flgCSR)) + csr, err := storage.ReadCSRFile(cmd.String(flags.FlgCSR)) if err != nil { return nil, err } - err = hookManager.Pre(ctx, cmd.String(flgCertName), certcrypto.ExtractDomainsCSR(csr)) + err = hookManager.Pre(ctx, cmd.String(flags.FlgCertName), certcrypto.ExtractDomainsCSR(csr)) if err != nil { return nil, err } @@ -132,10 +133,10 @@ func obtainCertificate(ctx context.Context, cmd *cli.Command, client *lego.Clien request := newObtainForCSRRequest(cmd, csr) // TODO(ldez): factorize? - if cmd.IsSet(flgPrivateKey) { + if cmd.IsSet(flags.FlgPrivateKey) { var err error - request.PrivateKey, err = storage.ReadPrivateKeyFile(cmd.String(flgPrivateKey)) + request.PrivateKey, err = storage.ReadPrivateKeyFile(cmd.String(flags.FlgPrivateKey)) if err != nil { return nil, fmt.Errorf("load private key: %w", err) } diff --git a/cmd/flags.go b/cmd/internal/flags/flags.go similarity index 61% rename from cmd/flags.go rename to cmd/internal/flags/flags.go index cf152af92..93fec721a 100644 --- a/cmd/flags.go +++ b/cmd/internal/flags/flags.go @@ -1,4 +1,4 @@ -package cmd +package flags import ( "context" @@ -9,7 +9,6 @@ import ( "path/filepath" "strings" "time" - "unicode" "github.com/go-acme/lego/v5/acme" "github.com/go-acme/lego/v5/certificate" @@ -20,190 +19,185 @@ import ( "software.sslmate.com/src/go-pkcs12" ) -const ( - categoryHTTP01Challenge = "Flags related to the HTTP-01 challenge:" - categoryTLSALPN01Challenge = "Flags related to the TLS-ALPN-01 challenge:" - categoryDNS01Challenge = "Flags related to the DNS-01 challenge:" - categoryDNSPersist01Challenge = "Flags related to the DNS-PERSIST-01 challenge:" - categoryStorage = "Flags related to the storage:" - categoryHooks = "Flags related to hooks:" - categoryEAB = "Flags related to External Account Binding:" - categoryACMEClient = "Flags related to the ACME client:" - categoryAdvanced = "Flags related to advanced options:" - categoryARI = "Flags related to ACME Renewal Information (ARI) Extension:" - categoryLogs = "Flags related to logs:" -) +func CreateLogFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Category: categoryLogs, + Name: FlgLogLevel, + Sources: cli.EnvVars(toEnvName(FlgLogLevel)), + Usage: "Set the logging level. Supported values: 'debug', 'info', 'warn', 'error'.", + Value: "info", + }, + &cli.StringFlag{ + Category: categoryLogs, + Name: FlgLogFormat, + Sources: cli.EnvVars(toEnvName(FlgLogFormat)), + Usage: "Set the logging format. Supported values: 'colored', 'text', 'json'.", + Value: "colored", + }, + } +} -// Flag aliases (short-codes). -const ( - flgAliasAcceptTOS = "a" - flgAliasCertName = "c" - flgAliasDomains = "d" - flgAliasEmail = "m" - flgAliasIPv4Only = "4" - flgAliasIPv6Only = "6" - flgAliasKeyType = "k" - flgAliasServer = "s" -) +func CreateRunFlags() []cli.Flag { + flags := []cli.Flag{ + createDomainFlag(), + createCertNameFlag(), + } -// Flag names related to the account. -const ( - flgAcceptTOS = "accept-tos" - flgEmail = "email" - flgKeyType = "key-type" - flgAccountID = "account-id" - flgEAB = "eab" - flgEABKID = "eab.kid" - flgEABHMAC = "eab.hmac" -) + flags = append(flags, createAccountFlags()...) + flags = append(flags, createACMEClientFlags()...) + flags = append(flags, createStorageFlags()...) + flags = append(flags, createAcceptFlag()) + flags = append(flags, createChallengesFlags()...) + flags = append(flags, createObtainFlags()...) + flags = append(flags, createPreHookFlags()...) + flags = append(flags, createDeployHookFlags()...) + flags = append(flags, createPostHookFlags()...) -// Flag names related to Obtain certificates. -const ( - flgDomains = "domains" - flgCSR = "csr" - flgNoBundle = "no-bundle" - flgMustStaple = "must-staple" - flgNotBefore = "not-before" - flgNotAfter = "not-after" - flgPreferredChain = "preferred-chain" - flgProfile = "profile" - flgAlwaysDeactivateAuthorizations = "always-deactivate-authorizations" -) + flags = append(flags, + &cli.StringFlag{ + Category: categoryAdvanced, + Name: FlgPrivateKey, + Sources: cli.EnvVars(toEnvName(FlgPrivateKey)), + Usage: "Path to a private key (in PEM encoding) for the certificate. By default, a private key is generated.", + }, + ) -// Flag names related to the storage. -const ( - flgPath = "path" - flgPEM = "pem" - flgPFX = "pfx" - flgPFXPass = "pfx.pass" - flgPFXFormat = "pfx.format" -) + return flags +} -// Flag names related to the ACME client. -const ( - flgServer = "server" - flgEnableCommonName = "enable-cn" - flgHTTPTimeout = "http-timeout" - flgTLSSkipVerify = "tls-skip-verify" - flgOverallRequestLimit = "overall-request-limit" - flgUserAgent = "user-agent" -) +func CreateRenewFlags() []cli.Flag { + flags := []cli.Flag{ + createDomainFlag(), + createCertNameFlag(), + } -// Flag names related to certificates. -const ( - flgCertTimeout = "cert.timeout" - flgCertName = "cert.name" -) + flags = append(flags, createAccountFlags()...) + flags = append(flags, createACMEClientFlags()...) + flags = append(flags, createStorageFlags()...) + flags = append(flags, createChallengesFlags()...) + flags = append(flags, createObtainFlags()...) + flags = append(flags, createPreHookFlags()...) + flags = append(flags, createDeployHookFlags()...) + flags = append(flags, createPostHookFlags()...) -// Flag names related to the network stack. -const ( - flgIPv4Only = "ipv4only" - flgIPv6Only = "ipv6only" -) + flags = append(flags, + &cli.IntFlag{ + Name: FlgRenewDays, + Sources: cli.EnvVars(toEnvName(FlgRenewDays)), + Usage: "The number of days left on a certificate to renew it." + + "\n\tBy default, 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).", + }, + &cli.BoolFlag{ + Name: FlgRenewForce, + Sources: cli.EnvVars(toEnvName(FlgRenewForce)), + Usage: "Force the renewal of the certificate even if it is not due for renewal yet.", + }, + &cli.BoolFlag{ + Category: categoryARI, + Name: FlgARIDisable, + Sources: cli.EnvVars(toEnvName(FlgARIDisable)), + Usage: "Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.", + }, + &cli.DurationFlag{ + Category: categoryARI, + Name: FlgARIWaitToRenewDuration, + Sources: cli.EnvVars(toEnvName(FlgARIWaitToRenewDuration)), + Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.", + }, + &cli.BoolFlag{ + Category: categoryAdvanced, + Name: FlgReuseKey, + Sources: cli.EnvVars(toEnvName(FlgReuseKey)), + Usage: "Used to indicate you want to reuse your current private key for the new certificate.", + }, + &cli.BoolFlag{ + Category: categoryAdvanced, + Name: FlgNoRandomSleep, + Sources: cli.EnvVars(toEnvName(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{ + Category: categoryAdvanced, + Name: FlgForceCertDomains, + Sources: cli.EnvVars(toEnvName(FlgForceCertDomains)), + Usage: "Check and ensure that the cert's domain list matches those passed in the domains argument.", + }, + ) -// Flag names related to HTTP-01 challenge. -const ( - flgHTTP = "http" - flgHTTPPort = "http.port" - flgHTTPDelay = "http.delay" - flgHTTPProxyHeader = "http.proxy-header" - flgHTTPWebroot = "http.webroot" - flgHTTPMemcachedHost = "http.memcached-host" - flgHTTPS3Bucket = "http.s3-bucket" -) + return flags +} -// Flag names related to TLS-ALPN-01 challenge. -const ( - flgTLS = "tls" - flgTLSPort = "tls.port" - flgTLSDelay = "tls.delay" -) +func CreateRevokeFlags() []cli.Flag { + flags := []cli.Flag{ + createPathFlag(false), + createCertNamesFlag(), + &cli.BoolFlag{ + Name: FlgKeep, + Sources: cli.EnvVars(toEnvName(FlgKeep)), + Usage: "Keep the certificates after the revocation instead of archiving them.", + }, + &cli.UintFlag{ + Name: FlgReason, + Sources: cli.EnvVars(toEnvName(FlgReason)), + Usage: "Identifies the reason for the certificate revocation." + + " See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." + + "\n\tValid 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, + }, + } -// Flag names related to DNS-01 challenge. -const ( - flgDNS = "dns" - flgDNSPropagationWait = "dns.propagation.wait" - flgDNSPropagationDisableANS = "dns.propagation.disable-ans" - flgDNSPropagationDisableRNS = "dns.propagation.disable-rns" - flgDNSResolvers = "dns.resolvers" - flgDNSTimeout = "dns.timeout" -) + flags = append(flags, createAccountFlags()...) + flags = append(flags, createACMEClientFlags()...) -// Flag names related to the DNS-PERSIST-01 challenge. -const ( - flgDNSPersist = "dns-persist" - flgDNSPersistIssuerDomainName = "dns-persist.issuer-domain-name" - flgDNSPersistPersistUntil = "dns-persist.persist-until" - flgDNSPersistPropagationWait = "dns-persist.propagation.wait" - flgDNSPersistPropagationDisableANS = "dns-persist.propagation.disable-ans" - flgDNSPersistPropagationDisableRNS = "dns-persist.propagation.disable-rns" - flgDNSPersistResolvers = "dns-persist.resolvers" - flgDNSPersistTimeout = "dns-persist.timeout" -) + return flags +} -// Flags names related to hooks. -const ( - flgPreHook = "pre-hook" - flgPreHookTimeout = "pre-hook-timeout" - flgDeployHook = "deploy-hook" - flgDeployHookTimeout = "deploy-hook-timeout" - flgPostHook = "post-hook" - flgPostHookTimeout = "post-hook-timeout" -) +func CreateRegisterFlags() []cli.Flag { + flags := []cli.Flag{ + createPathFlag(true), + createAcceptFlag(), + } -// Flag names related to logs. -const ( - flgLogLevel = "log.level" - flgLogFormat = "log.format" -) + flags = append(flags, createACMEClientFlags()...) + flags = append(flags, createAccountFlags()...) -// Flag names related to the specific run command. -const ( - flgPrivateKey = "private-key" -) + return flags +} -// Flag names related to the specific renew command. -const ( - flgRenewDays = "renew-days" - flgRenewForce = "renew-force" - flgARIDisable = "ari-disable" - flgARIWaitToRenewDuration = "ari-wait-to-renew-duration" - flgReuseKey = "reuse-key" - flgNoRandomSleep = "no-random-sleep" - flgForceCertDomains = "force-cert-domains" -) +func CreateListFlags() []cli.Flag { + return []cli.Flag{ + createPathFlag(false), + &cli.BoolFlag{ + Name: FlgFormatJSON, + Usage: "Format the output as JSON.", + }, + } +} -// Flag names related to the specific revoke command. -const ( - flgKeep = "keep" - flgReason = "reason" -) - -// Flag names related to the list commands. -const ( - flgFormatJSON = "json" -) - -// Flag names related to the migrate command. -const ( - flgAccountOnly = "account-only" -) - -func toEnvName(flg string) string { - fields := strings.FieldsFunc(flg, func(r rune) bool { - return !unicode.IsLetter(r) && !unicode.IsNumber(r) - }) - - return "LEGO_" + strings.ToUpper(strings.Join(fields, "_")) +func CreateMigrateFlags() []cli.Flag { + return []cli.Flag{ + createPathFlag(false), + &cli.BoolFlag{ + Name: FlgAccountOnly, + Sources: cli.EnvVars(toEnvName(FlgAccountOnly)), + Usage: "Only migrate accounts.", + Value: false, + }, + } } func createACMEClientFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ // NOTE(ldez): if Required is true, then the default value is not display in the help. - Name: flgServer, + Name: FlgServer, Aliases: []string{flgAliasServer}, - Sources: cli.EnvVars(toEnvName(flgServer)), + Sources: cli.EnvVars(toEnvName(FlgServer)), Usage: fmt.Sprintf("CA (ACME server). It can be either a URL or a shortcode."+ "\n\t(available shortcodes: %s)", strings.Join(lego.GetAllCodes(), ", ")), Value: lego.DirectoryURLLetsEncrypt, @@ -215,52 +209,52 @@ func createACMEClientFlags() []cli.Flag { directoryURL = s } - return cmd.Set(flgServer, directoryURL) + return cmd.Set(FlgServer, directoryURL) }, }, &cli.BoolFlag{ Category: categoryAdvanced, - Name: flgEnableCommonName, - Sources: cli.EnvVars(toEnvName(flgEnableCommonName)), + Name: FlgEnableCommonName, + Sources: cli.EnvVars(toEnvName(FlgEnableCommonName)), Usage: "Enable the use of the common name. (Not recommended)", }, &cli.StringFlag{ - Name: flgKeyType, + Name: FlgKeyType, Aliases: []string{flgAliasKeyType}, - Sources: cli.EnvVars(toEnvName(flgKeyType)), + Sources: cli.EnvVars(toEnvName(FlgKeyType)), Value: "ec256", Usage: "Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384.", }, &cli.IntFlag{ Category: categoryACMEClient, - Name: flgHTTPTimeout, - Sources: cli.EnvVars(toEnvName(flgHTTPTimeout)), + Name: FlgHTTPTimeout, + Sources: cli.EnvVars(toEnvName(FlgHTTPTimeout)), Usage: "Set the HTTP timeout value to a specific value in seconds.", }, &cli.BoolFlag{ Category: categoryACMEClient, - Name: flgTLSSkipVerify, - Sources: cli.EnvVars(toEnvName(flgTLSSkipVerify)), + Name: FlgTLSSkipVerify, + Sources: cli.EnvVars(toEnvName(FlgTLSSkipVerify)), Usage: "Skip the TLS verification of the ACME server.", }, &cli.IntFlag{ Category: categoryAdvanced, - Name: flgCertTimeout, - Sources: cli.EnvVars(toEnvName(flgCertTimeout)), + Name: FlgCertTimeout, + Sources: cli.EnvVars(toEnvName(FlgCertTimeout)), Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.", Value: 30, }, &cli.IntFlag{ Category: categoryACMEClient, - Name: flgOverallRequestLimit, - Sources: cli.EnvVars(toEnvName(flgOverallRequestLimit)), + Name: FlgOverallRequestLimit, + Sources: cli.EnvVars(toEnvName(FlgOverallRequestLimit)), Usage: "ACME overall requests limit.", Value: certificate.DefaultOverallRequestLimit, }, &cli.StringFlag{ Category: categoryACMEClient, - Name: flgUserAgent, - Sources: cli.EnvVars(toEnvName(flgUserAgent)), + Name: FlgUserAgent, + Sources: cli.EnvVars(toEnvName(FlgUserAgent)), Usage: "Add to the user-agent sent to the CA to identify an application embedding lego-cli", }, } @@ -282,16 +276,16 @@ func createNetworkStackFlags() []cli.Flag { return []cli.Flag{ &cli.BoolFlag{ Category: categoryAdvanced, - Name: flgIPv4Only, + Name: FlgIPv4Only, Aliases: []string{flgAliasIPv4Only}, - Sources: cli.EnvVars(toEnvName(flgIPv4Only)), + Sources: cli.EnvVars(toEnvName(FlgIPv4Only)), Usage: "Use IPv4 only.", }, &cli.BoolFlag{ Category: categoryAdvanced, - Name: flgIPv6Only, + Name: FlgIPv6Only, Aliases: []string{flgAliasIPv6Only}, - Sources: cli.EnvVars(toEnvName(flgIPv6Only)), + Sources: cli.EnvVars(toEnvName(FlgIPv6Only)), Usage: "Use IPv6 only.", }, } @@ -301,48 +295,48 @@ func createHTTPChallengeFlags() []cli.Flag { return []cli.Flag{ &cli.BoolFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTP, - Sources: cli.EnvVars(toEnvName(flgHTTP)), + Name: FlgHTTP, + Sources: cli.EnvVars(toEnvName(FlgHTTP)), Usage: "Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges.", }, &cli.StringFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPPort, - Sources: cli.EnvVars(toEnvName(flgHTTPPort)), + Name: FlgHTTPPort, + Sources: cli.EnvVars(toEnvName(FlgHTTPPort)), Usage: "Set the port and interface to use for HTTP-01 based challenges to listen on. Supported: interface:port or :port.", Value: ":80", }, &cli.DurationFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPDelay, - Sources: cli.EnvVars(toEnvName(flgHTTPDelay)), + Name: FlgHTTPDelay, + Sources: cli.EnvVars(toEnvName(FlgHTTPDelay)), Usage: "Delay between the starts of the HTTP server (use for HTTP-01 based challenges) and the validation of the challenge.", Value: 0, }, &cli.StringFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPProxyHeader, - Sources: cli.EnvVars(toEnvName(flgHTTPProxyHeader)), + Name: FlgHTTPProxyHeader, + Sources: cli.EnvVars(toEnvName(FlgHTTPProxyHeader)), Usage: "Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy.", Value: "Host", }, &cli.StringFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPWebroot, - Sources: cli.EnvVars(toEnvName(flgHTTPWebroot)), + Name: FlgHTTPWebroot, + Sources: cli.EnvVars(toEnvName(FlgHTTPWebroot)), Usage: "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", }, &cli.StringSliceFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPMemcachedHost, - Sources: cli.EnvVars(toEnvName(flgHTTPMemcachedHost)), + Name: FlgHTTPMemcachedHost, + Sources: cli.EnvVars(toEnvName(FlgHTTPMemcachedHost)), Usage: "Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.", }, &cli.StringFlag{ Category: categoryHTTP01Challenge, - Name: flgHTTPS3Bucket, - Sources: cli.EnvVars(toEnvName(flgHTTPS3Bucket)), + Name: FlgHTTPS3Bucket, + Sources: cli.EnvVars(toEnvName(FlgHTTPS3Bucket)), Usage: "Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.", }, } @@ -352,21 +346,21 @@ func createTLSChallengeFlags() []cli.Flag { return []cli.Flag{ &cli.BoolFlag{ Category: categoryTLSALPN01Challenge, - Name: flgTLS, - Sources: cli.EnvVars(toEnvName(flgTLS)), + Name: FlgTLS, + Sources: cli.EnvVars(toEnvName(FlgTLS)), Usage: "Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.", }, &cli.StringFlag{ Category: categoryTLSALPN01Challenge, - Name: flgTLSPort, - Sources: cli.EnvVars(toEnvName(flgTLSPort)), + Name: FlgTLSPort, + Sources: cli.EnvVars(toEnvName(FlgTLSPort)), Usage: "Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port.", Value: ":443", }, &cli.DurationFlag{ Category: categoryTLSALPN01Challenge, - Name: flgTLSDelay, - Sources: cli.EnvVars(toEnvName(flgTLSDelay)), + Name: FlgTLSDelay, + Sources: cli.EnvVars(toEnvName(FlgTLSDelay)), Usage: "Delay between the start of the TLS listener (use for TLSALPN-01 based challenges) and the validation of the challenge.", Value: 0, }, @@ -377,26 +371,26 @@ func createDNSChallengeFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Category: categoryDNS01Challenge, - Name: flgDNS, - Sources: cli.EnvVars(toEnvName(flgDNS)), + Name: FlgDNS, + Sources: cli.EnvVars(toEnvName(FlgDNS)), Usage: "Solve a DNS-01 challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.", }, &cli.BoolFlag{ Category: categoryDNS01Challenge, - Name: flgDNSPropagationDisableANS, - Sources: cli.EnvVars(toEnvName(flgDNSPropagationDisableANS)), + Name: FlgDNSPropagationDisableANS, + Sources: cli.EnvVars(toEnvName(FlgDNSPropagationDisableANS)), Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers.", }, &cli.BoolFlag{ Category: categoryDNS01Challenge, - Name: flgDNSPropagationDisableRNS, - Sources: cli.EnvVars(toEnvName(flgDNSPropagationDisableRNS)), + Name: FlgDNSPropagationDisableRNS, + Sources: cli.EnvVars(toEnvName(FlgDNSPropagationDisableRNS)), Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all recursive name servers (aka resolvers).", }, &cli.DurationFlag{ Category: categoryDNS01Challenge, - Name: flgDNSPropagationWait, - Sources: cli.EnvVars(toEnvName(flgDNSPropagationWait)), + Name: FlgDNSPropagationWait, + Sources: cli.EnvVars(toEnvName(FlgDNSPropagationWait)), Usage: "By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead.", Validator: func(d time.Duration) error { if d < 0 { @@ -408,8 +402,8 @@ func createDNSChallengeFlags() []cli.Flag { }, &cli.StringSliceFlag{ Category: categoryDNS01Challenge, - Name: flgDNSResolvers, - Sources: cli.EnvVars(toEnvName(flgDNSResolvers)), + Name: FlgDNSResolvers, + Sources: cli.EnvVars(toEnvName(FlgDNSResolvers)), Usage: "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." + @@ -417,8 +411,8 @@ func createDNSChallengeFlags() []cli.Flag { }, &cli.IntFlag{ Category: categoryDNS01Challenge, - Name: flgDNSTimeout, - Sources: cli.EnvVars(toEnvName(flgDNSTimeout)), + Name: FlgDNSTimeout, + Sources: cli.EnvVars(toEnvName(FlgDNSTimeout)), Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries.", Value: 10, }, @@ -429,29 +423,29 @@ func createDNSPersistChallengeFlags() []cli.Flag { return []cli.Flag{ &cli.BoolFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersist, - Sources: cli.EnvVars(toEnvName(flgDNSPersist)), + Name: FlgDNSPersist, + Sources: cli.EnvVars(toEnvName(FlgDNSPersist)), Usage: "Use the DNS-PERSIST-01 challenge to solve challenges. Manual verification only. Can be mixed with other types of challenges.", }, &cli.StringFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistIssuerDomainName, - Sources: cli.EnvVars(toEnvName(flgDNSPersistIssuerDomainName)), + Name: FlgDNSPersistIssuerDomainName, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistIssuerDomainName)), Usage: "Override the issuer-domain-name to use for DNS-PERSIST-01 when multiple are offered. Must be offered by the challenge.", }, &cli.TimestampFlag{ - Name: flgDNSPersistPersistUntil, + Name: FlgDNSPersistPersistUntil, Category: categoryDNSPersist01Challenge, Usage: "Set the optional persistUntil for DNS-PERSIST-01 records as an RFC3339 timestamp (for example 2026-03-01T00:00:00Z).", - Sources: cli.EnvVars(toEnvName(flgDNSPersistPersistUntil)), + Sources: cli.EnvVars(toEnvName(FlgDNSPersistPersistUntil)), Config: cli.TimestampConfig{ Layouts: []string{time.RFC3339}, }, }, &cli.DurationFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistPropagationWait, - Sources: cli.EnvVars(toEnvName(flgDNSPersistPropagationWait)), + Name: FlgDNSPersistPropagationWait, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistPropagationWait)), Usage: "By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead.", Validator: func(d time.Duration) error { if d < 0 { @@ -463,28 +457,28 @@ func createDNSPersistChallengeFlags() []cli.Flag { }, &cli.BoolFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistPropagationDisableANS, - Sources: cli.EnvVars(toEnvName(flgDNSPersistPropagationDisableANS)), + Name: FlgDNSPersistPropagationDisableANS, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistPropagationDisableANS)), Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers.", }, &cli.BoolFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistPropagationDisableRNS, - Sources: cli.EnvVars(toEnvName(flgDNSPersistPropagationDisableRNS)), + Name: FlgDNSPersistPropagationDisableRNS, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistPropagationDisableRNS)), Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all recursive name servers (aka resolvers).", }, &cli.StringSliceFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistResolvers, - Sources: cli.EnvVars(toEnvName(flgDNSPersistResolvers)), + Name: FlgDNSPersistResolvers, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistResolvers)), Usage: "Set the resolvers to use for DNS-PERSIST-01 TXT lookups." + " Supported: host:port." + " The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.", }, &cli.IntFlag{ Category: categoryDNSPersist01Challenge, - Name: flgDNSPersistTimeout, - Sources: cli.EnvVars(toEnvName(flgDNSPersistTimeout)), + Name: FlgDNSPersistTimeout, + Sources: cli.EnvVars(toEnvName(FlgDNSPersistTimeout)), Usage: "Set the DNS timeout value to a specific value in seconds. Used for DNS-PERSIST-01 lookups.", }, } @@ -495,27 +489,27 @@ func createStorageFlags() []cli.Flag { createPathFlag(true), &cli.BoolFlag{ Category: categoryStorage, - Name: flgPEM, - Sources: cli.EnvVars(toEnvName(flgPEM)), + Name: FlgPEM, + Sources: cli.EnvVars(toEnvName(FlgPEM)), Usage: "Generate an additional .pem (base64) file by concatenating the .key and .crt files together.", }, &cli.BoolFlag{ Category: categoryStorage, - Name: flgPFX, - Sources: cli.EnvVars(toEnvName(flgPFX)), + Name: FlgPFX, + Sources: cli.EnvVars(toEnvName(FlgPFX)), Usage: "Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together.", }, &cli.StringFlag{ Category: categoryStorage, - Name: flgPFXPass, - Sources: cli.EnvVars(toEnvName(flgPFXPass)), + Name: FlgPFXPass, + Sources: cli.EnvVars(toEnvName(FlgPFXPass)), Usage: "The password used to encrypt the .pfx (PCKS#12) file.", Value: pkcs12.DefaultPassword, }, &cli.StringFlag{ Category: categoryStorage, - Name: flgPFXFormat, - Sources: cli.EnvVars(toEnvName(flgPFXFormat)), + Name: FlgPFXFormat, + Sources: cli.EnvVars(toEnvName(FlgPFXFormat)), Usage: "The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256.", Value: "RC2", }, @@ -525,33 +519,33 @@ func createStorageFlags() []cli.Flag { func createAccountFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ - Name: flgEmail, + Name: FlgEmail, Aliases: []string{flgAliasEmail}, - Sources: cli.EnvVars(toEnvName(flgEmail)), + Sources: cli.EnvVars(toEnvName(FlgEmail)), Usage: "Email used for registration and recovery contact.", }, &cli.StringFlag{ Category: categoryStorage, - Name: flgAccountID, - Sources: cli.EnvVars(toEnvName(flgAccountID)), + Name: FlgAccountID, + Sources: cli.EnvVars(toEnvName(FlgAccountID)), Usage: "Account identifier (The email is used if there is account ID is undefined).", }, &cli.BoolFlag{ Category: categoryEAB, - Name: flgEAB, - Sources: cli.EnvVars(toEnvName(flgEAB)), - Usage: fmt.Sprintf("Use External Account Binding for account registration. Requires %s and %s.", flgEABKID, flgEABHMAC), + Name: FlgEAB, + Sources: cli.EnvVars(toEnvName(FlgEAB)), + Usage: fmt.Sprintf("Use External Account Binding for account registration. Requires %s and %s.", FlgEABKID, FlgEABHMAC), }, &cli.StringFlag{ Category: categoryEAB, - Name: flgEABKID, - Sources: cli.EnvVars(toEnvName(flgEABKID)), + Name: FlgEABKID, + Sources: cli.EnvVars(toEnvName(FlgEABKID)), Usage: "Key identifier for External Account Binding.", }, &cli.StringFlag{ Category: categoryEAB, - Name: flgEABHMAC, - Sources: cli.EnvVars(toEnvName(flgEABHMAC)), + Name: FlgEABHMAC, + Sources: cli.EnvVars(toEnvName(FlgEABHMAC)), Usage: "MAC key for External Account Binding. Should be in Base64 URL Encoding without padding format.", }, } @@ -561,27 +555,27 @@ func createObtainFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Category: categoryAdvanced, - Name: flgCSR, - Sources: cli.EnvVars(toEnvName(flgCSR)), + Name: FlgCSR, + Sources: cli.EnvVars(toEnvName(FlgCSR)), Usage: "Certificate signing request filename, if an external CSR is to be used.", }, &cli.BoolFlag{ Category: categoryAdvanced, - Name: flgNoBundle, - Sources: cli.EnvVars(toEnvName(flgNoBundle)), + Name: FlgNoBundle, + Sources: cli.EnvVars(toEnvName(FlgNoBundle)), Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", }, &cli.BoolFlag{ Category: categoryAdvanced, - Name: flgMustStaple, - Sources: cli.EnvVars(toEnvName(flgMustStaple)), + Name: FlgMustStaple, + Sources: cli.EnvVars(toEnvName(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{ Category: categoryAdvanced, - Name: flgNotBefore, - Sources: cli.EnvVars(toEnvName(flgNotBefore)), + Name: FlgNotBefore, + Sources: cli.EnvVars(toEnvName(FlgNotBefore)), Usage: "Set the notBefore field in the certificate (RFC3339 format)", Config: cli.TimestampConfig{ Layouts: []string{time.RFC3339}, @@ -589,8 +583,8 @@ func createObtainFlags() []cli.Flag { }, &cli.TimestampFlag{ Category: categoryAdvanced, - Name: flgNotAfter, - Sources: cli.EnvVars(toEnvName(flgNotAfter)), + Name: FlgNotAfter, + Sources: cli.EnvVars(toEnvName(FlgNotAfter)), Usage: "Set the notAfter field in the certificate (RFC3339 format)", Config: cli.TimestampConfig{ Layouts: []string{time.RFC3339}, @@ -598,21 +592,21 @@ func createObtainFlags() []cli.Flag { }, &cli.StringFlag{ Category: categoryAdvanced, - Name: flgPreferredChain, - Sources: cli.EnvVars(toEnvName(flgPreferredChain)), + Name: FlgPreferredChain, + Sources: cli.EnvVars(toEnvName(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{ Category: categoryAdvanced, - Name: flgProfile, - Sources: cli.EnvVars(toEnvName(flgProfile)), + Name: FlgProfile, + Sources: cli.EnvVars(toEnvName(FlgProfile)), Usage: "If the CA offers multiple certificate profiles (draft-ietf-acme-profiles), choose this one.", }, &cli.StringFlag{ Category: categoryAdvanced, - Name: flgAlwaysDeactivateAuthorizations, - Sources: cli.EnvVars(toEnvName(flgAlwaysDeactivateAuthorizations)), + Name: FlgAlwaysDeactivateAuthorizations, + Sources: cli.EnvVars(toEnvName(FlgAlwaysDeactivateAuthorizations)), Usage: "Force the authorizations to be relinquished even if the certificate request was successful.", }, } @@ -622,14 +616,14 @@ func createPreHookFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Category: categoryHooks, - Name: flgPreHook, - Sources: cli.EnvVars(toEnvName(flgPreHook)), + Name: FlgPreHook, + Sources: cli.EnvVars(toEnvName(FlgPreHook)), Usage: "Define a pre-hook. This hook runs, before the creation or the renewal, in cases where a certificate will be effectively created/renewed.", }, &cli.DurationFlag{ Category: categoryHooks, - Name: flgPreHookTimeout, - Sources: cli.EnvVars(toEnvName(flgPreHookTimeout)), + Name: FlgPreHookTimeout, + Sources: cli.EnvVars(toEnvName(FlgPreHookTimeout)), Usage: "Define the timeout for the pre-hook execution.", Value: 2 * time.Minute, }, @@ -640,14 +634,14 @@ func createDeployHookFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Category: categoryHooks, - Name: flgDeployHook, - Sources: cli.EnvVars(toEnvName(flgDeployHook)), + Name: FlgDeployHook, + Sources: cli.EnvVars(toEnvName(FlgDeployHook)), Usage: "Define a hook. The hook runs, after the creation or the renewal, in cases where a certificate is successfully created/renewed.", }, &cli.DurationFlag{ Category: categoryHooks, - Name: flgDeployHookTimeout, - Sources: cli.EnvVars(toEnvName(flgDeployHookTimeout)), + Name: FlgDeployHookTimeout, + Sources: cli.EnvVars(toEnvName(FlgDeployHookTimeout)), Usage: "Define the timeout for the hook execution.", Value: 2 * time.Minute, }, @@ -658,206 +652,34 @@ func createPostHookFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Category: categoryHooks, - Name: flgPostHook, - Sources: cli.EnvVars(toEnvName(flgPostHook)), + Name: FlgPostHook, + Sources: cli.EnvVars(toEnvName(FlgPostHook)), Usage: "Define a post-hook. This hook runs, after the creation or the renewal, in cases where a certificate is created/renewed, regardless of whether any errors occurred.", }, &cli.DurationFlag{ Category: categoryHooks, - Name: flgPostHookTimeout, - Sources: cli.EnvVars(toEnvName(flgPostHookTimeout)), + Name: FlgPostHookTimeout, + Sources: cli.EnvVars(toEnvName(FlgPostHookTimeout)), Usage: "Define the timeout for the post-hook execution.", Value: 2 * time.Minute, }, } } -func CreateLogFlags() []cli.Flag { - return []cli.Flag{ - &cli.StringFlag{ - Category: categoryLogs, - Name: flgLogLevel, - Sources: cli.EnvVars(toEnvName(flgLogLevel)), - Usage: "Set the logging level. Supported values: 'debug', 'info', 'warn', 'error'.", - Value: "info", - }, - &cli.StringFlag{ - Category: categoryLogs, - Name: flgLogFormat, - Sources: cli.EnvVars(toEnvName(flgLogFormat)), - Usage: "Set the logging format. Supported values: 'colored', 'text', 'json'.", - Value: "colored", - }, - } -} - -func createRunFlags() []cli.Flag { - flags := []cli.Flag{ - createDomainFlag(), - createCertNameFlag(), - } - - flags = append(flags, createAccountFlags()...) - flags = append(flags, createACMEClientFlags()...) - flags = append(flags, createStorageFlags()...) - flags = append(flags, createAcceptFlag()) - flags = append(flags, createChallengesFlags()...) - flags = append(flags, createObtainFlags()...) - flags = append(flags, createPreHookFlags()...) - flags = append(flags, createDeployHookFlags()...) - flags = append(flags, createPostHookFlags()...) - - flags = append(flags, - &cli.StringFlag{ - Category: categoryAdvanced, - Name: flgPrivateKey, - Sources: cli.EnvVars(toEnvName(flgPrivateKey)), - Usage: "Path to a private key (in PEM encoding) for the certificate. By default, a private key is generated.", - }, - ) - - return flags -} - -func createRenewFlags() []cli.Flag { - flags := []cli.Flag{ - createDomainFlag(), - createCertNameFlag(), - } - - flags = append(flags, createAccountFlags()...) - flags = append(flags, createACMEClientFlags()...) - flags = append(flags, createStorageFlags()...) - flags = append(flags, createChallengesFlags()...) - flags = append(flags, createObtainFlags()...) - flags = append(flags, createPreHookFlags()...) - flags = append(flags, createDeployHookFlags()...) - flags = append(flags, createPostHookFlags()...) - - flags = append(flags, - &cli.IntFlag{ - Name: flgRenewDays, - Sources: cli.EnvVars(toEnvName(flgRenewDays)), - Usage: "The number of days left on a certificate to renew it." + - "\n\tBy default, 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).", - }, - &cli.BoolFlag{ - Name: flgRenewForce, - Sources: cli.EnvVars(toEnvName(flgRenewForce)), - Usage: "Force the renewal of the certificate even if it is not due for renewal yet.", - }, - &cli.BoolFlag{ - Category: categoryARI, - Name: flgARIDisable, - Sources: cli.EnvVars(toEnvName(flgARIDisable)), - Usage: "Do not use the renewalInfo endpoint (RFC9773) to check if a certificate should be renewed.", - }, - &cli.DurationFlag{ - Category: categoryARI, - Name: flgARIWaitToRenewDuration, - Sources: cli.EnvVars(toEnvName(flgARIWaitToRenewDuration)), - Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.", - }, - &cli.BoolFlag{ - Category: categoryAdvanced, - Name: flgReuseKey, - Sources: cli.EnvVars(toEnvName(flgReuseKey)), - Usage: "Used to indicate you want to reuse your current private key for the new certificate.", - }, - &cli.BoolFlag{ - Category: categoryAdvanced, - Name: flgNoRandomSleep, - Sources: cli.EnvVars(toEnvName(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{ - Category: categoryAdvanced, - Name: flgForceCertDomains, - Sources: cli.EnvVars(toEnvName(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 := []cli.Flag{ - createPathFlag(false), - createCertNamesFlag(), - &cli.BoolFlag{ - Name: flgKeep, - Sources: cli.EnvVars(toEnvName(flgKeep)), - Usage: "Keep the certificates after the revocation instead of archiving them.", - }, - &cli.UintFlag{ - Name: flgReason, - Sources: cli.EnvVars(toEnvName(flgReason)), - Usage: "Identifies the reason for the certificate revocation." + - " See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." + - "\n\tValid 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, - }, - } - - flags = append(flags, createAccountFlags()...) - flags = append(flags, createACMEClientFlags()...) - - return flags -} - -func createRegisterFlags() []cli.Flag { - flags := []cli.Flag{ - createPathFlag(true), - createAcceptFlag(), - } - - flags = append(flags, createACMEClientFlags()...) - flags = append(flags, createAccountFlags()...) - - return flags -} - -func createListFlags() []cli.Flag { - return []cli.Flag{ - createPathFlag(false), - &cli.BoolFlag{ - Name: flgFormatJSON, - Usage: "Format the output as JSON.", - }, - } -} - -func createMigrateFlags() []cli.Flag { - return []cli.Flag{ - createPathFlag(false), - &cli.BoolFlag{ - Name: flgAccountOnly, - Sources: cli.EnvVars(toEnvName(flgAccountOnly)), - Usage: "Only migrate accounts.", - Value: false, - }, - } -} - func createAcceptFlag() cli.Flag { return &cli.BoolFlag{ - Name: flgAcceptTOS, + Name: FlgAcceptTOS, Aliases: []string{flgAliasAcceptTOS}, - Sources: cli.EnvVars(toEnvName(flgAcceptTOS)), - Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.", + Sources: cli.EnvVars(toEnvName(FlgAcceptTOS)), + Usage: "By setting this flag to true you indicate that you accept the current CA terms of service.", } } func createDomainFlag() cli.Flag { return &cli.StringSliceFlag{ - Name: flgDomains, + Name: FlgDomains, Aliases: []string{flgAliasDomains}, - Sources: cli.EnvVars(toEnvName(flgDomains)), + Sources: cli.EnvVars(toEnvName(FlgDomains)), Usage: "Add a domain. For multiple domains either repeat the option or provide a comma-separated list.", } } @@ -865,18 +687,18 @@ func createDomainFlag() cli.Flag { func createCertNameFlag() cli.Flag { return &cli.StringFlag{ Category: categoryStorage, - Name: flgCertName, + Name: FlgCertName, Aliases: []string{flgAliasCertName}, - Sources: cli.EnvVars(toEnvName(flgCertName)), + Sources: cli.EnvVars(toEnvName(FlgCertName)), Usage: "The certificate ID/Name, used to store and retrieve a certificate. By default, it uses the first domain name.", } } func createCertNamesFlag() cli.Flag { return &cli.StringSliceFlag{ - Name: flgCertName, + Name: FlgCertName, Aliases: []string{flgAliasCertName}, - Sources: cli.EnvVars(toEnvName(flgCertName)), + Sources: cli.EnvVars(toEnvName(FlgCertName)), Usage: "The certificate IDs/Names, used to retrieve the certificates.", } } @@ -884,8 +706,8 @@ func createCertNamesFlag() cli.Flag { func createPathFlag(forceCreation bool) cli.Flag { return &cli.StringFlag{ Category: categoryStorage, - Name: flgPath, - Sources: cli.NewValueSourceChain(cli.EnvVar(toEnvName(flgPath)), &defaultPathValueSource{}), + Name: FlgPath, + Sources: cli.NewValueSourceChain(cli.EnvVar(toEnvName(FlgPath)), &defaultPathValueSource{}), Usage: "Directory to use for storing the data.", Validator: func(s string) error { if !forceCreation { diff --git a/cmd/internal/flags/names.go b/cmd/internal/flags/names.go new file mode 100644 index 000000000..6d7f00bbf --- /dev/null +++ b/cmd/internal/flags/names.go @@ -0,0 +1,183 @@ +package flags + +import ( + "strings" + "unicode" +) + +const ( + categoryHTTP01Challenge = "Flags related to the HTTP-01 challenge:" + categoryTLSALPN01Challenge = "Flags related to the TLS-ALPN-01 challenge:" + categoryDNS01Challenge = "Flags related to the DNS-01 challenge:" + categoryDNSPersist01Challenge = "Flags related to the DNS-PERSIST-01 challenge:" + categoryStorage = "Flags related to the storage:" + categoryHooks = "Flags related to hooks:" + categoryEAB = "Flags related to External Account Binding:" + categoryACMEClient = "Flags related to the ACME client:" + categoryAdvanced = "Flags related to advanced options:" + categoryARI = "Flags related to ACME Renewal Information (ARI) Extension:" + categoryLogs = "Flags related to logs:" +) + +// Flag aliases (short-codes). +const ( + flgAliasAcceptTOS = "a" + flgAliasCertName = "c" + flgAliasDomains = "d" + flgAliasEmail = "m" + flgAliasIPv4Only = "4" + flgAliasIPv6Only = "6" + flgAliasKeyType = "k" + flgAliasServer = "s" +) + +// Flag names related to the account. +const ( + FlgAcceptTOS = "accept-tos" + FlgEmail = "email" + FlgKeyType = "key-type" + FlgAccountID = "account-id" + FlgEAB = "eab" + FlgEABKID = "eab.kid" + FlgEABHMAC = "eab.hmac" +) + +// Flag names related to Obtain certificates. +const ( + FlgDomains = "domains" + 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 storage. +const ( + FlgPath = "path" + FlgPEM = "pem" + FlgPFX = "pfx" + FlgPFXPass = "pfx.pass" + FlgPFXFormat = "pfx.format" +) + +// Flag names related to the ACME client. +const ( + FlgServer = "server" + FlgEnableCommonName = "enable-cn" + FlgHTTPTimeout = "http-timeout" + FlgTLSSkipVerify = "tls-skip-verify" + FlgOverallRequestLimit = "overall-request-limit" + FlgUserAgent = "user-agent" +) + +// Flag names related to certificates. +const ( + FlgCertTimeout = "cert.timeout" + FlgCertName = "cert.name" +) + +// Flag names related to the network stack. +const ( + FlgIPv4Only = "ipv4only" + FlgIPv6Only = "ipv6only" +) + +// Flag names related to HTTP-01 challenge. +const ( + FlgHTTP = "http" + FlgHTTPPort = "http.port" + FlgHTTPDelay = "http.delay" + FlgHTTPProxyHeader = "http.proxy-header" + FlgHTTPWebroot = "http.webroot" + FlgHTTPMemcachedHost = "http.memcached-host" + FlgHTTPS3Bucket = "http.s3-bucket" +) + +// Flag names related to TLS-ALPN-01 challenge. +const ( + FlgTLS = "tls" + FlgTLSPort = "tls.port" + FlgTLSDelay = "tls.delay" +) + +// Flag names related to DNS-01 challenge. +const ( + FlgDNS = "dns" + FlgDNSPropagationWait = "dns.propagation.wait" + FlgDNSPropagationDisableANS = "dns.propagation.disable-ans" + FlgDNSPropagationDisableRNS = "dns.propagation.disable-rns" + FlgDNSResolvers = "dns.resolvers" + FlgDNSTimeout = "dns.timeout" +) + +// Flag names related to the DNS-PERSIST-01 challenge. +const ( + FlgDNSPersist = "dns-persist" + FlgDNSPersistIssuerDomainName = "dns-persist.issuer-domain-name" + FlgDNSPersistPersistUntil = "dns-persist.persist-until" + FlgDNSPersistPropagationWait = "dns-persist.propagation.wait" + FlgDNSPersistPropagationDisableANS = "dns-persist.propagation.disable-ans" + FlgDNSPersistPropagationDisableRNS = "dns-persist.propagation.disable-rns" + FlgDNSPersistResolvers = "dns-persist.resolvers" + FlgDNSPersistTimeout = "dns-persist.timeout" +) + +// Flags names related to hooks. +const ( + FlgPreHook = "pre-hook" + FlgPreHookTimeout = "pre-hook-timeout" + FlgDeployHook = "deploy-hook" + FlgDeployHookTimeout = "deploy-hook-timeout" + FlgPostHook = "post-hook" + FlgPostHookTimeout = "post-hook-timeout" +) + +// Flag names related to logs. +const ( + FlgLogLevel = "log.level" + FlgLogFormat = "log.format" +) + +// Flag names related to the specific run command. +const ( + FlgPrivateKey = "private-key" +) + +// Flag names related to the specific renew command. +const ( + FlgRenewDays = "renew-days" + FlgRenewForce = "renew-force" + 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 commands. +const ( + FlgFormatJSON = "json" +) + +// Flag names related to the migrate command. +const ( + FlgAccountOnly = "account-only" +) + +func toEnvName(flg string) string { + fields := strings.FieldsFunc(flg, func(r rune) bool { + return !unicode.IsLetter(r) && !unicode.IsNumber(r) + }) + + return "LEGO_" + strings.ToUpper(strings.Join(fields, "_")) +} diff --git a/cmd/flags_test.go b/cmd/internal/flags/names_test.go similarity index 83% rename from cmd/flags_test.go rename to cmd/internal/flags/names_test.go index ef89c045c..dd71df96c 100644 --- a/cmd/flags_test.go +++ b/cmd/internal/flags/names_test.go @@ -1,4 +1,4 @@ -package cmd +package flags import ( "testing" @@ -14,22 +14,22 @@ func Test_toEnvName(t *testing.T) { }{ { desc: "only letters", - flag: flgServer, + flag: FlgServer, expected: "LEGO_SERVER", }, { desc: "letters and digits", - flag: flgIPv6Only, + flag: FlgIPv6Only, expected: "LEGO_IPV6ONLY", }, { desc: "hyphen", - flag: flgHTTPPort, + flag: FlgHTTPPort, expected: "LEGO_HTTP_PORT", }, { desc: "dot, hyphen", - flag: flgDNSPropagationDisableRNS, + flag: FlgDNSPropagationDisableRNS, expected: "LEGO_DNS_PROPAGATION_DISABLE_RNS", }, } diff --git a/cmd/flags_validation.go b/cmd/internal/flags/validation.go similarity index 60% rename from cmd/flags_validation.go rename to cmd/internal/flags/validation.go index 7b343ca40..15a1f9ca1 100644 --- a/cmd/flags_validation.go +++ b/cmd/internal/flags/validation.go @@ -1,4 +1,4 @@ -package cmd +package flags import ( "context" @@ -7,19 +7,19 @@ import ( "github.com/urfave/cli/v3" ) -func runFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Context, error) { +func RunFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Context, error) { // we require either domains or csr, but not both - hasDomains := len(cmd.StringSlice(flgDomains)) > 0 + hasDomains := len(cmd.StringSlice(FlgDomains)) > 0 - hasCsr := cmd.String(flgCSR) != "" + hasCsr := cmd.String(FlgCSR) != "" if hasDomains && hasCsr { return ctx, fmt.Errorf("please specify either '--%s'/'-%s' or '--%s', but not both", - flgDomains, flgAliasDomains, flgCSR) + FlgDomains, flgAliasDomains, FlgCSR) } if !hasDomains && !hasCsr { return ctx, fmt.Errorf("please specify '--%s'/'-%s' (or '--%s' if you already have a CSR)", - flgDomains, flgAliasDomains, flgCSR) + FlgDomains, flgAliasDomains, FlgCSR) } err := validateChallengeRequirements(cmd) @@ -30,24 +30,24 @@ func runFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Context, return ctx, validateNetworkStack(cmd) } -func renewFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Context, error) { - hasDomains := len(cmd.StringSlice(flgDomains)) > 0 - hasCsr := cmd.String(flgCSR) != "" - hasCertID := cmd.String(flgCertName) != "" +func RenewFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Context, error) { + hasDomains := len(cmd.StringSlice(FlgDomains)) > 0 + hasCsr := cmd.String(FlgCSR) != "" + hasCertID := cmd.String(FlgCertName) != "" if hasDomains && hasCsr { return ctx, fmt.Errorf("please specify either '--%s'/'-%s' or '--%s', but not both", - flgDomains, flgAliasDomains, flgCSR) + FlgDomains, flgAliasDomains, FlgCSR) } if !hasCertID && !hasDomains && !hasCsr { return ctx, fmt.Errorf("please specify '--%s' or '--%s'/'-%s' (or '--%s' if you already have a CSR)", - flgCertName, flgDomains, flgAliasDomains, flgCSR) + FlgCertName, FlgDomains, flgAliasDomains, FlgCSR) } - if cmd.Bool(flgForceCertDomains) && hasCsr { + if cmd.Bool(FlgForceCertDomains) && hasCsr { return ctx, fmt.Errorf("'--%s' only works with '--%s'/'-%s', '--%s' doesn't support this option", - flgForceCertDomains, flgDomains, flgAliasDomains, flgCSR) + FlgForceCertDomains, FlgDomains, flgAliasDomains, FlgCSR) } err := validateChallengeRequirements(cmd) @@ -59,28 +59,28 @@ func renewFlagsValidation(ctx context.Context, cmd *cli.Command) (context.Contex } func validateNetworkStack(cmd *cli.Command) error { - if cmd.Bool(flgIPv4Only) && cmd.Bool(flgIPv6Only) { - return fmt.Errorf("cannot specify both '--%s' and '--%s'", flgIPv4Only, flgIPv6Only) + if cmd.Bool(FlgIPv4Only) && cmd.Bool(FlgIPv6Only) { + return fmt.Errorf("cannot specify both '--%s' and '--%s'", FlgIPv4Only, FlgIPv6Only) } return nil } func validateChallengeRequirements(cmd *cli.Command) error { - if !cmd.Bool(flgHTTP) && !cmd.Bool(flgTLS) && !cmd.IsSet(flgDNS) && !cmd.Bool(flgDNSPersist) { + if !cmd.Bool(FlgHTTP) && !cmd.Bool(FlgTLS) && !cmd.IsSet(FlgDNS) && !cmd.Bool(FlgDNSPersist) { return fmt.Errorf("no challenge selected: you must specify at least one challenge: '--%s', '--%s', '--%s', '--%s'", - flgHTTP, flgTLS, flgDNS, flgDNSPersist) + FlgHTTP, FlgTLS, FlgDNS, FlgDNSPersist) } - if isSetBool(cmd, flgDNS) { - err := validatePropagationExclusiveOptions(cmd, flgDNSPropagationWait, flgDNSPropagationDisableANS, flgDNSPropagationDisableRNS) + if isSetBool(cmd, FlgDNS) { + err := validatePropagationExclusiveOptions(cmd, FlgDNSPropagationWait, FlgDNSPropagationDisableANS, FlgDNSPropagationDisableRNS) if err != nil { return err } } - if isSetBool(cmd, flgDNSPersist) { - err := validatePropagationExclusiveOptions(cmd, flgDNSPersistPropagationWait, flgDNSPersistPropagationDisableANS, flgDNSPersistIssuerDomainName) + if isSetBool(cmd, FlgDNSPersist) { + err := validatePropagationExclusiveOptions(cmd, FlgDNSPersistPropagationWait, FlgDNSPersistPropagationDisableANS, FlgDNSPersistIssuerDomainName) if err != nil { return err } diff --git a/cmd/setup.go b/cmd/setup.go index 0a0c53eeb..7612a7c1d 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -12,6 +12,7 @@ import ( "github.com/go-acme/lego/v5/certcrypto" "github.com/go-acme/lego/v5/certificate" "github.com/go-acme/lego/v5/cmd/internal" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/cmd/internal/hook" "github.com/go-acme/lego/v5/cmd/internal/storage" "github.com/go-acme/lego/v5/lego" @@ -25,7 +26,7 @@ func newClient(cmd *cli.Command, account registration.User, keyType certcrypto.K return nil, fmt.Errorf("new client: %w", err) } - if client.GetServerMetadata().ExternalAccountRequired && !cmd.IsSet(flgEAB) { + if client.GetServerMetadata().ExternalAccountRequired && !cmd.IsSet(flags.FlgEAB) { return nil, errors.New("server requires External Account Binding (EAB)") } @@ -34,20 +35,20 @@ func newClient(cmd *cli.Command, account registration.User, keyType certcrypto.K func newClientConfig(cmd *cli.Command, account registration.User, keyType certcrypto.KeyType) *lego.Config { config := lego.NewConfig(account) - config.CADirURL = cmd.String(flgServer) + config.CADirURL = cmd.String(flags.FlgServer) config.UserAgent = getUserAgent(cmd) config.Certificate = lego.CertificateConfig{ KeyType: keyType, - Timeout: time.Duration(cmd.Int(flgCertTimeout)) * time.Second, - OverallRequestLimit: cmd.Int(flgOverallRequestLimit), + Timeout: time.Duration(cmd.Int(flags.FlgCertTimeout)) * time.Second, + OverallRequestLimit: cmd.Int(flags.FlgOverallRequestLimit), } - if cmd.IsSet(flgHTTPTimeout) { - config.HTTPClient.Timeout = time.Duration(cmd.Int(flgHTTPTimeout)) * time.Second + if cmd.IsSet(flags.FlgHTTPTimeout) { + config.HTTPClient.Timeout = time.Duration(cmd.Int(flags.FlgHTTPTimeout)) * time.Second } - if cmd.Bool(flgTLSSkipVerify) { + if cmd.Bool(flags.FlgTLSSkipVerify) { defaultTransport, ok := config.HTTPClient.Transport.(*http.Transport) if ok { // This is always true because the default client used by the CLI defined the transport. tr := defaultTransport.Clone() @@ -62,59 +63,59 @@ func newClientConfig(cmd *cli.Command, account registration.User, keyType certcr } func getUserAgent(cmd *cli.Command) string { - return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", cmd.String(flgUserAgent), cmd.Version)) + return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", cmd.String(flags.FlgUserAgent), cmd.Version)) } 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), - EnableCommonName: cmd.Bool(flgEnableCommonName), - Profile: cmd.String(flgProfile), - AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations), + MustStaple: cmd.Bool(flags.FlgMustStaple), + NotBefore: cmd.Timestamp(flags.FlgNotBefore), + NotAfter: cmd.Timestamp(flags.FlgNotAfter), + Bundle: !cmd.Bool(flags.FlgNoBundle), + PreferredChain: cmd.String(flags.FlgPreferredChain), + EnableCommonName: cmd.Bool(flags.FlgEnableCommonName), + Profile: cmd.String(flags.FlgProfile), + AlwaysDeactivateAuthorizations: cmd.Bool(flags.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), - EnableCommonName: cmd.Bool(flgEnableCommonName), - Profile: cmd.String(flgProfile), - AlwaysDeactivateAuthorizations: cmd.Bool(flgAlwaysDeactivateAuthorizations), + NotBefore: cmd.Timestamp(flags.FlgNotBefore), + NotAfter: cmd.Timestamp(flags.FlgNotAfter), + Bundle: !cmd.Bool(flags.FlgNoBundle), + PreferredChain: cmd.String(flags.FlgPreferredChain), + EnableCommonName: cmd.Bool(flags.FlgEnableCommonName), + Profile: cmd.String(flags.FlgProfile), + AlwaysDeactivateAuthorizations: cmd.Bool(flags.FlgAlwaysDeactivateAuthorizations), } } func newAccountsStorageConfig(cmd *cli.Command) storage.AccountsStorageConfig { return storage.AccountsStorageConfig{ - BasePath: cmd.String(flgPath), - Server: cmd.String(flgServer), + BasePath: cmd.String(flags.FlgPath), + Server: cmd.String(flags.FlgServer), UserAgent: getUserAgent(cmd), } } func newSaveOptions(cmd *cli.Command) *storage.SaveOptions { return &storage.SaveOptions{ - PEM: cmd.Bool(flgPEM), - PFX: cmd.Bool(flgPFX), - PFXFormat: cmd.String(flgPFXPass), - PFXPassword: cmd.String(flgPFXFormat), + PEM: cmd.Bool(flags.FlgPEM), + PFX: cmd.Bool(flags.FlgPFX), + PFXFormat: cmd.String(flags.FlgPFXPass), + PFXPassword: cmd.String(flags.FlgPFXFormat), } } func newHookManager(cmd *cli.Command, certsStorage *storage.CertificatesStorage, account *storage.Account) *hook.Manager { return hook.NewManager( certsStorage, - hook.WithPre(cmd.String(flgPreHook), cmd.Duration(flgPreHookTimeout)), - hook.WithDeploy(cmd.String(flgDeployHook), cmd.Duration(flgDeployHookTimeout)), - hook.WithPost(cmd.String(flgPostHook), cmd.Duration(flgPostHookTimeout)), + hook.WithPre(cmd.String(flags.FlgPreHook), cmd.Duration(flags.FlgPreHookTimeout)), + hook.WithDeploy(cmd.String(flags.FlgDeployHook), cmd.Duration(flags.FlgDeployHookTimeout)), + hook.WithPost(cmd.String(flags.FlgPostHook), cmd.Duration(flags.FlgPostHookTimeout)), hook.WithAccountMetadata(account), ) } diff --git a/cmd/setup_challenges.go b/cmd/setup_challenges.go index 870819a68..395e9b579 100644 --- a/cmd/setup_challenges.go +++ b/cmd/setup_challenges.go @@ -11,6 +11,7 @@ import ( "github.com/go-acme/lego/v5/challenge/dnspersist01" "github.com/go-acme/lego/v5/challenge/http01" "github.com/go-acme/lego/v5/challenge/tlsalpn01" + "github.com/go-acme/lego/v5/cmd/internal/flags" "github.com/go-acme/lego/v5/lego" "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/providers/dns" @@ -21,28 +22,28 @@ import ( ) func setupChallenges(cmd *cli.Command, client *lego.Client) { - if cmd.Bool(flgHTTP) { - err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(cmd), http01.SetDelay(cmd.Duration(flgHTTPDelay))) + if cmd.Bool(flags.FlgHTTP) { + err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(cmd), http01.SetDelay(cmd.Duration(flags.FlgHTTPDelay))) if err != nil { log.Fatal("Could not set HTTP challenge provider.", log.ErrorAttr(err)) } } - if cmd.Bool(flgTLS) { - err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(cmd), tlsalpn01.SetDelay(cmd.Duration(flgTLSDelay))) + if cmd.Bool(flags.FlgTLS) { + err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(cmd), tlsalpn01.SetDelay(cmd.Duration(flags.FlgTLSDelay))) if err != nil { log.Fatal("Could not set TLS challenge provider.", log.ErrorAttr(err)) } } - if cmd.IsSet(flgDNS) { + if cmd.IsSet(flags.FlgDNS) { err := setupDNS(cmd, client) if err != nil { log.Fatal("Could not set DNS challenge provider.", log.ErrorAttr(err)) } } - if cmd.Bool(flgDNSPersist) { + if cmd.Bool(flags.FlgDNSPersist) { err := setupDNSPersist(cmd, client) if err != nil { log.Fatal("Could not set DNS-PERSIST-01 challenge provider.", log.ErrorAttr(err)) @@ -52,44 +53,44 @@ func setupChallenges(cmd *cli.Command, client *lego.Client) { func setupHTTPProvider(cmd *cli.Command) challenge.Provider { switch { - case cmd.IsSet(flgHTTPWebroot): - ps, err := webroot.NewHTTPProvider(cmd.String(flgHTTPWebroot)) + case cmd.IsSet(flags.FlgHTTPWebroot): + ps, err := webroot.NewHTTPProvider(cmd.String(flags.FlgHTTPWebroot)) if err != nil { log.Fatal("Could not create the webroot provider.", - slog.String("flag", flgHTTPWebroot), - slog.String("webRoot", cmd.String(flgHTTPWebroot)), + slog.String("flag", flags.FlgHTTPWebroot), + slog.String("webRoot", cmd.String(flags.FlgHTTPWebroot)), log.ErrorAttr(err), ) } return ps - case cmd.IsSet(flgHTTPMemcachedHost): - ps, err := memcached.NewMemcachedProvider(cmd.StringSlice(flgHTTPMemcachedHost)) + case cmd.IsSet(flags.FlgHTTPMemcachedHost): + ps, err := memcached.NewMemcachedProvider(cmd.StringSlice(flags.FlgHTTPMemcachedHost)) if err != nil { log.Fatal("Could not create the memcached provider.", - slog.String("flag", flgHTTPMemcachedHost), - slog.String("memcachedHosts", strings.Join(cmd.StringSlice(flgHTTPMemcachedHost), ", ")), + slog.String("flag", flags.FlgHTTPMemcachedHost), + slog.String("memcachedHosts", strings.Join(cmd.StringSlice(flags.FlgHTTPMemcachedHost), ", ")), log.ErrorAttr(err), ) } return ps - case cmd.IsSet(flgHTTPS3Bucket): - ps, err := s3.NewHTTPProvider(cmd.String(flgHTTPS3Bucket)) + case cmd.IsSet(flags.FlgHTTPS3Bucket): + ps, err := s3.NewHTTPProvider(cmd.String(flags.FlgHTTPS3Bucket)) if err != nil { log.Fatal("Could not create the S3 provider.", - slog.String("flag", flgHTTPS3Bucket), - slog.String("bucket", cmd.String(flgHTTPS3Bucket)), + slog.String("flag", flags.FlgHTTPS3Bucket), + slog.String("bucket", cmd.String(flags.FlgHTTPS3Bucket)), log.ErrorAttr(err), ) } return ps - case cmd.IsSet(flgHTTPPort): - host, port, err := parseAddress(cmd, flgHTTPPort) + case cmd.IsSet(flags.FlgHTTPPort): + host, port, err := parseAddress(cmd, flags.FlgHTTPPort) if err != nil { log.Fatal("Invalid address.", log.ErrorAttr(err)) } @@ -99,19 +100,19 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { Address: net.JoinHostPort(host, port), }) - if header := cmd.String(flgHTTPProxyHeader); header != "" { + if header := cmd.String(flags.FlgHTTPProxyHeader); header != "" { srv.SetProxyHeader(header) } return srv - case cmd.Bool(flgHTTP): + case cmd.Bool(flags.FlgHTTP): srv := http01.NewProviderServerWithOptions(http01.Options{ Network: getNetworkStack(cmd).Network("tcp"), Address: net.JoinHostPort("", ":80"), }) - if header := cmd.String(flgHTTPProxyHeader); header != "" { + if header := cmd.String(flags.FlgHTTPProxyHeader); header != "" { srv.SetProxyHeader(header) } @@ -125,8 +126,8 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { func setupTLSProvider(cmd *cli.Command) challenge.Provider { switch { - case cmd.IsSet(flgTLSPort): - host, port, err := parseAddress(cmd, flgTLSPort) + case cmd.IsSet(flags.FlgTLSPort): + host, port, err := parseAddress(cmd, flags.FlgTLSPort) if err != nil { log.Fatal("Invalid address.", log.ErrorAttr(err)) } @@ -137,7 +138,7 @@ func setupTLSProvider(cmd *cli.Command) challenge.Provider { Port: port, }) - case cmd.Bool(flgTLS): + case cmd.Bool(flags.FlgTLS): return tlsalpn01.NewProviderServerWithOptions(tlsalpn01.Options{ Network: getNetworkStack(cmd).Network("tcp"), }) @@ -149,32 +150,32 @@ func setupTLSProvider(cmd *cli.Command) challenge.Provider { } func setupDNS(cmd *cli.Command, client *lego.Client) error { - provider, err := dns.NewDNSChallengeProviderByName(cmd.String(flgDNS)) + provider, err := dns.NewDNSChallengeProviderByName(cmd.String(flags.FlgDNS)) if err != nil { return err } - opts := &dns01.Options{RecursiveNameservers: cmd.StringSlice(flgDNSResolvers)} + opts := &dns01.Options{RecursiveNameservers: cmd.StringSlice(flags.FlgDNSResolvers)} - if cmd.IsSet(flgDNSTimeout) { - opts.Timeout = time.Duration(cmd.Int(flgDNSTimeout)) * time.Second + if cmd.IsSet(flags.FlgDNSTimeout) { + opts.Timeout = time.Duration(cmd.Int(flags.FlgDNSTimeout)) * time.Second } opts.NetworkStack = getNetworkStack(cmd) dns01.SetDefaultClient(dns01.NewClient(opts)) - shouldWait := cmd.IsSet(flgDNSPropagationWait) + shouldWait := cmd.IsSet(flags.FlgDNSPropagationWait) err = client.Challenge.SetDNS01Provider(provider, dns01.CondOptions(shouldWait, - dns01.PropagationWait(cmd.Duration(flgDNSPropagationWait), true), + dns01.PropagationWait(cmd.Duration(flags.FlgDNSPropagationWait), true), ), dns01.CondOptions(!shouldWait, - dns01.CondOptions(cmd.Bool(flgDNSPropagationDisableANS), + dns01.CondOptions(cmd.Bool(flags.FlgDNSPropagationDisableANS), dns01.DisableAuthoritativeNssPropagationRequirement(), ), - dns01.CondOptions(cmd.Bool(flgDNSPropagationDisableRNS), + dns01.CondOptions(cmd.Bool(flags.FlgDNSPropagationDisableRNS), dns01.DisableRecursiveNSsPropagationRequirement(), ), ), @@ -184,31 +185,31 @@ func setupDNS(cmd *cli.Command, client *lego.Client) error { } func setupDNSPersist(cmd *cli.Command, client *lego.Client) error { - opts := &dnspersist01.Options{RecursiveNameservers: cmd.StringSlice(flgDNSPersistResolvers)} + opts := &dnspersist01.Options{RecursiveNameservers: cmd.StringSlice(flags.FlgDNSPersistResolvers)} - if cmd.IsSet(flgDNSPersistTimeout) { - opts.Timeout = time.Duration(cmd.Int(flgDNSPersistTimeout)) * time.Second + if cmd.IsSet(flags.FlgDNSPersistTimeout) { + opts.Timeout = time.Duration(cmd.Int(flags.FlgDNSPersistTimeout)) * time.Second } opts.NetworkStack = getNetworkStack(cmd) dnspersist01.SetDefaultClient(dnspersist01.NewClient(opts)) - shouldWait := cmd.IsSet(flgDNSPersistPropagationWait) + shouldWait := cmd.IsSet(flags.FlgDNSPersistPropagationWait) return client.Challenge.SetDNSPersist01( - dnspersist01.WithIssuerDomainName(cmd.String(flgDNSPersistIssuerDomainName)), - dnspersist01.CondOptions(cmd.IsSet(flgDNSPersistPersistUntil), - dnspersist01.WithPersistUntil(cmd.Timestamp(flgDNSPersistPersistUntil)), + dnspersist01.WithIssuerDomainName(cmd.String(flags.FlgDNSPersistIssuerDomainName)), + dnspersist01.CondOptions(cmd.IsSet(flags.FlgDNSPersistPersistUntil), + dnspersist01.WithPersistUntil(cmd.Timestamp(flags.FlgDNSPersistPersistUntil)), ), dnspersist01.CondOptions(shouldWait, - dnspersist01.PropagationWait(cmd.Duration(flgDNSPersistPropagationWait), true), + dnspersist01.PropagationWait(cmd.Duration(flags.FlgDNSPersistPropagationWait), true), ), dnspersist01.CondOptions(!shouldWait, - dnspersist01.CondOptions(cmd.Bool(flgDNSPersistPropagationDisableANS), + dnspersist01.CondOptions(cmd.Bool(flags.FlgDNSPersistPropagationDisableANS), dnspersist01.DisableAuthoritativeNssPropagationRequirement(), ), - dnspersist01.CondOptions(cmd.Bool(flgDNSPersistPropagationDisableRNS), + dnspersist01.CondOptions(cmd.Bool(flags.FlgDNSPersistPropagationDisableRNS), dnspersist01.DisableRecursiveNSsPropagationRequirement(), ), ), @@ -217,10 +218,10 @@ func setupDNSPersist(cmd *cli.Command, client *lego.Client) error { func getNetworkStack(cmd *cli.Command) challenge.NetworkStack { switch { - case cmd.Bool(flgIPv4Only): + case cmd.Bool(flags.FlgIPv4Only): return challenge.IPv4Only - case cmd.Bool(flgIPv6Only): + case cmd.Bool(flags.FlgIPv6Only): return challenge.IPv6Only default: diff --git a/internal/generators/clihelp/generator.go b/internal/generators/clihelp/generator.go index 70a4bdb35..847f063fd 100644 --- a/internal/generators/clihelp/generator.go +++ b/internal/generators/clihelp/generator.go @@ -89,12 +89,11 @@ func generate(ctx context.Context) error { // - do not include version information, because we're likely running against a snapshot // - skip DNS help and provider list, as initialization takes time, and we don't generate `lego dns --help` here. func createStubApp() *cli.Command { - return &cli.Command{ - Name: "lego", - Usage: "ACME client written in Go", - Commands: cmd.CreateCommands(), - Flags: cmd.CreateLogFlags(), - } + root := cmd.CreateRootCommand() + root.EnableShellCompletion = false + root.Before = nil + + return root } func run(ctx context.Context, app *cli.Command, args []string) (h commandHelp, err error) {