diff --git a/certificate/authorization.go b/certificate/authorization.go index 61c1244d99..5118912f8a 100644 --- a/certificate/authorization.go +++ b/certificate/authorization.go @@ -7,19 +7,10 @@ import ( "github.com/go-acme/lego/v4/log" ) -const ( - // overallRequestLimit is the overall number of request per second - // limited on the "new-reg", "new-authz" and "new-cert" endpoints. - // From the documentation the limitation is 20 requests per second, - // but using 20 as value doesn't work but 18 do. - // https://letsencrypt.org/docs/rate-limits/ - overallRequestLimit = 18 -) - func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authorization, error) { resc, errc := make(chan acme.Authorization), make(chan domainError) - delay := time.Second / overallRequestLimit + delay := time.Second / time.Duration(c.overallRequestLimit) for _, authzURL := range order.Authorizations { time.Sleep(delay) diff --git a/certificate/certificates.go b/certificate/certificates.go index 7e69d1f4e8..fc139937bb 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -22,6 +22,17 @@ import ( "golang.org/x/net/idna" ) +const ( + // DefaultOverallRequestLimit is the overall number of request per second + // limited on the "new-reg", "new-authz" and "new-cert" endpoints. + // From the documentation the limitation is 20 requests per second, + // but using 20 as value doesn't work but 18 do. + // https://letsencrypt.org/docs/rate-limits/ + // ZeroSSL has a limit of 7. + // https://help.zerossl.com/hc/en-us/articles/17864245480093-Advantages-over-Using-Let-s-Encrypt#h_01HT4Z1JCJFJQFJ1M3P7S085Q9 + DefaultOverallRequestLimit = 18 +) + // maxBodySize is the maximum size of body that we will read. const maxBodySize = 1024 * 1024 @@ -94,24 +105,33 @@ type resolver interface { } type CertifierOptions struct { - KeyType certcrypto.KeyType - Timeout time.Duration + KeyType certcrypto.KeyType + Timeout time.Duration + OverallRequestLimit int } // Certifier A service to obtain/renew/revoke certificates. type Certifier struct { - core *api.Core - resolver resolver - options CertifierOptions + core *api.Core + resolver resolver + options CertifierOptions + overallRequestLimit int } // NewCertifier creates a Certifier. func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier { - return &Certifier{ + c := &Certifier{ core: core, resolver: resolver, options: options, } + + c.overallRequestLimit = options.OverallRequestLimit + if c.overallRequestLimit <= 0 { + c.overallRequestLimit = DefaultOverallRequestLimit + } + + return c } // Obtain tries to obtain a single certificate using all domains passed into it. diff --git a/cmd/flags.go b/cmd/flags.go index 1d8ca58e06..cd21c466bb 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -3,6 +3,7 @@ package cmd import ( "time" + "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/lego" "github.com/urfave/cli/v2" "software.sslmate.com/src/go-pkcs12" @@ -154,6 +155,11 @@ func CreateFlags(defaultPath string) []cli.Flag { 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", + Usage: "ACME overall requests limit.", + Value: certificate.DefaultOverallRequestLimit, + }, &cli.StringFlag{ Name: "user-agent", Usage: "Add to the user-agent sent to the CA to identify an application embedding lego-cli", diff --git a/cmd/setup.go b/cmd/setup.go index e07a878003..d2defb13f8 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -38,8 +38,9 @@ func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyTy config.CADirURL = ctx.String("server") config.Certificate = lego.CertificateConfig{ - KeyType: keyType, - Timeout: time.Duration(ctx.Int("cert.timeout")) * time.Second, + KeyType: keyType, + Timeout: time.Duration(ctx.Int("cert.timeout")) * time.Second, + OverallRequestLimit: ctx.Int("overall-request-limit"), } config.UserAgent = getUserAgent(ctx) diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml index b1b4072ca0..6b3d91c0ec 100644 --- a/docs/data/zz_cli_help.toml +++ b/docs/data/zz_cli_help.toml @@ -48,6 +48,7 @@ GLOBAL OPTIONS: --pfx.pass value The password used to encrypt the .pfx (PCKS#12) file. (default: "changeit") [$LEGO_PFX_PASSWORD] --pfx.format value The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256. (default: "RC2") [$LEGO_PFX_FORMAT] --cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30) + --overall-request-limit value ACME overall requests limit. (default: 18) --user-agent value Add to the user-agent sent to the CA to identify an application embedding lego-cli --help, -h show help """ diff --git a/lego/client.go b/lego/client.go index ef72a2889c..1109e1224a 100644 --- a/lego/client.go +++ b/lego/client.go @@ -53,7 +53,7 @@ func NewClient(config *Config) (*Client, error) { solversManager := resolver.NewSolversManager(core) prober := resolver.NewProber(solversManager) - certifier := certificate.NewCertifier(core, prober, certificate.CertifierOptions{KeyType: config.Certificate.KeyType, Timeout: config.Certificate.Timeout}) + certifier := certificate.NewCertifier(core, prober, certificate.CertifierOptions{KeyType: config.Certificate.KeyType, Timeout: config.Certificate.Timeout, OverallRequestLimit: config.Certificate.OverallRequestLimit}) return &Client{ Certificate: certifier, diff --git a/lego/client_config.go b/lego/client_config.go index 27bc1872d7..7ce6394c22 100644 --- a/lego/client_config.go +++ b/lego/client_config.go @@ -61,8 +61,9 @@ func NewConfig(user registration.User) *Config { } type CertificateConfig struct { - KeyType certcrypto.KeyType - Timeout time.Duration + KeyType certcrypto.KeyType + Timeout time.Duration + OverallRequestLimit int } // createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value