Skip to content

Commit

Permalink
Expose the redis adapter's redis connection in the allow_domain callback
Browse files Browse the repository at this point in the history
The auto_ssl instance is now passed as the second argument to
the `allow_domain` callback. If using the Redis storage adapter, then
now the Redis connection can be accessed by
`auto_ssl.storage.adapter:get_connection()`. This allows for more easily
accessing the same redis connection in this callback function (or
anywhere else the auto_ssl instance is available).

This rolls back the change in 9703684
to rename the storage adapter instance inside the storage library, so
it continues to just be `adapter` (instead of `storage_adapter`). This
makes this access a bit easier and also keeps backwards compatibility
with the previous release.

See: #38
  • Loading branch information
GUI committed Feb 5, 2018
1 parent 4aed490 commit ebfa5d3
Show file tree
Hide file tree
Showing 13 changed files with 244 additions and 75 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added
- Allow for the Redis `db` number to be configured. Thanks to [@RainFlying](https://github.com/RainFlying). ([#103](https://github.com/GUI/lua-resty-auto-ssl/pull/103))
- Expose the storage adapter instance in the `allow_domain` callback so the Redis connection can be reused. ([#38](https://github.com/GUI/lua-resty-auto-ssl/issues/38))
- Add `generate_certs` option to allow for disabling SSL certification generation within specific server blocks. Thanks to [@mklauber](https://github.com/mklauber). ([#91](https://github.com/GUI/lua-resty-auto-ssl/issues/91), [#92](https://github.com/GUI/lua-resty-auto-ssl/pull/92))
- Add `json_adapter` option for choosing a different JSON encoder/decoder library. Thanks to [@meyskens](https://github.com/meyskens). ([#85](https://github.com/GUI/lua-resty-auto-ssl/pull/85), [#84](https://github.com/GUI/lua-resty-auto-ssl/issues/84))

Expand All @@ -12,6 +13,9 @@
- Only call the `allow_domain` callback if a certificate is not present in shared memory. This may improve efficiency in cases where the `allow_domain` callback is more costly or takes longer. Thanks to [@gohai](https://github.com/gohai). ([#107](https://github.com/GUI/lua-resty-auto-ssl/pull/107))
- Upgrade dehydrated to latest version from master to fix redirect issues on the Let's Encrypt staging server.

### Deprecated
- If accessing the storage object off of the auto-ssl instance, use `auto_ssl.storage` instead of `auto_ssl:get("storage")`.

### Fixed
- Fix renewals when using the file adapter and too many certificate files were present for shell globbing ([#109](https://github.com/GUI/lua-resty-auto-ssl/issues/109))

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,18 @@ http {
Additional configuration options can be set on the `auto_ssl` instance that is created:

### `allow_domain`
*Default:* `function(domain) return false end`
*Default:* `function(domain, auto_ssl) return false end`

A function that determines whether the incoming domain should automatically issue a new SSL certificate.

By default, resty-auto-ssl will not perform any SSL registrations until you define the `allow_domain` function. You may return `true` to handle all possible domains, but be aware that bogus SNI hostnames can then be used to trigger an indefinite number of SSL registration attempts (which will be rejected). A better approach may be to whitelist the allowed domains in some way.

When using the Redis storage adapter, you can access the current Redis connection inside the `allow_domain` callback by accessing `auto_ssl.storage.adapter:get_connection()`.

*Example:*

```lua
auto_ssl:set("allow_domain", function(domain)
auto_ssl:set("allow_domain", function(domain, auto_ssl)
return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)
```
Expand Down
13 changes: 12 additions & 1 deletion lib/resty/auto-ssl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,25 @@ function _M.new(options)
end

function _M.set(self, key, value)
if key == "storage" then
ngx.log(ngx.ERR, "auto-ssl: DEPRECATED: Don't use auto_ssl:set() for the 'storage' instance. Set directly with auto_ssl.storage.")
self.storage = value
return
end

self.options[key] = value
end

function _M.get(self, key)
if key == "storage" then
ngx.log(ngx.ERR, "auto-ssl: DEPRECATED: Don't use auto_ssl:get() for the 'storage' instance. Get directly with auto_ssl.storage.")
return self.storage
end

return self.options[key]
end

function _M.allow_domain(domain) -- luacheck: ignore
function _M.allow_domain(domain, auto_ssl) -- luacheck: ignore
return false
end

Expand Down
4 changes: 2 additions & 2 deletions lib/resty/auto-ssl/init_master.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ local function setup_storage(auto_ssl_instance)

local storage = require "resty.auto-ssl.storage"
local storage_instance = storage.new({
storage_adapter = storage_adapter_instance,
adapter = storage_adapter_instance,
json_adapter = json_adapter_instance,
})
auto_ssl_instance:set("storage", storage_instance)
auto_ssl_instance.storage = storage_instance
end

return function(auto_ssl_instance)
Expand Down
4 changes: 2 additions & 2 deletions lib/resty/auto-ssl/init_worker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ return function(auto_ssl_instance)
-- background process, which would be nice.
start_sockproc()

local storage = auto_ssl_instance:get("storage")
local storage_adapter = storage.storage_adapter
local storage = auto_ssl_instance.storage
local storage_adapter = storage.adapter
if storage_adapter.setup_worker then
storage_adapter:setup_worker()
end
Expand Down
2 changes: 1 addition & 1 deletion lib/resty/auto-ssl/jobs/renewal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ end

local function renew_all_domains(auto_ssl_instance)
-- Loop through all known domains and check to see if they should be renewed.
local storage = auto_ssl_instance:get("storage")
local storage = auto_ssl_instance.storage
local domains, domains_err = storage:all_cert_domains()
if domains_err then
ngx.log(ngx.ERR, "auto-ssl: failed to fetch all certificate domains: ", domains_err)
Expand Down
2 changes: 1 addition & 1 deletion lib/resty/auto-ssl/servers/challenge.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ return function(auto_ssl_instance)

-- Return the challenge value for this token if it's found.
local domain = ngx.var.host
local storage = auto_ssl_instance:get("storage")
local storage = auto_ssl_instance.storage
local value = storage:get_challenge(domain, token_filename)
if value then
ngx.say(value)
Expand Down
2 changes: 1 addition & 1 deletion lib/resty/auto-ssl/servers/hook.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ return function(auto_ssl_instance)
end

local path = ngx.var.request_uri
local storage = auto_ssl_instance:get("storage")
local storage = auto_ssl_instance.storage
if path == "/deploy-challenge" then
assert(params["domain"])
assert(params["token_filename"])
Expand Down
4 changes: 2 additions & 2 deletions lib/resty/auto-ssl/ssl_certificate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ local function get_cert_der(auto_ssl_instance, domain, ssl_options)
-- We may want to consider caching the results of allow_domain lookups
-- (including negative caching or disallowed domains).
local allow_domain = auto_ssl_instance:get("allow_domain")
if not allow_domain(domain) then
if not allow_domain(domain, auto_ssl_instance) then
return nil, "domain not allowed"
end

-- Next, look for the certificate in permanent storage (which can be shared
-- across servers depending on the storage).
local storage = auto_ssl_instance:get("storage")
local storage = auto_ssl_instance.storage
local cert, get_cert_err = storage:get_cert(domain)
if get_cert_err then
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
Expand Down
2 changes: 1 addition & 1 deletion lib/resty/auto-ssl/ssl_providers/lets_encrypt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function _M.issue_cert(auto_ssl_instance, domain)

-- The result of running that command should result in the certs being
-- populated in our storage (due to the deploy_cert hook triggering).
local storage = auto_ssl_instance:get("storage")
local storage = auto_ssl_instance.storage
local cert, get_cert_err = storage:get_cert(domain)
if get_cert_err then
ngx.log(ngx.ERR, "auto-ssl: error fetching certificate from storage for ", domain, ": ", get_cert_err)
Expand Down
26 changes: 15 additions & 11 deletions lib/resty/auto-ssl/storage.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@ local str = require "resty.string"
local _M = {}

function _M.new(options)
assert(options)
assert(options["adapter"])
assert(options["json_adapter"])

return setmetatable(options, { __index = _M })
end

function _M.get_challenge(self, domain, path)
return self.storage_adapter:get(domain .. ":challenge:" .. path)
return self.adapter:get(domain .. ":challenge:" .. path)
end

function _M.set_challenge(self, domain, path, value)
return self.storage_adapter:set(domain .. ":challenge:" .. path, value)
return self.adapter:set(domain .. ":challenge:" .. path, value)
end

function _M.delete_challenge(self, domain, path)
return self.storage_adapter:delete(domain .. ":challenge:" .. path)
return self.adapter:delete(domain .. ":challenge:" .. path)
end

function _M.get_cert(self, domain)
local json, err = self.storage_adapter:get(domain .. ":latest")
local json, err = self.adapter:get(domain .. ":latest")
if err then
return nil, err
elseif not json then
Expand Down Expand Up @@ -55,14 +59,14 @@ function _M.set_cert(self, domain, fullchain_pem, privkey_pem, cert_pem, expiry)
-- Store the cert with the current timestamp, so the old certs are preserved
-- in case something goes wrong.
local time = ngx.now() * 1000
self.storage_adapter:set(domain .. ":" .. time, string)
self.adapter:set(domain .. ":" .. time, string)

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

function _M.all_cert_domains(self)
local keys, err = self.storage_adapter:keys_with_suffix(":latest")
local keys, err = self.adapter:keys_with_suffix(":latest")
if err then
return nil, err
end
Expand Down Expand Up @@ -97,7 +101,7 @@ function _M.issue_cert_lock(self, domain)
local sleep_time = 0.5
local max_time = 30
repeat
local existing_value = self.storage_adapter:get(key)
local existing_value = self.adapter:get(key)
if not existing_value then
unlocked = true
else
Expand All @@ -107,7 +111,7 @@ function _M.issue_cert_lock(self, domain)
until unlocked or wait_time > max_time

-- Create a new lock.
local ok, err = self.storage_adapter:set(key, lock_rand_value, { exptime = 30 })
local ok, err = self.adapter:set(key, lock_rand_value, { exptime = 30 })
if not ok then
return nil, err
else
Expand All @@ -119,9 +123,9 @@ function _M.issue_cert_unlock(self, domain, lock_rand_value)
local key = domain .. ":issue_cert_lock"

-- Remove the existing lock if it matches the expected value.
local current_value, err = self.storage_adapter:get(key)
local current_value, err = self.adapter:get(key)
if lock_rand_value == current_value then
return self.storage_adapter:delete(key)
return self.adapter:delete(key)
elseif current_value then
return false, "lock does not match expected value"
else
Expand Down
100 changes: 50 additions & 50 deletions lib/resty/auto-ssl/storage_adapters/redis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,74 @@ local redis = require "resty.redis"

local _M = {}

local function get_redis_instance(self)
local instance = ngx.ctx.auto_ssl_redis_instance
if instance then
return instance
local function prefixed_key(self, key)
if self.options["prefix"] then
return self.options["prefix"] .. ":" .. key
else
return key
end
end

function _M.new(auto_ssl_instance)
local options = auto_ssl_instance:get("redis") or {}

if not options["host"] then
options["host"] = "127.0.0.1"
end

if not options["port"] then
options["port"] = 6379
end

return setmetatable({ options = options }, { __index = _M })
end

function _M.get_connection(self)
local connection = ngx.ctx.auto_ssl_redis_connection
if connection then
return connection
end

instance = redis:new()
connection = redis:new()
local ok, err

if self.options["socket"] then
ok, err = instance:connect(self.options["socket"])
ok, err = connection:connect(self.options["socket"])
else
ok, err = instance:connect(self.options["host"], self.options["port"])
ok, err = connection:connect(self.options["host"], self.options["port"])
end
if not ok then
return false, err
end

if self.options["auth"] then
ok, err = instance:auth(self.options["auth"])
ok, err = connection:auth(self.options["auth"])
if not ok then
return false, err
end
end

if self.options["db"] then
ok, err = instance:select(self.options["db"])
ok, err = connection:select(self.options["db"])
if not ok then
return false, err
end
end

ngx.ctx.auto_ssl_redis_instance = instance
return instance
end

local function prefixed_key(self, key)
if self.options["prefix"] then
return self.options["prefix"] .. ":" .. key
else
return key
end
end

function _M.new(auto_ssl_instance)
local options = auto_ssl_instance:get("redis") or {}

if not options["host"] then
options["host"] = "127.0.0.1"
end

if not options["port"] then
options["port"] = 6379
end

return setmetatable({ options = options }, { __index = _M })
ngx.ctx.auto_ssl_redis_connection = connection
return connection
end

function _M.setup()
end

function _M.get(self, key)
local redis_instance, instance_err = get_redis_instance(self)
if instance_err then
return nil, instance_err
local connection, connection_err = self:get_connection()
if connection_err then
return nil, connection_err
end

local res, err = redis_instance:get(prefixed_key(self, key))
local res, err = connection:get(prefixed_key(self, key))
if res == ngx.null then
res = nil
end
Expand All @@ -78,16 +78,16 @@ function _M.get(self, key)
end

function _M.set(self, key, value, options)
local redis_instance, instance_err = get_redis_instance(self)
if instance_err then
return false, instance_err
local connection, connection_err = self:get_connection()
if connection_err then
return false, connection_err
end

key = prefixed_key(self, key)
local ok, err = redis_instance:set(key, value)
local ok, err = connection:set(key, value)
if ok then
if options and options["exptime"] then
local _, expire_err = redis_instance:expire(key, options["exptime"])
local _, expire_err = connection:expire(key, options["exptime"])
if expire_err then
ngx.log(ngx.ERR, "auto-ssl: failed to set expire: ", expire_err)
end
Expand All @@ -98,21 +98,21 @@ function _M.set(self, key, value, options)
end

function _M.delete(self, key)
local redis_instance, instance_err = get_redis_instance(self)
if instance_err then
return false, instance_err
local connection, connection_err = self:get_connection()
if connection_err then
return false, connection_err
end

return redis_instance:del(prefixed_key(self, key))
return connection:del(prefixed_key(self, key))
end

function _M.keys_with_suffix(self, suffix)
local redis_instance, instance_err = get_redis_instance(self)
if instance_err then
return false, instance_err
local connection, connection_err = self:get_connection()
if connection_err then
return false, connection_err
end

local keys, err = redis_instance:keys(prefixed_key(self, "*" .. suffix))
local keys, err = connection:keys(prefixed_key(self, "*" .. suffix))

if keys and self.options["prefix"] then
local unprefixed_keys = {}
Expand Down
Loading

0 comments on commit ebfa5d3

Please sign in to comment.