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.
email := getEmail(ctx)
serverURL, err := url.Parse(ctx.String("server"))
serverURL, err := url.Parse(ctx.String(flgServer))
if err != nil {
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)
accountsPath := filepath.Join(rootPath, serverPath)
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) {
// couldn't load account but got a key. Try to look the account up.
config := lego.NewConfig(&Account{key: privateKey})
config.CADirURL = ctx.String("server")
config.CADirURL = ctx.String(flgServer)
config.UserAgent = getUserAgent(ctx)
client, err := lego.NewClient(config)

View file

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

View file

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

View file

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

View file

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

View file

@ -17,6 +17,16 @@ import (
"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 (
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
renewEnvCertDomain = "LEGO_CERT_DOMAIN"
@ -34,68 +44,68 @@ func createRenew() *cli.Command {
Action: renew,
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0
hasCsr := ctx.String("csr") != ""
hasDomains := len(ctx.StringSlice(flgDomains)) > 0
hasCsr := ctx.String(flgCSR) != ""
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 {
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
},
Flags: []cli.Flag{
&cli.IntFlag{
Name: "days",
Name: flgDays,
Value: 30,
Usage: "The number of days left on a certificate to renew it.",
},
&cli.BoolFlag{
Name: "ari-enable",
Name: flgARIEnable,
Usage: "Use the renewalInfo endpoint (draft-ietf-acme-ari) to check if a certificate should be renewed.",
},
&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.",
},
&cli.BoolFlag{
Name: "reuse-key",
Name: flgReuseKey,
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
},
&cli.BoolFlag{
Name: "no-bundle",
Name: flgNoBundle,
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
&cli.BoolFlag{
Name: "must-staple",
Name: flgMustStaple,
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
" Only works if the CSR is generated by lego.",
},
&cli.TimestampFlag{
Name: "not-before",
Name: flgNotBefore,
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
Layout: time.RFC3339,
},
&cli.TimestampFlag{
Name: "not-after",
Name: flgNotAfter,
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
Layout: time.RFC3339,
},
&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." +
" If no match, the default offered chain will be used.",
},
&cli.StringFlag{
Name: "always-deactivate-authorizations",
Name: flgAlwaysDeactivateAuthorizations,
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
&cli.StringFlag{
Name: "renew-hook",
Name: flgRenewHook,
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
},
&cli.BoolFlag{
Name: "no-random-sleep",
Name: flgNoRandomSleep,
Usage: "Do not add a random sleep before the renewal." +
" We do not recommend using this flag if you are doing your renewals in an automated way.",
},
@ -113,12 +123,12 @@ func renew(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx)
bundle := !ctx.Bool("no-bundle")
bundle := !ctx.Bool(flgNoBundle)
meta := map[string]string{renewEnvAccountEmail: account.Email}
// CSR
if ctx.IsSet("csr") {
if ctx.IsSet(flgCSR) {
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 {
domains := ctx.StringSlice("domains")
domains := ctx.StringSlice(flgDomains)
domain := domains[0]
// load the cert resource from 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.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
certificates, err := certsStorage.ReadCertificate(domain, certExt)
if err != nil {
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]
var ariRenewalTime *time.Time
if ctx.Bool("ari-enable") {
if ctx.Bool(flgARIEnable) {
ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client)
if ariRenewalTime != nil {
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
}
@ -164,8 +174,8 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
if ctx.Bool("reuse-key") {
keyBytes, errR := certsStorage.ReadFile(domain, ".key")
if ctx.Bool(flgReuseKey) {
keyBytes, errR := certsStorage.ReadFile(domain, keyExt)
if errR != nil {
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/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
const jitter = 8 * time.Minute
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{
Domains: merge(certDomains, domains),
PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"),
NotBefore: getTime(ctx, "not-before"),
NotAfter: getTime(ctx, "not-after"),
MustStaple: ctx.Bool(flgMustStaple),
NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
}
if ctx.Bool("ari-enable") {
if ctx.Bool(flgARIEnable) {
request.ReplacesCertID, err = certificate.MakeARICertID(cert)
if err != nil {
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)
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 {
csr, err := readCSRFile(ctx.String("csr"))
csr, err := readCSRFile(ctx.String(flgCSR))
if err != nil {
log.Fatal(err)
}
@ -232,7 +242,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
// load the cert resource from 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.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
certificates, err := certsStorage.ReadCertificate(domain, certExt)
if err != nil {
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]
var ariRenewalTime *time.Time
if ctx.Bool("ari-enable") {
if ctx.Bool(flgARIEnable) {
ariRenewalTime = getARIRenewalTime(ctx, cert, domain, client)
if ariRenewalTime != nil {
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
}
@ -262,14 +272,14 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
request := certificate.ObtainForCSRRequest{
CSR: csr,
NotBefore: getTime(ctx, "not-before"),
NotAfter: getTime(ctx, "not-after"),
NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
}
if ctx.Bool("ari-enable") {
if ctx.Bool(flgARIEnable) {
request.ReplacesCertID, err = certificate.MakeARICertID(cert)
if err != nil {
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)
return launchHook(ctx.String("renew-hook"), meta)
return launchHook(ctx.String(flgRenewHook), meta)
}
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()
renewalTime := renewalInfo.ShouldRenewAt(now, ctx.Duration("ari-wait-to-renew-duration"))
renewalTime := renewalInfo.ShouldRenewAt(now, ctx.Duration(flgARIWaitToRenewDuration))
if renewalTime == nil {
log.Infof("[%s] acme: renewalInfo endpoint indicates that renewal is not needed", domain)
return nil

View file

@ -6,6 +6,12 @@ import (
"github.com/urfave/cli/v2"
)
// Flag names.
const (
flgKeep = "keep"
flgReason = "reason"
)
func createRevoke() *cli.Command {
return &cli.Command{
Name: "revoke",
@ -13,12 +19,12 @@ func createRevoke() *cli.Command {
Action: revoke,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "keep",
Name: flgKeep,
Aliases: []string{"k"},
Usage: "Keep the certificates after the revocation instead of archiving them.",
},
&cli.UintFlag{
Name: "reason",
Name: flgReason,
Usage: "Identifies the reason for the certificate revocation." +
" See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1." +
" Valid values are:" +
@ -41,15 +47,15 @@ func revoke(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
for _, domain := range ctx.StringSlice("domains") {
for _, domain := range ctx.StringSlice(flgDomains) {
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 {
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)
if err != nil {
@ -58,7 +64,7 @@ func revoke(ctx *cli.Context) error {
log.Println("Certificate was revoked.")
if ctx.Bool("keep") {
if ctx.Bool(flgKeep) {
return nil
}

View file

@ -14,14 +14,25 @@ import (
"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 {
return &cli.Command{
Name: "run",
Usage: "Register an account, then create and install a certificate",
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0
hasCsr := ctx.String("csr") != ""
hasDomains := len(ctx.StringSlice(flgDomains)) > 0
hasCsr := ctx.String(flgCSR) != ""
if hasDomains && hasCsr {
log.Fatal("Please specify either --domains/-d or --csr/-c, but not both")
}
@ -33,35 +44,35 @@ func createRun() *cli.Command {
Action: run,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "no-bundle",
Name: flgNoBundle,
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
&cli.BoolFlag{
Name: "must-staple",
Name: flgMustStaple,
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate." +
" Only works if the CSR is generated by lego.",
},
&cli.TimestampFlag{
Name: "not-before",
Name: flgNotBefore,
Usage: "Set the notBefore field in the certificate (RFC3339 format)",
Layout: time.RFC3339,
},
&cli.TimestampFlag{
Name: "not-after",
Name: flgNotAfter,
Usage: "Set the notAfter field in the certificate (RFC3339 format)",
Layout: time.RFC3339,
},
&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." +
" If no match, the default offered chain will be used.",
},
&cli.StringFlag{
Name: "always-deactivate-authorizations",
Name: flgAlwaysDeactivateAuthorizations,
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
&cli.StringFlag{
Name: "run-hook",
Name: flgRunHook,
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)
return launchHook(ctx.String("run-hook"), meta)
return launchHook(ctx.String(flgRunHook), meta)
}
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
// Check for a global accept override
if ctx.Bool("accept-tos") {
if ctx.Bool(flgAcceptTOS) {
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.")
}
if ctx.Bool("eab") {
kid := ctx.String("kid")
hmacEncoded := ctx.String("hmac")
if ctx.Bool(flgEAB) {
kid := ctx.String(flgKID)
hmacEncoded := ctx.String(flgHMAC)
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{
@ -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) {
bundle := !ctx.Bool("no-bundle")
bundle := !ctx.Bool(flgNoBundle)
domains := ctx.StringSlice("domains")
domains := ctx.StringSlice(flgDomains)
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: bundle,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
MustStaple: ctx.Bool(flgMustStaple),
PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
}
notBefore := ctx.Timestamp("not-before")
notBefore := ctx.Timestamp(flgNotBefore)
if notBefore != nil {
request.NotBefore = *notBefore
}
notAfter := ctx.Timestamp("not-after")
notAfter := ctx.Timestamp(flgNotAfter)
if notAfter != nil {
request.NotAfter = *notAfter
}
@ -200,7 +211,7 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
}
// read the CSR
csr, err := readCSRFile(ctx.String("csr"))
csr, err := readCSRFile(ctx.String(flgCSR))
if err != nil {
return nil, err
}
@ -208,11 +219,11 @@ func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Reso
// obtain a certificate for this CSR
request := certificate.ObtainForCSRRequest{
CSR: csr,
NotBefore: getTime(ctx, "not-before"),
NotAfter: getTime(ctx, "not-after"),
NotBefore: getTime(ctx, flgNotBefore),
NotAfter: getTime(ctx, flgNotAfter),
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
PreferredChain: ctx.String(flgPreferredChain),
AlwaysDeactivateAuthorizations: ctx.Bool(flgAlwaysDeactivateAuthorizations),
}
return client.Certificate.ObtainForCSR(request)

View file

@ -9,163 +9,199 @@ import (
"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 {
return []cli.Flag{
&cli.StringSliceFlag{
Name: "domains",
Name: flgDomains,
Aliases: []string{"d"},
Usage: "Add a domain to the process. Can be specified multiple times.",
},
&cli.StringFlag{
Name: "server",
Name: flgServer,
Aliases: []string{"s"},
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.",
Value: lego.LEDirectoryProduction,
},
&cli.BoolFlag{
Name: "accept-tos",
Name: flgAcceptTOS,
Aliases: []string{"a"},
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
},
&cli.StringFlag{
Name: "email",
Name: flgEmail,
Aliases: []string{"m"},
Usage: "Email used for registration and recovery contact.",
},
&cli.StringFlag{
Name: "csr",
Name: flgCSR,
Aliases: []string{"c"},
Usage: "Certificate signing request filename, if an external CSR is to be used.",
},
&cli.BoolFlag{
Name: "eab",
Name: flgEAB,
EnvVars: []string{"LEGO_EAB"},
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
},
&cli.StringFlag{
Name: "kid",
Name: flgKID,
EnvVars: []string{"LEGO_EAB_KID"},
Usage: "Key identifier from External CA. Used for External Account Binding.",
},
&cli.StringFlag{
Name: "hmac",
Name: flgHMAC,
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.",
},
&cli.StringFlag{
Name: "key-type",
Name: flgKeyType,
Aliases: []string{"k"},
Value: "ec256",
Usage: "Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384.",
},
&cli.StringFlag{
Name: "filename",
Name: flgFilename,
Usage: "(deprecated) Filename of the generated certificate.",
},
&cli.StringFlag{
Name: "path",
Name: flgPath,
EnvVars: []string{"LEGO_PATH"},
Usage: "Directory to use for storing the data.",
Value: defaultPath,
},
&cli.BoolFlag{
Name: "http",
Name: flgHTTP,
Usage: "Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges.",
},
&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.",
Value: ":80",
},
&cli.StringFlag{
Name: "http.proxy-header",
Name: flgHTTPProxyHeader,
Usage: "Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy.",
Value: "Host",
},
&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." +
" This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
},
&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.",
},
&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.",
},
&cli.BoolFlag{
Name: "tls",
Name: flgTLS,
Usage: "Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges.",
},
&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.",
Value: ":443",
},
&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.",
},
&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.",
},
&cli.DurationFlag{
Name: "dns.propagation-wait",
Name: flgDNSPropagationWait,
Usage: "By setting this flag, disables all the propagation checks and uses a wait duration instead.",
},
&cli.StringSliceFlag{
Name: "dns.resolvers",
Name: 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." +
" The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
},
&cli.IntFlag{
Name: "http-timeout",
Name: flgHTTPTimeout,
Usage: "Set the HTTP timeout value to a specific value in seconds.",
},
&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.",
Value: 10,
},
&cli.BoolFlag{
Name: "pem",
Name: flgPEM,
Usage: "Generate an additional .pem (base64) file by concatenating the .key and .crt files together.",
},
&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.",
EnvVars: []string{"LEGO_PFX"},
},
&cli.StringFlag{
Name: "pfx.pass",
Name: flgPFXPass,
Usage: "The password used to encrypt the .pfx (PCKS#12) file.",
Value: pkcs12.DefaultPassword,
EnvVars: []string{"LEGO_PFX_PASSWORD"},
},
&cli.StringFlag{
Name: "pfx.format",
Name: flgPFXFormat,
Usage: "The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256.",
Value: "RC2",
EnvVars: []string{"LEGO_PFX_FORMAT"},
},
&cli.IntFlag{
Name: "cert.timeout",
Name: flgCertTimeout,
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
Value: 30,
},
&cli.IntFlag{
Name: "overall-request-limit",
Name: flgOverallRequestLimit,
Usage: "ACME overall requests limit.",
Value: certificate.DefaultOverallRequestLimit,
},
&cli.StringFlag{
Name: "user-agent",
Name: flgUserAgent,
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 {
config := lego.NewConfig(acc)
config.CADirURL = ctx.String("server")
config.CADirURL = ctx.String(flgServer)
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: time.Duration(ctx.Int("cert.timeout")) * time.Second,
OverallRequestLimit: ctx.Int("overall-request-limit"),
Timeout: time.Duration(ctx.Int(flgCertTimeout)) * time.Second,
OverallRequestLimit: ctx.Int(flgOverallRequestLimit),
}
config.UserAgent = getUserAgent(ctx)
if ctx.IsSet("http-timeout") {
config.HTTPClient.Timeout = time.Duration(ctx.Int("http-timeout")) * time.Second
if ctx.IsSet(flgHTTPTimeout) {
config.HTTPClient.Timeout = time.Duration(ctx.Int(flgHTTPTimeout)) * time.Second
}
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)
}
if client.GetExternalAccountRequired() && !ctx.IsSet("eab") {
log.Fatal("Server requires External Account Binding. Use --eab with --kid and --hmac.")
if client.GetExternalAccountRequired() && !ctx.IsSet(flgEAB) {
log.Fatalf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC)
}
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.
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
keyType := ctx.String("key-type")
keyType := ctx.String(flgKeyType)
switch strings.ToUpper(keyType) {
case "RSA2048":
return certcrypto.RSA2048
@ -83,15 +83,15 @@ func getKeyType(ctx *cli.Context) certcrypto.KeyType {
}
func getEmail(ctx *cli.Context) string {
email := ctx.String("email")
email := ctx.String(flgEmail)
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
}
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 {

View file

@ -1,7 +1,7 @@
package cmd
import (
"errors"
"fmt"
"net"
"strings"
"time"
@ -20,25 +20,25 @@ import (
)
func setupChallenges(ctx *cli.Context, client *lego.Client) {
if !ctx.Bool("http") && !ctx.Bool("tls") && !ctx.IsSet("dns") {
log.Fatal("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
if !ctx.Bool(flgHTTP) && !ctx.Bool(flgTLS) && !ctx.IsSet(flgDNS) {
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))
if err != nil {
log.Fatal(err)
}
}
if ctx.Bool("tls") {
if ctx.Bool(flgTLS) {
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
if err != nil {
log.Fatal(err)
}
}
if ctx.IsSet("dns") {
if ctx.IsSet(flgDNS) {
err := setupDNS(ctx, client)
if err != nil {
log.Fatal(err)
@ -49,28 +49,28 @@ func setupChallenges(ctx *cli.Context, client *lego.Client) {
//nolint:gocyclo // the complexity is expected.
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.IsSet("http.webroot"):
ps, err := webroot.NewHTTPProvider(ctx.String("http.webroot"))
case ctx.IsSet(flgHTTPWebroot):
ps, err := webroot.NewHTTPProvider(ctx.String(flgHTTPWebroot))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.memcached-host"):
ps, err := memcached.NewMemcachedProvider(ctx.StringSlice("http.memcached-host"))
case ctx.IsSet(flgHTTPMemcachedHost):
ps, err := memcached.NewMemcachedProvider(ctx.StringSlice(flgHTTPMemcachedHost))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.s3-bucket"):
ps, err := s3.NewHTTPProvider(ctx.String("http.s3-bucket"))
case ctx.IsSet(flgHTTPS3Bucket):
ps, err := s3.NewHTTPProvider(ctx.String(flgHTTPS3Bucket))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.port"):
iface := ctx.String("http.port")
case ctx.IsSet(flgHTTPPort):
iface := ctx.String(flgHTTPPort)
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)
@ -79,13 +79,13 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
}
srv := http01.NewProviderServer(host, port)
if header := ctx.String("http.proxy-header"); header != "" {
if header := ctx.String(flgHTTPProxyHeader); header != "" {
srv.SetProxyHeader(header)
}
return srv
case ctx.Bool("http"):
case ctx.Bool(flgHTTP):
srv := http01.NewProviderServer("", "")
if header := ctx.String("http.proxy-header"); header != "" {
if header := ctx.String(flgHTTPProxyHeader); header != "" {
srv.SetProxyHeader(header)
}
return srv
@ -97,10 +97,10 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.IsSet("tls.port"):
iface := ctx.String("tls.port")
case ctx.IsSet(flgTLSPort):
iface := ctx.String(flgTLSPort)
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)
@ -109,7 +109,7 @@ func setupTLSProvider(ctx *cli.Context) challenge.Provider {
}
return tlsalpn01.NewProviderServer(host, port)
case ctx.Bool("tls"):
case ctx.Bool(flgTLS):
return tlsalpn01.NewProviderServer("", "")
default:
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 {
if ctx.IsSet("dns.disable-cp") && ctx.Bool("dns.disable-cp") && ctx.IsSet("dns.propagation-wait") {
return errors.New("'dns.disable-cp' and 'dns.propagation-wait' are mutually exclusive")
if ctx.IsSet(flgDNSDisableCP) && ctx.Bool(flgDNSDisableCP) && ctx.IsSet(flgDNSPropagationWait) {
return fmt.Errorf("'%s' and '%s' are mutually exclusive", flgDNSDisableCP, flgDNSPropagationWait)
}
wait := ctx.Duration("dns.propagation-wait")
wait := ctx.Duration(flgDNSPropagationWait)
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 {
return err
}
servers := ctx.StringSlice("dns.resolvers")
servers := ctx.StringSlice(flgDNSResolvers)
err = client.Challenge.SetDNS01Provider(provider,
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.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) {
time.Sleep(wait)
return true, nil
},
)),
dns01.CondOption(ctx.IsSet("dns-timeout"),
dns01.AddDNSTimeout(time.Duration(ctx.Int("dns-timeout"))*time.Second)),
dns01.CondOption(ctx.IsSet(flgDNSTimeout),
dns01.AddDNSTimeout(time.Duration(ctx.Int(flgDNSTimeout))*time.Second)),
)
return err