chore: use contants for flag names (#2283)

This commit is contained in:
Ludovic Fernandez 2024-09-20 19:47:50 +02:00 committed by GitHub
parent 20c8d6c413
commit d2898e1706
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 245 additions and 175 deletions

View file

@ -71,12 +71,12 @@ func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
// TODO: move to account struct? Currently MUST pass email. // TODO: move to account struct? Currently MUST pass email.
email := getEmail(ctx) email := getEmail(ctx)
serverURL, err := url.Parse(ctx.String("server")) serverURL, err := url.Parse(ctx.String(flgServer))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
rootPath := filepath.Join(ctx.String("path"), baseAccountsRootFolderName) rootPath := filepath.Join(ctx.String(flgPath), baseAccountsRootFolderName)
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host) serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host)
accountsPath := filepath.Join(rootPath, serverPath) accountsPath := filepath.Join(rootPath, serverPath)
rootUserPath := filepath.Join(accountsPath, email) rootUserPath := filepath.Join(accountsPath, email)
@ -224,7 +224,7 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) {
func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) { func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
// couldn't load account but got a key. Try to look the account up. // couldn't load account but got a key. Try to look the account up.
config := lego.NewConfig(&Account{key: privateKey}) config := lego.NewConfig(&Account{key: privateKey})
config.CADirURL = ctx.String("server") config.CADirURL = ctx.String(flgServer)
config.UserAgent = getUserAgent(ctx) config.UserAgent = getUserAgent(ctx)
client, err := lego.NewClient(config) client, err := lego.NewClient(config)

View file

@ -61,7 +61,7 @@ type CertificatesStorage struct {
// NewCertificatesStorage create a new certificates storage. // NewCertificatesStorage create a new certificates storage.
func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage { func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
pfxFormat := ctx.String("pfx.format") pfxFormat := ctx.String(flgPFXFormat)
switch pfxFormat { switch pfxFormat {
case "DES", "RC2", "SHA256": case "DES", "RC2", "SHA256":
@ -70,13 +70,13 @@ func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
} }
return &CertificatesStorage{ return &CertificatesStorage{
rootPath: filepath.Join(ctx.String("path"), baseCertificatesFolderName), rootPath: filepath.Join(ctx.String(flgPath), baseCertificatesFolderName),
archivePath: filepath.Join(ctx.String("path"), baseArchivesFolderName), archivePath: filepath.Join(ctx.String(flgPath), baseArchivesFolderName),
pem: ctx.Bool("pem"), pem: ctx.Bool(flgPEM),
pfx: ctx.Bool("pfx"), pfx: ctx.Bool(flgPFX),
pfxPassword: ctx.String("pfx.pass"), pfxPassword: ctx.String(flgPFXPass),
pfxFormat: pfxFormat, pfxFormat: pfxFormat,
filename: ctx.String("filename"), filename: ctx.String(flgFilename),
} }
} }

View file

@ -6,17 +6,17 @@ import (
) )
func Before(ctx *cli.Context) error { func Before(ctx *cli.Context) error {
if ctx.String("path") == "" { if ctx.String(flgPath) == "" {
log.Fatal("Could not determine current working directory. Please pass --path.") log.Fatalf("Could not determine current working directory. Please pass --%s.", flgPath)
} }
err := createNonExistingFolder(ctx.String("path")) err := createNonExistingFolder(ctx.String(flgPath))
if err != nil { if err != nil {
log.Fatalf("Could not check/create path: %v", err) log.Fatalf("Could not check/create path: %v", err)
} }
if ctx.String("server") == "" { if ctx.String(flgServer) == "" {
log.Fatal("Could not determine current working server. Please pass --server.") log.Fatalf("Could not determine current working server. Please pass --%s.", flgServer)
} }
return nil return nil

View file

@ -9,6 +9,8 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
const flgCode = "code"
func createDNSHelp() *cli.Command { func createDNSHelp() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "dnshelp", Name: "dnshelp",
@ -16,7 +18,7 @@ func createDNSHelp() *cli.Command {
Action: dnsHelp, Action: dnsHelp,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "code", Name: flgCode,
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()), Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
}, },
@ -25,7 +27,7 @@ func createDNSHelp() *cli.Command {
} }
func dnsHelp(ctx *cli.Context) error { func dnsHelp(ctx *cli.Context) error {
code := ctx.String("code") code := ctx.String(flgCode)
if code == "" { if code == "" {
w := tabwriter.NewWriter(ctx.App.Writer, 0, 0, 2, ' ', 0) w := tabwriter.NewWriter(ctx.App.Writer, 0, 0, 2, ' ', 0)
ew := &errWriter{w: w} ew := &errWriter{w: w}

View file

@ -12,6 +12,11 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
const (
flgAccounts = "accounts"
flgNames = "names"
)
func createList() *cli.Command { func createList() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "list", Name: "list",
@ -19,18 +24,18 @@ func createList() *cli.Command {
Action: list, Action: list,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "accounts", Name: flgAccounts,
Aliases: []string{"a"}, Aliases: []string{"a"},
Usage: "Display accounts.", Usage: "Display accounts.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "names", Name: flgNames,
Aliases: []string{"n"}, Aliases: []string{"n"},
Usage: "Display certificate common names only.", Usage: "Display certificate common names only.",
}, },
// fake email, needed by NewAccountsStorage // fake email, needed by NewAccountsStorage
&cli.StringFlag{ &cli.StringFlag{
Name: "email", Name: flgEmail,
Value: "unknown", Value: "unknown",
Hidden: true, Hidden: true,
}, },
@ -39,7 +44,7 @@ func createList() *cli.Command {
} }
func list(ctx *cli.Context) error { func list(ctx *cli.Context) error {
if ctx.Bool("accounts") && !ctx.Bool("names") { if ctx.Bool(flgAccounts) && !ctx.Bool(flgNames) {
if err := listAccount(ctx); err != nil { if err := listAccount(ctx); err != nil {
return err return err
} }
@ -56,7 +61,7 @@ func listCertificates(ctx *cli.Context) error {
return err return err
} }
names := ctx.Bool("names") names := ctx.Bool(flgNames)
if len(matches) == 0 { if len(matches) == 0 {
if !names { if !names {
@ -70,7 +75,7 @@ func listCertificates(ctx *cli.Context) error {
} }
for _, filename := range matches { for _, filename := range matches {
if strings.HasSuffix(filename, ".issuer.crt") { if strings.HasSuffix(filename, issuerExt) {
continue continue
} }

View file

@ -17,6 +17,16 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
// Flag names.
const (
flgDays = "days"
flgARIEnable = "ari-enable"
flgARIWaitToRenewDuration = "ari-wait-to-renew-duration"
flgReuseKey = "reuse-key"
flgRenewHook = "renew-hook"
flgNoRandomSleep = "no-random-sleep"
)
const ( const (
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL" renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
renewEnvCertDomain = "LEGO_CERT_DOMAIN" renewEnvCertDomain = "LEGO_CERT_DOMAIN"
@ -34,68 +44,68 @@ func createRenew() *cli.Command {
Action: renew, Action: renew,
Before: func(ctx *cli.Context) error { Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both // we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0 hasDomains := len(ctx.StringSlice(flgDomains)) > 0
hasCsr := ctx.String("csr") != "" hasCsr := ctx.String(flgCSR) != ""
if hasDomains && hasCsr { if hasDomains && hasCsr {
log.Fatal("Please specify either --domains/-d or --csr/-c, but not both") log.Fatal("Please specify either --%s/-d or --%s/-c, but not both", flgDomains, flgCSR)
} }
if !hasDomains && !hasCsr { if !hasDomains && !hasCsr {
log.Fatal("Please specify --domains/-d (or --csr/-c if you already have a CSR)") log.Fatal("Please specify --%s/-d (or --%s/-c if you already have a CSR)", flgDomains, flgCSR)
} }
return nil return nil
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.IntFlag{ &cli.IntFlag{
Name: "days", Name: flgDays,
Value: 30, Value: 30,
Usage: "The number of days left on a certificate to renew it.", Usage: "The number of days left on a certificate to renew it.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "ari-enable", Name: flgARIEnable,
Usage: "Use the renewalInfo endpoint (draft-ietf-acme-ari) to check if a certificate should be renewed.", Usage: "Use the renewalInfo endpoint (draft-ietf-acme-ari) to check if a certificate should be renewed.",
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: "ari-wait-to-renew-duration", Name: flgARIWaitToRenewDuration,
Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.", Usage: "The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "reuse-key", Name: flgReuseKey,
Usage: "Used to indicate you want to reuse your current private key for the new certificate.", Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "no-bundle", Name: flgNoBundle,
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-staple", Name: flgMustStaple,
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." + Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
" Only works if the CSR is generated by lego.", " Only works if the CSR is generated by lego.",
}, },
&cli.TimestampFlag{ &cli.TimestampFlag{
Name: "not-before", Name: flgNotBefore,
Usage: "Set the notBefore field in the certificate (RFC3339 format)", Usage: "Set the notBefore field in the certificate (RFC3339 format)",
Layout: time.RFC3339, Layout: time.RFC3339,
}, },
&cli.TimestampFlag{ &cli.TimestampFlag{
Name: "not-after", Name: flgNotAfter,
Usage: "Set the notAfter field in the certificate (RFC3339 format)", Usage: "Set the notAfter field in the certificate (RFC3339 format)",
Layout: time.RFC3339, Layout: time.RFC3339,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "preferred-chain", Name: flgPreferredChain,
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name." + 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.", " If no match, the default offered chain will be used.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "always-deactivate-authorizations", Name: flgAlwaysDeactivateAuthorizations,
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.", Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "renew-hook", Name: flgRenewHook,
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.", Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "no-random-sleep", Name: flgNoRandomSleep,
Usage: "Do not add a random sleep before the renewal." + 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.", " We do not recommend using this flag if you are doing your renewals in an automated way.",
}, },
@ -113,12 +123,12 @@ func renew(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx) certsStorage := NewCertificatesStorage(ctx)
bundle := !ctx.Bool("no-bundle") bundle := !ctx.Bool(flgNoBundle)
meta := map[string]string{renewEnvAccountEmail: account.Email} meta := map[string]string{renewEnvAccountEmail: account.Email}
// CSR // CSR
if ctx.IsSet("csr") { if ctx.IsSet(flgCSR) {
return renewForCSR(ctx, client, certsStorage, bundle, meta) return renewForCSR(ctx, client, certsStorage, bundle, meta)
} }
@ -127,13 +137,13 @@ func renew(ctx *cli.Context) error {
} }
func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
domains := ctx.StringSlice("domains") domains := ctx.StringSlice(flgDomains)
domain := domains[0] domain := domains[0]
// load the cert resource from files. // load the cert resource from files.
// We store the certificate, private key and metadata in different files // We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file. // as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt") certificates, err := certsStorage.ReadCertificate(domain, certExt)
if err != nil { if err != nil {
log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err) log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err)
} }
@ -141,7 +151,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
cert := certificates[0] cert := certificates[0]
var ariRenewalTime *time.Time var ariRenewalTime *time.Time
if ctx.Bool("ari-enable") { if ctx.Bool(flgARIEnable) {
ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client) ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client)
if ariRenewalTime != nil { if ariRenewalTime != nil {
now := time.Now().UTC() now := time.Now().UTC()
@ -153,7 +163,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
} }
} }
if ariRenewalTime == nil && !needRenewal(cert, domain, ctx.Int("days")) { if ariRenewalTime == nil && !needRenewal(cert, domain, ctx.Int(flgDays)) {
return nil return nil
} }
@ -164,8 +174,8 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
certDomains := certcrypto.ExtractDomains(cert) certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey var privateKey crypto.PrivateKey
if ctx.Bool("reuse-key") { if ctx.Bool(flgReuseKey) {
keyBytes, errR := certsStorage.ReadFile(domain, ".key") keyBytes, errR := certsStorage.ReadFile(domain, keyExt)
if errR != nil { if errR != nil {
log.Fatalf("Error while loading the private key for domain %s\n\t%v", domain, errR) log.Fatalf("Error while loading the private key for domain %s\n\t%v", domain, errR)
} }
@ -178,7 +188,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
// https://github.com/go-acme/lego/issues/1656 // https://github.com/go-acme/lego/issues/1656
// https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L435-L440 // https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L435-L440
if !isatty.IsTerminal(os.Stdout.Fd()) && !ctx.Bool("no-random-sleep") { if !isatty.IsTerminal(os.Stdout.Fd()) && !ctx.Bool(flgNoRandomSleep) {
// https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L472 // https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L472
const jitter = 8 * time.Minute const jitter = 8 * time.Minute
rnd := rand.New(rand.NewSource(time.Now().UnixNano())) rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
@ -191,15 +201,15 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: merge(certDomains, domains), Domains: merge(certDomains, domains),
PrivateKey: privateKey, PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"), MustStaple: ctx.Bool(flgMustStaple),
NotBefore: getTime(ctx, "not-before"), NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, "not-after"), NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle, Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
} }
if ctx.Bool("ari-enable") { if ctx.Bool(flgARIEnable) {
request.ReplacesCertID, err = certificate.MakeARICertID(cert) request.ReplacesCertID, err = certificate.MakeARICertID(cert)
if err != nil { if err != nil {
log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err) log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err)
@ -215,11 +225,11 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
addPathToMetadata(meta, domain, certRes, certsStorage) addPathToMetadata(meta, domain, certRes, certsStorage)
return launchHook(ctx.String("renew-hook"), meta) return launchHook(ctx.String(flgRenewHook), meta)
} }
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
csr, err := readCSRFile(ctx.String("csr")) csr, err := readCSRFile(ctx.String(flgCSR))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -232,7 +242,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
// load the cert resource from files. // load the cert resource from files.
// We store the certificate, private key and metadata in different files // We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file. // as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt") certificates, err := certsStorage.ReadCertificate(domain, certExt)
if err != nil { if err != nil {
log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err) log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err)
} }
@ -240,7 +250,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
cert := certificates[0] cert := certificates[0]
var ariRenewalTime *time.Time var ariRenewalTime *time.Time
if ctx.Bool("ari-enable") { if ctx.Bool(flgARIEnable) {
ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client) ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client)
if ariRenewalTime != nil { if ariRenewalTime != nil {
now := time.Now().UTC() now := time.Now().UTC()
@ -252,7 +262,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
} }
} }
if ariRenewalTime == nil && !needRenewal(cert, domain, ctx.Int("days")) { if ariRenewalTime == nil && !needRenewal(cert, domain, ctx.Int(flgDays)) {
return nil return nil
} }
@ -262,14 +272,14 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
request := certificate.ObtainForCSRRequest{ request := certificate.ObtainForCSRRequest{
CSR: csr, CSR: csr,
NotBefore: getTime(ctx, "not-before"), NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, "not-after"), NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle, Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
} }
if ctx.Bool("ari-enable") { if ctx.Bool(flgARIEnable) {
request.ReplacesCertID, err = certificate.MakeARICertID(cert) request.ReplacesCertID, err = certificate.MakeARICertID(cert)
if err != nil { if err != nil {
log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err) log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err)
@ -285,7 +295,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
addPathToMetadata(meta, domain, certRes, certsStorage) addPathToMetadata(meta, domain, certRes, certsStorage)
return launchHook(ctx.String("renew-hook"), meta) return launchHook(ctx.String(flgRenewHook), meta)
} }
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool { func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
@ -323,7 +333,7 @@ func getARIRenewalTime(ctx *cli.Context, cert *x509.Certificate, domain string,
} }
now := time.Now().UTC() now := time.Now().UTC()
renewalTime := renewalInfo.ShouldRenewAt(now, ctx.Duration("ari-wait-to-renew-duration")) renewalTime := renewalInfo.ShouldRenewAt(now, ctx.Duration(flgARIWaitToRenewDuration))
if renewalTime == nil { if renewalTime == nil {
log.Infof("[%s] acme: renewalInfo endpoint indicates that renewal is not needed", domain) log.Infof("[%s] acme: renewalInfo endpoint indicates that renewal is not needed", domain)
return nil return nil

View file

@ -6,6 +6,12 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
// Flag names.
const (
flgKeep = "keep"
flgReason = "reason"
)
func createRevoke() *cli.Command { func createRevoke() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "revoke", Name: "revoke",
@ -13,12 +19,12 @@ func createRevoke() *cli.Command {
Action: revoke, Action: revoke,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "keep", Name: flgKeep,
Aliases: []string{"k"}, Aliases: []string{"k"},
Usage: "Keep the certificates after the revocation instead of archiving them.", Usage: "Keep the certificates after the revocation instead of archiving them.",
}, },
&cli.UintFlag{ &cli.UintFlag{
Name: "reason", Name: flgReason,
Usage: "Identifies the reason for the certificate revocation." + Usage: "Identifies the reason for the certificate revocation." +
" See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." + " See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." +
" Valid values are:" + " Valid values are:" +
@ -41,15 +47,15 @@ func revoke(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx) certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder() certsStorage.CreateRootFolder()
for _, domain := range ctx.StringSlice("domains") { for _, domain := range ctx.StringSlice(flgDomains) {
log.Printf("Trying to revoke certificate for domain %s", domain) log.Printf("Trying to revoke certificate for domain %s", domain)
certBytes, err := certsStorage.ReadFile(domain, ".crt") certBytes, err := certsStorage.ReadFile(domain, certExt)
if err != nil { if err != nil {
log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err) log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
} }
reason := ctx.Uint("reason") reason := ctx.Uint(flgReason)
err = client.Certificate.RevokeWithReason(certBytes, &reason) err = client.Certificate.RevokeWithReason(certBytes, &reason)
if err != nil { if err != nil {
@ -58,7 +64,7 @@ func revoke(ctx *cli.Context) error {
log.Println("Certificate was revoked.") log.Println("Certificate was revoked.")
if ctx.Bool("keep") { if ctx.Bool(flgKeep) {
return nil return nil
} }

View file

@ -14,14 +14,25 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
// Flag names.
const (
flgNoBundle = "no-bundle"
flgMustStaple = "must-staple"
flgNotBefore = "not-before"
flgNotAfter = "not-after"
flgPreferredChain = "preferred-chain"
flgAlwaysDeactivateAuthorizations = "always-deactivate-authorizations"
flgRunHook = "run-hook"
)
func createRun() *cli.Command { func createRun() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "run", Name: "run",
Usage: "Register an account, then create and install a certificate", Usage: "Register an account, then create and install a certificate",
Before: func(ctx *cli.Context) error { Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both // we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0 hasDomains := len(ctx.StringSlice(flgDomains)) > 0
hasCsr := ctx.String("csr") != "" hasCsr := ctx.String(flgCSR) != ""
if hasDomains && hasCsr { if hasDomains && hasCsr {
log.Fatal("Please specify either --domains/-d or --csr/-c, but not both") log.Fatal("Please specify either --domains/-d or --csr/-c, but not both")
} }
@ -33,35 +44,35 @@ func createRun() *cli.Command {
Action: run, Action: run,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "no-bundle", Name: flgNoBundle,
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-staple", Name: flgMustStaple,
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." + Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
" Only works if the CSR is generated by lego.", " Only works if the CSR is generated by lego.",
}, },
&cli.TimestampFlag{ &cli.TimestampFlag{
Name: "not-before", Name: flgNotBefore,
Usage: "Set the notBefore field in the certificate (RFC3339 format)", Usage: "Set the notBefore field in the certificate (RFC3339 format)",
Layout: time.RFC3339, Layout: time.RFC3339,
}, },
&cli.TimestampFlag{ &cli.TimestampFlag{
Name: "not-after", Name: flgNotAfter,
Usage: "Set the notAfter field in the certificate (RFC3339 format)", Usage: "Set the notAfter field in the certificate (RFC3339 format)",
Layout: time.RFC3339, Layout: time.RFC3339,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "preferred-chain", Name: flgPreferredChain,
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name." + 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.", " If no match, the default offered chain will be used.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "always-deactivate-authorizations", Name: flgAlwaysDeactivateAuthorizations,
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.", Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "run-hook", Name: flgRunHook,
Usage: "Define a hook. The hook is executed when the certificates are effectively created.", Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
}, },
}, },
@ -117,12 +128,12 @@ func run(ctx *cli.Context) error {
addPathToMetadata(meta, cert.Domain, cert, certsStorage) addPathToMetadata(meta, cert.Domain, cert, certsStorage)
return launchHook(ctx.String("run-hook"), meta) return launchHook(ctx.String(flgRunHook), meta)
} }
func handleTOS(ctx *cli.Context, client *lego.Client) bool { func handleTOS(ctx *cli.Context, client *lego.Client) bool {
// Check for a global accept override // Check for a global accept override
if ctx.Bool("accept-tos") { if ctx.Bool(flgAcceptTOS) {
return true return true
} }
@ -154,12 +165,12 @@ func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, er
log.Fatal("You did not accept the TOS. Unable to proceed.") log.Fatal("You did not accept the TOS. Unable to proceed.")
} }
if ctx.Bool("eab") { if ctx.Bool(flgEAB) {
kid := ctx.String("kid") kid := ctx.String(flgKID)
hmacEncoded := ctx.String("hmac") hmacEncoded := ctx.String(flgHMAC)
if kid == "" || hmacEncoded == "" { if kid == "" || hmacEncoded == "" {
log.Fatalf("Requires arguments --kid and --hmac.") log.Fatalf("Requires arguments --%s and --%s.", flgKID, flgHMAC)
} }
return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
@ -173,25 +184,25 @@ func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, er
} }
func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) { func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) {
bundle := !ctx.Bool("no-bundle") bundle := !ctx.Bool(flgNoBundle)
domains := ctx.StringSlice("domains") domains := ctx.StringSlice(flgDomains)
if len(domains) > 0 { if len(domains) > 0 {
// obtain a certificate, generating a new private key // obtain a certificate, generating a new private key
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: domains, Domains: domains,
Bundle: bundle, Bundle: bundle,
MustStaple: ctx.Bool("must-staple"), MustStaple: ctx.Bool(flgMustStaple),
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
} }
notBefore := ctx.Timestamp("not-before") notBefore := ctx.Timestamp(flgNotBefore)
if notBefore != nil { if notBefore != nil {
request.NotBefore = *notBefore request.NotBefore = *notBefore
} }
notAfter := ctx.Timestamp("not-after") notAfter := ctx.Timestamp(flgNotAfter)
if notAfter != nil { if notAfter != nil {
request.NotAfter = *notAfter request.NotAfter = *notAfter
} }
@ -200,7 +211,7 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
} }
// read the CSR // read the CSR
csr, err := readCSRFile(ctx.String("csr")) csr, err := readCSRFile(ctx.String(flgCSR))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -208,11 +219,11 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
// obtain a certificate for this CSR // obtain a certificate for this CSR
request := certificate.ObtainForCSRRequest{ request := certificate.ObtainForCSRRequest{
CSR: csr, CSR: csr,
NotBefore: getTime(ctx, "not-before"), NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, "not-after"), NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle, Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"), PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"), AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
} }
return client.Certificate.ObtainForCSR(request) return client.Certificate.ObtainForCSR(request)

View file

@ -9,163 +9,199 @@ import (
"software.sslmate.com/src/go-pkcs12" "software.sslmate.com/src/go-pkcs12"
) )
// Flag names.
const (
flgDomains = "domains"
flgServer = "server"
flgAcceptTOS = "accept-tos"
flgEmail = "email"
flgCSR = "csr"
flgEAB = "eab"
flgKID = "kid"
flgHMAC = "hmac"
flgKeyType = "key-type"
flgFilename = "filename"
flgPath = "path"
flgHTTP = "http"
flgHTTPPort = "http.port"
flgHTTPProxyHeader = "http.proxy-header"
flgHTTPWebroot = "http.webroot"
flgHTTPMemcachedHost = "http.memcached-host"
flgHTTPS3Bucket = "http.s3-bucket"
flgTLS = "tls"
flgTLSPort = "tls.port"
flgDNS = "dns"
flgDNSDisableCP = "dns.disable-cp"
flgDNSPropagationWait = "dns.propagation-wait"
flgDNSResolvers = "dns.resolvers"
flgHTTPTimeout = "http-timeout"
flgDNSTimeout = "dns-timeout"
flgPEM = "pem"
flgPFX = "pfx"
flgPFXPass = "pfx.pass"
flgPFXFormat = "pfx.format"
flgCertTimeout = "cert.timeout"
flgOverallRequestLimit = "overall-request-limit"
flgUserAgent = "user-agent"
)
func CreateFlags(defaultPath string) []cli.Flag { func CreateFlags(defaultPath string) []cli.Flag {
return []cli.Flag{ return []cli.Flag{
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "domains", Name: flgDomains,
Aliases: []string{"d"}, Aliases: []string{"d"},
Usage: "Add a domain to the process. Can be specified multiple times.", Usage: "Add a domain to the process. Can be specified multiple times.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "server", Name: flgServer,
Aliases: []string{"s"}, Aliases: []string{"s"},
EnvVars: []string{"LEGO_SERVER"}, EnvVars: []string{"LEGO_SERVER"},
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.", Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
Value: lego.LEDirectoryProduction, Value: lego.LEDirectoryProduction,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "accept-tos", Name: flgAcceptTOS,
Aliases: []string{"a"}, Aliases: []string{"a"},
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.", Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "email", Name: flgEmail,
Aliases: []string{"m"}, Aliases: []string{"m"},
Usage: "Email used for registration and recovery contact.", Usage: "Email used for registration and recovery contact.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "csr", Name: flgCSR,
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Certificate signing request filename, if an external CSR is to be used.", Usage: "Certificate signing request filename, if an external CSR is to be used.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "eab", Name: flgEAB,
EnvVars: []string{"LEGO_EAB"}, EnvVars: []string{"LEGO_EAB"},
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.", Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "kid", Name: flgKID,
EnvVars: []string{"LEGO_EAB_KID"}, EnvVars: []string{"LEGO_EAB_KID"},
Usage: "Key identifier from External CA. Used for External Account Binding.", Usage: "Key identifier from External CA. Used for External Account Binding.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "hmac", Name: flgHMAC,
EnvVars: []string{"LEGO_EAB_HMAC"}, EnvVars: []string{"LEGO_EAB_HMAC"},
Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.", Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "key-type", Name: flgKeyType,
Aliases: []string{"k"}, Aliases: []string{"k"},
Value: "ec256", Value: "ec256",
Usage: "Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384.", Usage: "Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "filename", Name: flgFilename,
Usage: "(deprecated) Filename of the generated certificate.", Usage: "(deprecated) Filename of the generated certificate.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "path", Name: flgPath,
EnvVars: []string{"LEGO_PATH"}, EnvVars: []string{"LEGO_PATH"},
Usage: "Directory to use for storing the data.", Usage: "Directory to use for storing the data.",
Value: defaultPath, Value: defaultPath,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "http", Name: flgHTTP,
Usage: "Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges.", Usage: "Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "http.port", Name: flgHTTPPort,
Usage: "Set the port and interface to use for HTTP-01 based challenges to listen on. Supported: interface:port or :port.", Usage: "Set the port and interface to use for HTTP-01 based challenges to listen on. Supported: interface:port or :port.",
Value: ":80", Value: ":80",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "http.proxy-header", Name: flgHTTPProxyHeader,
Usage: "Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy.", Usage: "Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy.",
Value: "Host", Value: "Host",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "http.webroot", Name: flgHTTPWebroot,
Usage: "Set the webroot folder to use for HTTP-01 based challenges to write directly to the .well-known/acme-challenge file." + 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", " This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
}, },
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "http.memcached-host", Name: flgHTTPMemcachedHost,
Usage: "Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.", Usage: "Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "http.s3-bucket", Name: flgHTTPS3Bucket,
Usage: "Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.", Usage: "Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "tls", Name: flgTLS,
Usage: "Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.", Usage: "Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "tls.port", Name: flgTLSPort,
Usage: "Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port.", Usage: "Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port.",
Value: ":443", Value: ":443",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "dns", Name: 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.", 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{ &cli.BoolFlag{
Name: "dns.disable-cp", Name: flgDNSDisableCP,
Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers.", Usage: "By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers.",
}, },
&cli.DurationFlag{ &cli.DurationFlag{
Name: "dns.propagation-wait", Name: flgDNSPropagationWait,
Usage: "By setting this flag, disables all the propagation checks and uses a wait duration instead.", Usage: "By setting this flag, disables all the propagation checks and uses a wait duration instead.",
}, },
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "dns.resolvers", Name: flgDNSResolvers,
Usage: "Set the resolvers to use for performing (recursive) CNAME resolving and apex domain determination." + 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." + " For DNS-01 challenge verification, the authoritative DNS server is queried directly." +
" Supported: host:port." + " Supported: host:port." +
" The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.", " The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "http-timeout", Name: flgHTTPTimeout,
Usage: "Set the HTTP timeout value to a specific value in seconds.", Usage: "Set the HTTP timeout value to a specific value in seconds.",
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "dns-timeout", Name: flgDNSTimeout,
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries.", Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries.",
Value: 10, Value: 10,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "pem", Name: flgPEM,
Usage: "Generate an additional .pem (base64) file by concatenating the .key and .crt files together.", Usage: "Generate an additional .pem (base64) file by concatenating the .key and .crt files together.",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "pfx", Name: flgPFX,
Usage: "Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together.", Usage: "Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together.",
EnvVars: []string{"LEGO_PFX"}, EnvVars: []string{"LEGO_PFX"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "pfx.pass", Name: flgPFXPass,
Usage: "The password used to encrypt the .pfx (PCKS#12) file.", Usage: "The password used to encrypt the .pfx (PCKS#12) file.",
Value: pkcs12.DefaultPassword, Value: pkcs12.DefaultPassword,
EnvVars: []string{"LEGO_PFX_PASSWORD"}, EnvVars: []string{"LEGO_PFX_PASSWORD"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "pfx.format", Name: flgPFXFormat,
Usage: "The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256.", Usage: "The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256.",
Value: "RC2", Value: "RC2",
EnvVars: []string{"LEGO_PFX_FORMAT"}, EnvVars: []string{"LEGO_PFX_FORMAT"},
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "cert.timeout", Name: flgCertTimeout,
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.", Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
Value: 30, Value: 30,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "overall-request-limit", Name: flgOverallRequestLimit,
Usage: "ACME overall requests limit.", Usage: "ACME overall requests limit.",
Value: certificate.DefaultOverallRequestLimit, Value: certificate.DefaultOverallRequestLimit,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "user-agent", Name: flgUserAgent,
Usage: "Add to the user-agent sent to the CA to identify an application embedding lego-cli", Usage: "Add to the user-agent sent to the CA to identify an application embedding lego-cli",
}, },
} }

View file

@ -35,17 +35,17 @@ func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client { func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc) config := lego.NewConfig(acc)
config.CADirURL = ctx.String("server") config.CADirURL = ctx.String(flgServer)
config.Certificate = lego.CertificateConfig{ config.Certificate = lego.CertificateConfig{
KeyType: keyType, KeyType: keyType,
Timeout: time.Duration(ctx.Int("cert.timeout")) * time.Second, Timeout: time.Duration(ctx.Int(flgCertTimeout)) * time.Second,
OverallRequestLimit: ctx.Int("overall-request-limit"), OverallRequestLimit: ctx.Int(flgOverallRequestLimit),
} }
config.UserAgent = getUserAgent(ctx) config.UserAgent = getUserAgent(ctx)
if ctx.IsSet("http-timeout") { if ctx.IsSet(flgHTTPTimeout) {
config.HTTPClient.Timeout = time.Duration(ctx.Int("http-timeout")) * time.Second config.HTTPClient.Timeout = time.Duration(ctx.Int(flgHTTPTimeout)) * time.Second
} }
client, err := lego.NewClient(config) client, err := lego.NewClient(config)
@ -53,8 +53,8 @@ func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyTy
log.Fatalf("Could not create client: %v", err) log.Fatalf("Could not create client: %v", err)
} }
if client.GetExternalAccountRequired() && !ctx.IsSet("eab") { if client.GetExternalAccountRequired() && !ctx.IsSet(flgEAB) {
log.Fatal("Server requires External Account Binding. Use --eab with --kid and --hmac.") log.Fatalf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC)
} }
return client return client
@ -62,7 +62,7 @@ func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyTy
// getKeyType the type from which private keys should be generated. // getKeyType the type from which private keys should be generated.
func getKeyType(ctx *cli.Context) certcrypto.KeyType { func getKeyType(ctx *cli.Context) certcrypto.KeyType {
keyType := ctx.String("key-type") keyType := ctx.String(flgKeyType)
switch strings.ToUpper(keyType) { switch strings.ToUpper(keyType) {
case "RSA2048": case "RSA2048":
return certcrypto.RSA2048 return certcrypto.RSA2048
@ -83,15 +83,15 @@ func getKeyType(ctx *cli.Context) certcrypto.KeyType {
} }
func getEmail(ctx *cli.Context) string { func getEmail(ctx *cli.Context) string {
email := ctx.String("email") email := ctx.String(flgEmail)
if email == "" { if email == "" {
log.Fatal("You have to pass an account (email address) to the program using --email or -m") log.Fatalf("You have to pass an account (email address) to the program using --%s or -m", flgEmail)
} }
return email return email
} }
func getUserAgent(ctx *cli.Context) string { func getUserAgent(ctx *cli.Context) string {
return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", ctx.String("user-agent"), ctx.App.Version)) return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", ctx.String(flgUserAgent), ctx.App.Version))
} }
func createNonExistingFolder(path string) error { func createNonExistingFolder(path string) error {

View file

@ -1,7 +1,7 @@
package cmd package cmd
import ( import (
"errors" "fmt"
"net" "net"
"strings" "strings"
"time" "time"
@ -20,25 +20,25 @@ import (
) )
func setupChallenges(ctx *cli.Context, client *lego.Client) { func setupChallenges(ctx *cli.Context, client *lego.Client) {
if !ctx.Bool("http") && !ctx.Bool("tls") && !ctx.IsSet("dns") { if !ctx.Bool(flgHTTP) && !ctx.Bool(flgTLS) && !ctx.IsSet(flgDNS) {
log.Fatal("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.") log.Fatalf("No challenge selected. You must specify at least one challenge: `--%s`, `--%s`, `--%s`.", flgHTTP, flgTLS, flgDNS)
} }
if ctx.Bool("http") { if ctx.Bool(flgHTTP) {
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx)) err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
if ctx.Bool("tls") { if ctx.Bool(flgTLS) {
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx)) err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
if ctx.IsSet("dns") { if ctx.IsSet(flgDNS) {
err := setupDNS(ctx, client) err := setupDNS(ctx, client)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -49,28 +49,28 @@ func setupChallenges(ctx *cli.Context, client *lego.Client) {
//nolint:gocyclo // the complexity is expected. //nolint:gocyclo // the complexity is expected.
func setupHTTPProvider(ctx *cli.Context) challenge.Provider { func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
switch { switch {
case ctx.IsSet("http.webroot"): case ctx.IsSet(flgHTTPWebroot):
ps, err := webroot.NewHTTPProvider(ctx.String("http.webroot")) ps, err := webroot.NewHTTPProvider(ctx.String(flgHTTPWebroot))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return ps return ps
case ctx.IsSet("http.memcached-host"): case ctx.IsSet(flgHTTPMemcachedHost):
ps, err := memcached.NewMemcachedProvider(ctx.StringSlice("http.memcached-host")) ps, err := memcached.NewMemcachedProvider(ctx.StringSlice(flgHTTPMemcachedHost))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return ps return ps
case ctx.IsSet("http.s3-bucket"): case ctx.IsSet(flgHTTPS3Bucket):
ps, err := s3.NewHTTPProvider(ctx.String("http.s3-bucket")) ps, err := s3.NewHTTPProvider(ctx.String(flgHTTPS3Bucket))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return ps return ps
case ctx.IsSet("http.port"): case ctx.IsSet(flgHTTPPort):
iface := ctx.String("http.port") iface := ctx.String(flgHTTPPort)
if !strings.Contains(iface, ":") { if !strings.Contains(iface, ":") {
log.Fatalf("The --http switch only accepts interface:port or :port for its argument.") log.Fatalf("The --%s switch only accepts interface:port or :port for its argument.", flgHTTPPort)
} }
host, port, err := net.SplitHostPort(iface) host, port, err := net.SplitHostPort(iface)
@ -79,13 +79,13 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
} }
srv := http01.NewProviderServer(host, port) srv := http01.NewProviderServer(host, port)
if header := ctx.String("http.proxy-header"); header != "" { if header := ctx.String(flgHTTPProxyHeader); header != "" {
srv.SetProxyHeader(header) srv.SetProxyHeader(header)
} }
return srv return srv
case ctx.Bool("http"): case ctx.Bool(flgHTTP):
srv := http01.NewProviderServer("", "") srv := http01.NewProviderServer("", "")
if header := ctx.String("http.proxy-header"); header != "" { if header := ctx.String(flgHTTPProxyHeader); header != "" {
srv.SetProxyHeader(header) srv.SetProxyHeader(header)
} }
return srv return srv
@ -97,10 +97,10 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
func setupTLSProvider(ctx *cli.Context) challenge.Provider { func setupTLSProvider(ctx *cli.Context) challenge.Provider {
switch { switch {
case ctx.IsSet("tls.port"): case ctx.IsSet(flgTLSPort):
iface := ctx.String("tls.port") iface := ctx.String(flgTLSPort)
if !strings.Contains(iface, ":") { if !strings.Contains(iface, ":") {
log.Fatalf("The --tls switch only accepts interface:port or :port for its argument.") log.Fatalf("The --%s switch only accepts interface:port or :port for its argument.", flgTLSPort)
} }
host, port, err := net.SplitHostPort(iface) host, port, err := net.SplitHostPort(iface)
@ -109,7 +109,7 @@ func setupTLSProvider(ctx *cli.Context) challenge.Provider {
} }
return tlsalpn01.NewProviderServer(host, port) return tlsalpn01.NewProviderServer(host, port)
case ctx.Bool("tls"): case ctx.Bool(flgTLS):
return tlsalpn01.NewProviderServer("", "") return tlsalpn01.NewProviderServer("", "")
default: default:
log.Fatal("Invalid HTTP challenge options.") log.Fatal("Invalid HTTP challenge options.")
@ -118,38 +118,38 @@ func setupTLSProvider(ctx *cli.Context) challenge.Provider {
} }
func setupDNS(ctx *cli.Context, client *lego.Client) error { func setupDNS(ctx *cli.Context, client *lego.Client) error {
if ctx.IsSet("dns.disable-cp") && ctx.Bool("dns.disable-cp") && ctx.IsSet("dns.propagation-wait") { if ctx.IsSet(flgDNSDisableCP) && ctx.Bool(flgDNSDisableCP) && ctx.IsSet(flgDNSPropagationWait) {
return errors.New("'dns.disable-cp' and 'dns.propagation-wait' are mutually exclusive") return fmt.Errorf("'%s' and '%s' are mutually exclusive", flgDNSDisableCP, flgDNSPropagationWait)
} }
wait := ctx.Duration("dns.propagation-wait") wait := ctx.Duration(flgDNSPropagationWait)
if wait < 0 { if wait < 0 {
return errors.New("'dns.propagation-wait' cannot be negative") return fmt.Errorf("'%s' cannot be negative", flgDNSPropagationWait)
} }
provider, err := dns.NewDNSChallengeProviderByName(ctx.String("dns")) provider, err := dns.NewDNSChallengeProviderByName(ctx.String(flgDNS))
if err != nil { if err != nil {
return err return err
} }
servers := ctx.StringSlice("dns.resolvers") servers := ctx.StringSlice(flgDNSResolvers)
err = client.Challenge.SetDNS01Provider(provider, err = client.Challenge.SetDNS01Provider(provider,
dns01.CondOption(len(servers) > 0, dns01.CondOption(len(servers) > 0,
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.StringSlice("dns.resolvers")))), dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.StringSlice(flgDNSResolvers)))),
dns01.CondOption(ctx.Bool("dns.disable-cp"), dns01.CondOption(ctx.Bool(flgDNSDisableCP),
dns01.DisableCompletePropagationRequirement()), dns01.DisableCompletePropagationRequirement()),
dns01.CondOption(ctx.IsSet("dns.propagation-wait"), dns01.WrapPreCheck( dns01.CondOption(ctx.IsSet(flgDNSPropagationWait), dns01.WrapPreCheck(
func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) { func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
time.Sleep(wait) time.Sleep(wait)
return true, nil return true, nil
}, },
)), )),
dns01.CondOption(ctx.IsSet("dns-timeout"), dns01.CondOption(ctx.IsSet(flgDNSTimeout),
dns01.AddDNSTimeout(time.Duration(ctx.Int("dns-timeout"))*time.Second)), dns01.AddDNSTimeout(time.Duration(ctx.Int(flgDNSTimeout))*time.Second)),
) )
return err return err