Skip to content

Commit aa018af

Browse files
committed
Shift around some of the dehydrated cleanup logic.
Building on top of #206, some tweaks: - There was similar logic to cleanup dehydrated files both in the hook file (if the hook succeeded) and in the SSL provider (in case the dehydrated call failed). Since this means the files are deleted in either case (success or failure), try to simplify this by shifting the logic to always run in the SSL provider regardless of success status. - For the new delete logic introduced in the SSL provider, switch it to use non-blocking shell execution to prevent the `rm` from potentially blocking (even though this should be quick). - Since dehydrated's files are always being removed now, remove the logic we had in place to handle an edge-case of storage failing, but files remaining in dehydrated's cache, since this scenario is now moot. - Update tests to account for new delete logic..
1 parent 109c31e commit aa018af

File tree

3 files changed

+68
-58
lines changed

3 files changed

+68
-58
lines changed

lib/resty/auto-ssl/servers/hook.lua

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
local parse_openssl_time = require "resty.auto-ssl.utils.parse_openssl_time"
2-
local shell_blocking = require "shell-games"
32

43
-- This server provides an internal-only API for the dehydrated bash hook
54
-- script to call. This allows for storing the tokens or certificates in the
@@ -54,14 +53,6 @@ return function(auto_ssl_instance)
5453
ngx.log(ngx.ERR, "auto-ssl: failed to set cert: ", err)
5554
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
5655
end
57-
-- remove the extra copy of the certificate files in dehydrated's cert directory
58-
assert(string.find(params["domain"], "/") == nil)
59-
assert(string.find(params["domain"], "%.%.") == nil)
60-
local dir = auto_ssl_instance:get("dir") .. "/letsencrypt/certs/" .. params["domain"]
61-
local _, rm_err = shell_blocking.capture_combined({ "rm", "-rf", dir })
62-
if rm_err then
63-
ngx.log(ngx.ERR, "auto-ssl: failed to cleanup certs: ", rm_err)
64-
end
6556
else
6657
ngx.log(ngx.ERR, "auto-ssl: unknown request to hook server: ", path)
6758
return ngx.exit(ngx.HTTP_NOT_FOUND)
Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
local _M = {}
22

3-
local shell_blocking = require "shell-games"
43
local shell_execute = require "resty.auto-ssl.utils.shell_execute"
54

65
function _M.issue_cert(auto_ssl_instance, domain)
@@ -38,16 +37,14 @@ function _M.issue_cert(auto_ssl_instance, domain)
3837
"--hook", lua_root .. "/bin/resty-auto-ssl/letsencrypt_hooks",
3938
})
4039

40+
-- Cleanup dehydrated files after running to prevent temp files from piling
41+
-- up. This always runs, regardless of whether or not dehydrated succeeds (in
42+
-- which case the certs should be installed in storage) or dehydrated fails
43+
-- (in which case these files aren't of much additional use).
44+
_M.cleanup(auto_ssl_instance, domain)
45+
4146
if result["status"] ~= 0 then
4247
ngx.log(ngx.ERR, "auto-ssl: dehydrated failed: ", result["command"], " status: ", result["status"], " out: ", result["output"], " err: ", err)
43-
-- remove the created files from dehydrated's cert directory
44-
assert(string.find(domain, "/") == nil)
45-
assert(string.find(domain, "%.%.") == nil)
46-
local dir = auto_ssl_instance:get("dir") .. "/letsencrypt/certs/" .. domain
47-
local _, rm_err = shell_blocking.capture_combined({ "rm", "-rf", dir })
48-
if rm_err then
49-
ngx.log(ngx.ERR, "auto-ssl: failed to cleanup certs: ", rm_err)
50-
end
5148
return nil, "dehydrated failure"
5249
end
5350

@@ -61,40 +58,6 @@ function _M.issue_cert(auto_ssl_instance, domain)
6158
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
6259
end
6360

64-
-- If dehydrated succeeded, but we still don't have any certs in storage, the
65-
-- issue might be that dehydrated succeeded and has local certs cached, but
66-
-- the initial attempt to deploy them and save them into storage failed (eg,
67-
-- storage was temporarily unavailable). If this occurs, try to manually fire
68-
-- the deploy_cert hook again to populate our storage with dehydrated's local
69-
-- copies.
70-
if not cert or not cert["fullchain_pem"] or not cert["privkey_pem"] then
71-
ngx.log(ngx.WARN, "auto-ssl: dehydrated succeeded, but certs still missing from storage - trying to manually copy - domain: " .. domain)
72-
73-
result, err = shell_execute({
74-
"env",
75-
"HOOK_SECRET=" .. hook_secret,
76-
"HOOK_SERVER_PORT=" .. hook_port,
77-
lua_root .. "/bin/resty-auto-ssl/letsencrypt_hooks",
78-
"deploy_cert",
79-
domain,
80-
base_dir .. "/letsencrypt/certs/" .. domain .. "/privkey.pem",
81-
base_dir .. "/letsencrypt/certs/" .. domain .. "/cert.pem",
82-
base_dir .. "/letsencrypt/certs/" .. domain .. "/fullchain.pem",
83-
base_dir .. "/letsencrypt/certs/" .. domain .. "/chain.pem",
84-
math.floor(ngx.now()),
85-
})
86-
if result["status"] ~= 0 then
87-
ngx.log(ngx.ERR, "auto-ssl: dehydrated manual hook.sh failed: ", result["command"], " status: ", result["status"], " out: ", result["output"], " err: ", err)
88-
return nil, "dehydrated failure"
89-
end
90-
91-
-- Try fetching again.
92-
cert, get_cert_err = storage:get_cert(domain)
93-
if get_cert_err then
94-
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
95-
end
96-
end
97-
9861
-- Return error if things are still unexpectedly missing.
9962
if not cert or not cert["fullchain_pem"] or not cert["privkey_pem"] then
10063
return nil, "dehydrated succeeded, but no certs present"
@@ -103,4 +66,15 @@ function _M.issue_cert(auto_ssl_instance, domain)
10366
return cert
10467
end
10568

69+
function _M.cleanup(auto_ssl_instance, domain)
70+
assert(string.find(domain, "/") == nil)
71+
assert(string.find(domain, "%.%.") == nil)
72+
73+
local dir = auto_ssl_instance:get("dir") .. "/letsencrypt/certs/" .. domain
74+
local _, rm_err = shell_execute({ "rm", "-rf", dir })
75+
if rm_err then
76+
ngx.log(ngx.ERR, "auto-ssl: failed to cleanup certs: ", rm_err)
77+
end
78+
end
79+
10680
return _M

spec/sanity_spec.lua

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,52 @@ describe("sanity", function()
197197
assert.Not.matches("[emerg]", error_log, nil, true)
198198
end)
199199

200+
it("cleans up dehydrated files on certificate registration failure", function()
201+
server.start()
202+
203+
local ls_before_result, ls_before_err = shell_blocking.capture_combined({ "ls", "-1", server.current_test_dir .. "/auto-ssl/letsencrypt" })
204+
assert.equal(nil, ls_before_err)
205+
local expected_ls_before = {
206+
"conf.d",
207+
"config",
208+
"locks",
209+
}
210+
if server.dehydrated_cached_accounts then
211+
table.insert(expected_ls_before, "accounts")
212+
end
213+
table.sort(expected_ls_before)
214+
assert.same(expected_ls_before, pl_utils.split(ls_before_result["output"]))
215+
216+
local httpc = http.new()
217+
local _, connect_err = httpc:connect("127.0.0.1", 9443)
218+
assert.equal(nil, connect_err)
219+
220+
local _, ssl_err = httpc:ssl_handshake(nil, "unresolvable-sdjfklsdjf.example", true)
221+
assert.equal("18: self signed certificate", ssl_err)
222+
223+
local error_log = server.read_error_log()
224+
assert.matches("auto-ssl: issuing new certificate for unresolvable-sdjfklsdjf.example", error_log, nil, true)
225+
assert.matches("auto-ssl: dehydrated failed", error_log, nil, true)
226+
assert.matches("auto-ssl: could not get certificate for unresolvable-sdjfklsdjf.example", error_log, nil, true)
227+
assert.Not.matches("[alert]", error_log, nil, true)
228+
assert.Not.matches("[emerg]", error_log, nil, true)
229+
230+
local ls_result, ls_err = shell_blocking.capture_combined({ "ls", "-1", server.current_test_dir .. "/auto-ssl/letsencrypt" })
231+
assert.equal(nil, ls_err)
232+
assert.same({
233+
"accounts",
234+
"certs",
235+
"chains",
236+
"conf.d",
237+
"config",
238+
"locks",
239+
}, pl_utils.split(ls_result["output"]))
240+
241+
local ls_certs_result, ls_certs_err = shell_blocking.capture_combined({ "ls", "-1", server.current_test_dir .. "/auto-ssl/letsencrypt/certs" })
242+
assert.equal(nil, ls_certs_err)
243+
assert.same({}, pl_utils.split(ls_certs_result["output"]))
244+
end)
245+
200246
it("allows for custom logic to control domain name to handle lack of SNI support", function()
201247
server.start({
202248
auto_ssl_pre_new = [[
@@ -378,7 +424,7 @@ describe("sanity", function()
378424
assert.Not.matches("[emerg]", error_log, nil, true)
379425
end)
380426

381-
it("retains dehydrated temporary files if cert deployment fails", function()
427+
it("deletes dehydrated temporary files if cert deployment fails", function()
382428
server.start()
383429

384430
-- Create a directory where the storage file would normally belong so
@@ -429,9 +475,7 @@ describe("sanity", function()
429475

430476
local ls_certs_result, ls_certs_err = shell_blocking.capture_combined({ "ls", "-1", server.current_test_dir .. "/auto-ssl/letsencrypt/certs" })
431477
assert.equal(nil, ls_certs_err)
432-
assert.same({
433-
server.ngrok_hostname,
434-
}, pl_utils.split(ls_certs_result["output"]))
478+
assert.same({}, pl_utils.split(ls_certs_result["output"]))
435479

436480
assert(dir.rmtree(server.current_test_dir .. "/auto-ssl/storage/file/" .. ngx.escape_uri(server.ngrok_hostname .. ":latest")))
437481

@@ -452,8 +496,9 @@ describe("sanity", function()
452496

453497
local error_log = server.nginx_error_log_tail:read()
454498
assert.matches("auto-ssl: issuing new certificate for", error_log, nil, true)
455-
assert.matches("Checking domain name(s) of existing cert... unchanged.", error_log, nil, true)
456-
assert.matches("auto-ssl: dehydrated succeeded, but certs still missing from storage - trying to manually copy", error_log, nil, true)
499+
assert.Not.matches("[error]", error_log, nil, true)
500+
assert.Not.matches("[alert]", error_log, nil, true)
501+
assert.Not.matches("[emerg]", error_log, nil, true)
457502
end
458503

459504
local error_log = server.read_error_log()

0 commit comments

Comments
 (0)