Skip to content

Commit e9ab9d3

Browse files
committed
Throttle the rate of renewal requests (ACME v2 rate limit)
The ACME v2 API has a new rate limit of 300 new orders per account per 3 hours, which easily results in the "too many new orders recently" error for new or renewing domains. (see #213) As a first step to mitigate this new requirement, throttle the rate with which renewal requests are issued to a default 60 per hour (which leaves 40 for on-demand issues per hour).
1 parent 9d43c23 commit e9ab9d3

File tree

4 files changed

+28
-0
lines changed

4 files changed

+28
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ How frequently (in seconds) all of the domains should be checked for certificate
183183
auto_ssl:set("renew_check_interval", 172800)
184184
```
185185

186+
### `renewals_per_hour`
187+
*Default:* `60`
188+
189+
How many renewal requests to issue per hour at most. The ACME v2 protocol limits each account to 300 new orders per 3 hours. This setting will throttle the renewal job so that a sufficient margin remains available for new domains at all times. You might consider lowering this setting when the same Let's Encrypt account credentials are shared across multiple servers (in a load-balanced environment).
190+
186191
### `storage_adapter`
187192
*Default:* `resty.auto-ssl.storage_adapters.file`<br>
188193
*Options:* `resty.auto-ssl.storage_adapters.file`, `resty.auto-ssl.storage_adapters.redis`

lib/resty/auto-ssl.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ function _M.new(options)
4444
options["renew_check_interval"] = 86400 -- 1 day
4545
end
4646

47+
if not options["renewals_per_hour"] then
48+
options["renewals_per_hour"] = 60
49+
end
50+
4751
if not options["hook_server_port"] then
4852
options["hook_server_port"] = 8999
4953
end

lib/resty/auto-ssl/jobs/renewal.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ local shuffle_table = require "resty.auto-ssl.utils.shuffle_table"
55
local ssl_provider = require "resty.auto-ssl.ssl_providers.lets_encrypt"
66

77
local _M = {}
8+
local min_renewal_seconds
9+
local last_renewal
810

911
-- Based on lua-rest-upstream-healthcheck's lock:
1012
-- https://github.com/openresty/lua-resty-upstream-healthcheck/blob/v0.03/lib/resty/upstream/healthcheck.lua#L423-L440
@@ -184,6 +186,18 @@ local function renew_check_cert(auto_ssl_instance, storage, domain)
184186
end
185187

186188
renew_check_cert_unlock(domain, storage, local_lock, distributed_lock_value)
189+
190+
-- Throttle renewal requests based on renewals_per_hour setting.
191+
if last_renewal and ngx.now() - last_renewal < min_renewal_seconds then
192+
local to_sleep = min_renewal_seconds - (ngx.now() - last_renewal)
193+
ngx.log(ngx.NOTICE, "auto-ssl: pausing renewal job for " .. to_sleep .. " seconds")
194+
ngx.sleep(to_sleep)
195+
end
196+
if last_renewal then
197+
last_renewal = last_renewal + min_renewal_seconds
198+
else
199+
last_renewal = ngx.now()
200+
end
187201
end
188202

189203
local function renew_all_domains(auto_ssl_instance)
@@ -199,6 +213,10 @@ local function renew_all_domains(auto_ssl_instance)
199213
-- renewal attempts).
200214
shuffle_table(domains)
201215

216+
-- Set up renewal request throttling.
217+
min_renewal_seconds = 3600 / auto_ssl_instance:get("renewals_per_hour")
218+
last_renewal = ngx.now()
219+
202220
for _, domain in ipairs(domains) do
203221
renew_check_cert(auto_ssl_instance, storage, domain)
204222
end

spec/config/nginx.conf.etlua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ http {
3333
allow_domain = function(domain)
3434
return true
3535
end,
36+
renewals_per_hour = 3600,
3637
}
3738
<%- auto_ssl_pre_new or "" %>
3839
auto_ssl = (require "resty.auto-ssl").new(options)

0 commit comments

Comments
 (0)