Skip to content

Commit b2c0d8d

Browse files
committed
Revamp expiry date extraction. Add test coverage for expiry behavior.
Change the initial implementation in #128 to extract the expiration dates from the certificate files themselves, rather than from the storage timestamps (particularly needed now that we're no longer storing timestamped records in the storage). Also add test coverage to test how missing expiry dates and expirations removals are handled.
1 parent c6cdf95 commit b2c0d8d

File tree

7 files changed

+323
-72
lines changed

7 files changed

+323
-72
lines changed

lib/resty/auto-ssl/init_master.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ end
4747
local function generate_config(auto_ssl_instance)
4848
local base_dir = auto_ssl_instance:get("dir")
4949

50+
local _, _, tmp_mkdir_err = run_command("mkdir -p " .. base_dir .. "/tmp")
51+
if tmp_mkdir_err then
52+
ngx.log(ngx.ERR, "auto-ssl: failed to create tmp dir: ", tmp_mkdir_err)
53+
end
54+
55+
local _, _, tmp_chmod_err = run_command("chmod 777 " .. base_dir .. "/tmp")
56+
if tmp_chmod_err then
57+
ngx.log(ngx.ERR, "auto-ssl: failed to create tmp dir permissions: ", tmp_chmod_err)
58+
end
59+
5060
local _, _, mkdir_err = run_command("umask 0022 && mkdir -p " .. base_dir .. "/letsencrypt/conf.d")
5161
if mkdir_err then
5262
ngx.log(ngx.ERR, "auto-ssl: failed to create letsencrypt/conf.d dir: ", mkdir_err)

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

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,32 +86,35 @@ local function renew_check_cert(auto_ssl_instance, storage, domain)
8686
return
8787
end
8888

89-
-- If we don't have an expiry date yet, try to update the stored cert
90-
-- with a date based on backup timestamps
89+
-- While newer certs should have the expire date stored already, if an older
90+
-- cert doesn't have an expiry date stored yet, extract it and save it.
9191
if not cert["expiry"] then
92-
local keys, err = storage.adapter:keys_with_prefix(domain .. ":")
93-
if err then
94-
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate backups from storage for ", domain, ": ", err)
92+
local cert_pem_path = auto_ssl_instance:get("dir") .. "/tmp/extract-expiry-" .. ngx.escape_uri(domain)
93+
local file, file_err = io.open(cert_pem_path, "w")
94+
if file_err then
95+
ngx.log(ngx.ERR, "auto-ssl: write expiry cert file for " .. domain .. " failed: ", file_err)
9596
else
96-
local most_recent = 0
97-
for _, key in ipairs(keys) do
98-
local timestamp = string.sub(key, string.find(key, ":") + 1)
99-
timestamp = tonumber(timestamp)
100-
if timestamp and most_recent < timestamp then
101-
most_recent = timestamp
97+
file:write(cert["fullchain_pem"])
98+
file:close()
99+
100+
local _, date_output, date_err = run_command('date --date="$(openssl x509 -enddate -noout -in "' .. cert_pem_path .. '"|cut -d= -f 2)" +%s')
101+
if date_err then
102+
ngx.log(ngx.ERR, "auto-ssl: failed to extract expiry date from cert: ", date_err)
103+
else
104+
cert["expiry"] = tonumber(date_output)
105+
if cert["expiry"] then
106+
-- Update stored certificate to include expiry information
107+
ngx.log(ngx.NOTICE, "auto-ssl: setting expiration date of ", domain, " to ", cert["expiry"])
108+
local _, set_cert_err = storage:set_cert(domain, cert["fullchain_pem"], cert["privkey_pem"], cert["cert_pem"], cert["expiry"])
109+
if set_cert_err then
110+
ngx.log(ngx.ERR, "auto-ssl: failed to update cert: ", set_cert_err)
111+
end
102112
end
103113
end
104-
if most_recent ~= 0 then
105-
-- Backup timestamp used milliseconds, convert to seconds
106-
cert["expiry"] = math.floor(most_recent/1000) + (90 * 24 * 60 * 60)
107-
-- Update stored certificate to include expiry information
108-
ngx.log(ngx.NOTICE, "auto-ssl: setting expiration date of ", domain, " to ", cert["expiry"])
109-
local _, err = storage:set_cert(domain, cert["fullchain_pem"], cert["privkey_pem"], cert["cert_pem"], cert["expiry"])
110-
if err then
111-
ngx.log(ngx.ERR, "auto-ssl: failed to update cert: ", err)
112-
end
113-
else
114-
ngx.log(ngx.ERR, "auto-ssl: no certificate backups in storage for ", domain, ", unable to set expiration date")
114+
115+
local _, remove_err = os.remove(cert_pem_path)
116+
if remove_err then
117+
ngx.log(ngx.ERR, "auto-ssl: failed to remove expiry cert file: ", remove_err)
115118
end
116119
end
117120
end
@@ -158,15 +161,13 @@ local function renew_check_cert(auto_ssl_instance, storage, domain)
158161
-- configured time for renewals.
159162
local _, issue_err = ssl_provider.issue_cert(auto_ssl_instance, domain)
160163
if issue_err then
161-
ngx.log(ngx.ERR, "auto-ssl: issuing renewal certificate failed: ", err)
164+
ngx.log(ngx.ERR, "auto-ssl: issuing renewal certificate failed: ", issue_err)
165+
162166
-- Give up on renewing this certificate if we didn't manage to renew
163167
-- it before the expiration date
164-
local now = ngx.now()
165-
if cert["expiry"] then
166-
if cert["expiry"] < now then
167-
ngx.log(ngx.NOTICE, "auto-ssl: existing certificate is expired, deleting: ", domain)
168-
storage:delete_cert(domain)
169-
end
168+
if cert["expiry"] and cert["expiry"] < ngx.now() then
169+
ngx.log(ngx.WARN, "auto-ssl: existing certificate is expired, deleting: ", domain)
170+
storage:delete_cert(domain)
170171
end
171172
end
172173

lib/resty/auto-ssl/storage.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function _M.set_cert(self, domain, fullchain_pem, privkey_pem, cert_pem, expiry)
5050
fullchain_pem = fullchain_pem,
5151
privkey_pem = privkey_pem,
5252
cert_pem = cert_pem,
53-
expiry = expiry,
53+
expiry = tonumber(expiry),
5454
})
5555
if err then
5656
return nil, err

lib/resty/auto-ssl/storage_adapters/file.lua

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,6 @@ function _M.delete(self, key)
7272
end
7373
end
7474

75-
function _M.keys_with_prefix(self, prefix)
76-
local base_dir = self.options["dir"]
77-
local _, output, err = run_command("find " .. base_dir .. "/storage/file -name '" .. ngx.escape_uri(prefix) .. "*'")
78-
if err then
79-
return nil, err
80-
end
81-
82-
local keys = {}
83-
for path in string.gmatch(output, "[^\r\n]+") do
84-
local filename = ngx.re.sub(path, ".*/", "")
85-
local key = ngx.unescape_uri(filename)
86-
table.insert(keys, key)
87-
end
88-
89-
return keys
90-
end
91-
9275
function _M.keys_with_suffix(self, suffix)
9376
local base_dir = self.options["dir"]
9477
local _, output, err = run_command("find " .. base_dir .. "/storage/file -name '*" .. ngx.escape_uri(suffix) .. "'")

lib/resty/auto-ssl/storage_adapters/redis.lua

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -106,30 +106,6 @@ function _M.delete(self, key)
106106
return connection:del(prefixed_key(self, key))
107107
end
108108

109-
function _M.keys_with_prefix(self, prefix)
110-
local connection, connection_err = self:get_connection()
111-
if connection_err then
112-
return false, connection_err
113-
end
114-
115-
local keys, err = connection:keys(prefixed_key(self, prefix .. "*"))
116-
117-
if keys and self.options["prefix"] then
118-
local unprefixed_keys = {}
119-
-- First character past the prefix and a colon
120-
local offset = string.len(self.options["prefix"]) + 2
121-
122-
for _, key in ipairs(keys) do
123-
local unprefixed = string.sub(key, offset)
124-
table.insert(unprefixed_keys, unprefixed)
125-
end
126-
127-
keys = unprefixed_keys
128-
end
129-
130-
return keys, err
131-
end
132-
133109
function _M.keys_with_suffix(self, suffix)
134110
local connection, connection_err = self:get_connection()
135111
if connection_err then

0 commit comments

Comments
 (0)