Skip to content

Commit 9703684

Browse files
committed
Make JSON library configurable.
This sets up the JSON encoding/decoding so you can use a configurable library, using cjson as the default, but allowing optional usage of dkjson for a pure-lua library (in environments like ARM where cjson may be more difficult to get setup). We keep cjson as the default (since we assume its bundled with OpenResty), and don't add an explicit dependency for dkjson (since it's not available on OPM, and trying to keep that in mind for future installation). But if you manually add dkjson then the dkjson adapter should work. See #85
1 parent 181c9b3 commit 9703684

File tree

15 files changed

+483
-30
lines changed

15 files changed

+483
-30
lines changed

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ install:
2828
install -m 644 lib/resty/auto-ssl/init_master.lua $(INST_LUADIR)/resty/auto-ssl/init_master.lua
2929
install -m 644 lib/resty/auto-ssl/init_worker.lua $(INST_LUADIR)/resty/auto-ssl/init_worker.lua
3030
install -d $(INST_LUADIR)/resty/auto-ssl/jobs
31+
install -d $(INST_LUADIR)/resty/auto-ssl/json_adapters
32+
install -m 644 lib/resty/auto-ssl/json_adapters/cjson.lua $(INST_LUADIR)/resty/auto-ssl/json_adapters/cjson.lua
33+
install -m 644 lib/resty/auto-ssl/json_adapters/dkjson.lua $(INST_LUADIR)/resty/auto-ssl/json_adapters/dkjson.lua
3134
install -m 644 lib/resty/auto-ssl/jobs/renewal.lua $(INST_LUADIR)/resty/auto-ssl/jobs/renewal.lua
3235
install -d $(INST_LUADIR)/resty/auto-ssl/servers
3336
install -m 644 lib/resty/auto-ssl/servers/challenge.lua $(INST_LUADIR)/resty/auto-ssl/servers/challenge.lua
@@ -98,6 +101,9 @@ TEST_LUA_LIB_DIR:=$(TEST_VENDOR_DIR)/lib/lua/5.1
98101
LUACHECK:=luacheck
99102
LUACHECK_VERSION:=0.19.1-1
100103

104+
DKJSON:=dkjson
105+
DKJSON_VERSION:=2.5-2
106+
101107
OPENSSL_VERSION:=1.0.2k
102108
OPENSSL:=openssl-$(OPENSSL_VERSION)
103109

@@ -132,6 +138,9 @@ $(TEST_VENDOR_DIR):
132138
$(TEST_LUAROCKS_DIR)/$(LUACHECK)/$(LUACHECK_VERSION): $(TEST_TMP_DIR)/stamp-$(LUAROCKS) | $(TEST_VENDOR_DIR)
133139
$(call test_luarocks_install,LUACHECK)
134140

141+
$(TEST_LUAROCKS_DIR)/$(DKJSON)/$(DKJSON_VERSION): $(TEST_TMP_DIR)/stamp-$(LUAROCKS) | $(TEST_VENDOR_DIR)
142+
$(call test_luarocks_install,DKJSON)
143+
135144
$(TEST_TMP_DIR)/cpanm: | $(TEST_TMP_DIR)
136145
curl -o $@ -L http://cpanmin.us
137146
chmod +x $@
@@ -193,6 +202,7 @@ $(TEST_TMP_DIR)/stamp-$(LUAROCKS): $(TEST_TMP_DIR)/stamp-$(OPENRESTY) | $(TEST_T
193202

194203
test_dependencies: \
195204
$(TEST_LUAROCKS_DIR)/$(LUACHECK)/$(LUACHECK_VERSION) \
205+
$(TEST_LUAROCKS_DIR)/$(DKJSON)/$(DKJSON_VERSION) \
196206
$(TEST_VENDOR_DIR)/$(NGROK)/ngrok \
197207
$(TEST_TMP_DIR)/stamp-$(OPENRESTY) \
198208
$(TEST_TMP_DIR)/stamp-$(LUAROCKS) \

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,20 @@ Additional configuration options can be set on the `auto_ssl` instance that is c
272272
auto_ssl:set("hook_server_port", 90)
273273
```
274274

275+
- **`json_adapter`**
276+
*Default:* `resty.auto-ssl.json_adapters.cjson`
277+
*Options:* `resty.auto-ssl.json_adapters.cjson`, `resty.auto-ssl.json_adapters.dkjson`
278+
279+
The JSON adapter to use for encoding and decoding JSON. Defaults to using [cjson](https://github.com/openresty/lua-cjson), which is bundled with OpenResty installations and should probably be used in most cases. However, an adapter using the pure Lua [dkjson](https://luarocks.org/modules/dhkolf/dkjson) can be used for environments where cjson may not be available (you will need to manually install the dkjson dependency via luarocks to use this adapter).
280+
281+
cjson and dkjson json adapters are supplied, but custom external adapters may also be specified (the value simply needs to be on the `lua_package_path`).
282+
283+
*Example:*
284+
285+
```lua
286+
auto_ssl:set("json_adapter", "resty.auto-ssl.json_adapters.dkjson")
287+
```
288+
275289
### `ssl_certificate` Configuration
276290

277291
- **`generate_certs`**

lib/resty/auto-ssl.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ function _M.new(options)
3232
options["storage_adapter"] = "resty.auto-ssl.storage_adapters.file"
3333
end
3434

35+
if not options["json_adapter"] then
36+
options["json_adapter"] = "resty.auto-ssl.json_adapters.cjson"
37+
end
38+
3539
if not options["ocsp_stapling_error_level"] then
3640
options["ocsp_stapling_error_level"] = ngx.ERR
3741
end

lib/resty/auto-ssl/init_master.lua

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,20 @@ local function generate_config(auto_ssl_instance)
8080
end
8181

8282
local function setup_storage(auto_ssl_instance)
83-
local adapter = require(auto_ssl_instance:get("storage_adapter"))
84-
local adapter_instance = adapter.new(auto_ssl_instance)
85-
if adapter_instance.setup then
86-
adapter_instance:setup()
83+
local storage_adapter = require(auto_ssl_instance:get("storage_adapter"))
84+
local storage_adapter_instance = storage_adapter.new(auto_ssl_instance)
85+
if storage_adapter_instance.setup then
86+
storage_adapter_instance:setup()
8787
end
8888

89+
local json_adapter = require(auto_ssl_instance:get("json_adapter"))
90+
local json_adapter_instance = json_adapter.new(auto_ssl_instance)
91+
8992
local storage = require "resty.auto-ssl.storage"
90-
local storage_instance = storage.new(adapter_instance)
93+
local storage_instance = storage.new({
94+
storage_adapter = storage_adapter_instance,
95+
json_adapter = json_adapter_instance,
96+
})
9197
auto_ssl_instance:set("storage", storage_instance)
9298
end
9399

lib/resty/auto-ssl/init_worker.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ return function(auto_ssl_instance)
2626
start_sockproc()
2727

2828
local storage = auto_ssl_instance:get("storage")
29-
local adapter = storage.adapter
30-
if adapter.setup_worker then
31-
adapter:setup_worker()
29+
local storage_adapter = storage.storage_adapter
30+
if storage_adapter.setup_worker then
31+
storage_adapter:setup_worker()
3232
end
3333

3434
renewal_job.spawn(auto_ssl_instance)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ local function renew_check_cert(auto_ssl_instance, storage, domain)
6969
end
7070

7171
-- Fetch the current certificate.
72-
local fullchain_pem, _, cert_pem = storage:get_cert(domain)
72+
local fullchain_pem, _, cert_pem, get_cert_err = storage:get_cert(domain)
73+
if get_cert_err then
74+
ngx.log(ngx.ERR, "auto-ssl: renewal error fetching certificate from storage for ", domain, ": ", get_cert_err)
75+
end
76+
7377
if not fullchain_pem then
7478
ngx.log(ngx.ERR, "auto-ssl: attempting to renew certificate for domain without certificates in storage: ", domain)
7579
renew_check_cert_unlock(domain, storage, local_lock, distributed_lock_value)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local cjson = require "cjson.safe"
2+
3+
local _M = {}
4+
5+
function _M.new()
6+
return setmetatable({}, { __index = _M })
7+
end
8+
9+
function _M.encode(_, data)
10+
return cjson.encode(data)
11+
end
12+
13+
function _M.decode(_, string)
14+
return cjson.decode(string)
15+
end
16+
17+
return _M
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
local dkjson = require "dkjson"
2+
3+
local _M = {}
4+
5+
function _M.new()
6+
return setmetatable({}, { __index = _M })
7+
end
8+
9+
function _M.encode(_, data)
10+
return dkjson.encode(data)
11+
end
12+
13+
function _M.decode(_, string)
14+
local data, _, err = dkjson.decode(string)
15+
return data, err
16+
end
17+
18+
return _M

lib/resty/auto-ssl/ssl_certificate.lua

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ local function issue_cert(auto_ssl_instance, storage, domain)
8080

8181
-- After obtaining the local and distributed lock, see if the certificate
8282
-- has already been registered.
83-
fullchain_pem, privkey_pem = storage:get_cert(domain)
83+
fullchain_pem, privkey_pem, _, err = storage:get_cert(domain)
84+
if err then
85+
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", err)
86+
end
87+
8488
if fullchain_pem and privkey_pem then
8589
issue_cert_unlock(domain, storage, local_lock, distributed_lock_value)
8690
return fullchain_pem, privkey_pem
@@ -107,7 +111,11 @@ local function get_cert(auto_ssl_instance, domain, ssl_options)
107111
-- Next, look for the certificate in permanent storage (which can be shared
108112
-- across servers depending on the storage).
109113
local storage = auto_ssl_instance:get("storage")
110-
local fullchain_pem, privkey_pem = storage:get_cert(domain)
114+
local fullchain_pem, privkey_pem, _, get_cert_err = storage:get_cert(domain)
115+
if get_cert_err then
116+
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
117+
end
118+
111119
if fullchain_pem and privkey_pem then
112120
return convert_to_der_and_cache(domain, fullchain_pem, privkey_pem, false)
113121
end

lib/resty/auto-ssl/ssl_providers/lets_encrypt.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ function _M.issue_cert(auto_ssl_instance, domain)
4747
-- The result of running that command should result in the certs being
4848
-- populated in our storage (due to the deploy_cert hook triggering).
4949
local storage = auto_ssl_instance:get("storage")
50-
local fullchain_pem, privkey_pem = storage:get_cert(domain)
50+
local fullchain_pem, privkey_pem, _, get_cert_err = storage:get_cert(domain)
51+
if get_cert_err then
52+
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
53+
end
5154

5255
-- If dehydrated said it succeeded, but we still don't have any certs in
5356
-- storage, the issue is likely that the certs have been deleted out of our
@@ -73,7 +76,10 @@ function _M.issue_cert(auto_ssl_instance, domain)
7376
end
7477

7578
-- Try fetching again.
76-
fullchain_pem, privkey_pem = storage:get_cert(domain)
79+
fullchain_pem, privkey_pem, _, get_cert_err = storage:get_cert(domain)
80+
if get_cert_err then
81+
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
82+
end
7783
end
7884

7985
-- Return error if things are still unexpectedly missing.

lib/resty/auto-ssl/storage.lua

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,35 @@ local str = require "resty.string"
33

44
local _M = {}
55

6-
local cjson = require "cjson"
7-
8-
function _M.new(adapter)
9-
return setmetatable({ adapter = adapter }, { __index = _M })
6+
function _M.new(options)
7+
return setmetatable(options, { __index = _M })
108
end
119

1210
function _M.get_challenge(self, domain, path)
13-
return self.adapter:get(domain .. ":challenge:" .. path)
11+
return self.storage_adapter:get(domain .. ":challenge:" .. path)
1412
end
1513

1614
function _M.set_challenge(self, domain, path, value)
17-
return self.adapter:set(domain .. ":challenge:" .. path, value)
15+
return self.storage_adapter:set(domain .. ":challenge:" .. path, value)
1816
end
1917

2018
function _M.delete_challenge(self, domain, path)
21-
return self.adapter:delete(domain .. ":challenge:" .. path)
19+
return self.storage_adapter:delete(domain .. ":challenge:" .. path)
2220
end
2321

2422
function _M.get_cert(self, domain)
25-
local json, err = self.adapter:get(domain .. ":latest")
23+
local json, err = self.storage_adapter:get(domain .. ":latest")
2624
if err then
2725
return nil, nil, err
2826
elseif not json then
2927
return nil
3028
end
3129

32-
local data = cjson.decode(json)
30+
local data, json_err = self.json_adapter:decode(json)
31+
if json_err then
32+
return nil, nil, nil, json_err
33+
end
34+
3335
return data["fullchain_pem"], data["privkey_pem"], data["cert_pem"]
3436
end
3537

@@ -40,23 +42,26 @@ function _M.set_cert(self, domain, fullchain_pem, privkey_pem, cert_pem)
4042
-- a single string (regardless of implementation), and we don't have to worry
4143
-- about race conditions with the public cert and private key being stored
4244
-- separately and getting out of sync.
43-
local data = cjson.encode({
45+
local string, err = self.json_adapter:encode({
4446
fullchain_pem = fullchain_pem,
4547
privkey_pem = privkey_pem,
4648
cert_pem = cert_pem,
4749
})
50+
if err then
51+
return nil, err
52+
end
4853

4954
-- Store the cert with the current timestamp, so the old certs are preserved
5055
-- in case something goes wrong.
5156
local time = ngx.now() * 1000
52-
self.adapter:set(domain .. ":" .. time, data)
57+
self.storage_adapter:set(domain .. ":" .. time, string)
5358

5459
-- Store the cert under the "latest" alias, which is what this app will use.
55-
return self.adapter:set(domain .. ":latest", data)
60+
return self.storage_adapter:set(domain .. ":latest", string)
5661
end
5762

5863
function _M.all_cert_domains(self)
59-
local keys, err = self.adapter:keys_with_suffix(":latest")
64+
local keys, err = self.storage_adapter:keys_with_suffix(":latest")
6065
if err then
6166
return nil, err
6267
end
@@ -91,7 +96,7 @@ function _M.issue_cert_lock(self, domain)
9196
local sleep_time = 0.5
9297
local max_time = 30
9398
repeat
94-
local existing_value = self.adapter:get(key)
99+
local existing_value = self.storage_adapter:get(key)
95100
if not existing_value then
96101
unlocked = true
97102
else
@@ -101,7 +106,7 @@ function _M.issue_cert_lock(self, domain)
101106
until unlocked or wait_time > max_time
102107

103108
-- Create a new lock.
104-
local ok, err = self.adapter:set(key, lock_rand_value, { exptime = 30 })
109+
local ok, err = self.storage_adapter:set(key, lock_rand_value, { exptime = 30 })
105110
if not ok then
106111
return nil, err
107112
else
@@ -113,9 +118,9 @@ function _M.issue_cert_unlock(self, domain, lock_rand_value)
113118
local key = domain .. ":issue_cert_lock"
114119

115120
-- Remove the existing lock if it matches the expected value.
116-
local current_value, err = self.adapter:get(key)
121+
local current_value, err = self.storage_adapter:get(key)
117122
if lock_rand_value == current_value then
118-
return self.adapter:delete(key)
123+
return self.storage_adapter:delete(key)
119124
elseif current_value then
120125
return false, "lock does not match expected value"
121126
else

t/inc/setup.pl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ sub setup {
1616
$ENV{TEST_NGINX_NGROK_HOSTNAME} = ($ngrok->matchlist())[0] or die "failed to extract hostname for ngrok";
1717
$ENV{TEST_NGINX_RESTY_AUTO_SSL_DIR} ||= "/tmp/resty-auto-ssl-test";
1818
$ENV{TEST_NGINX_RESOLVER} ||= "8.8.8.8 8.8.4.4";
19+
$ENV{TEST_NGINX_LUA_SHARE_DIR} ||= $ENV{TEST_LUA_SHARE_DIR};
1920

2021
# If the tests have previously been run, wipe out any test data.
2122
if(-d $ENV{TEST_NGINX_RESTY_AUTO_SSL_DIR}) {

0 commit comments

Comments
 (0)