2018-06-05 13:51:22 +00:00
|
|
|
local ssl = require("ngx.ssl")
|
2018-11-29 11:29:10 +00:00
|
|
|
local re_sub = ngx.re.sub
|
2018-06-05 13:51:22 +00:00
|
|
|
|
|
|
|
local _M = {}
|
|
|
|
|
2019-04-12 05:12:57 +00:00
|
|
|
local DEFAULT_CERT_HOSTNAME = "_"
|
|
|
|
|
2020-02-19 18:41:50 +00:00
|
|
|
local certificate_data = ngx.shared.certificate_data
|
|
|
|
local certificate_servers = ngx.shared.certificate_servers
|
|
|
|
|
2020-02-19 16:08:57 +00:00
|
|
|
local function get_der_cert_and_priv_key(pem_cert_key)
|
2018-06-05 13:51:22 +00:00
|
|
|
local der_cert, der_cert_err = ssl.cert_pem_to_der(pem_cert_key)
|
|
|
|
if not der_cert then
|
2020-02-19 16:08:57 +00:00
|
|
|
return nil, nil, "failed to convert certificate chain from PEM to DER: " .. der_cert_err
|
2018-06-05 13:51:22 +00:00
|
|
|
end
|
|
|
|
|
2020-02-19 16:08:57 +00:00
|
|
|
local der_priv_key, dev_priv_key_err = ssl.priv_key_pem_to_der(pem_cert_key)
|
|
|
|
if not der_priv_key then
|
|
|
|
return nil, nil, "failed to convert private key from PEM to DER: " .. dev_priv_key_err
|
|
|
|
end
|
|
|
|
|
|
|
|
return der_cert, der_priv_key, nil
|
|
|
|
end
|
|
|
|
|
|
|
|
local function set_der_cert_and_key(der_cert, der_priv_key)
|
2018-06-05 13:51:22 +00:00
|
|
|
local set_cert_ok, set_cert_err = ssl.set_der_cert(der_cert)
|
|
|
|
if not set_cert_ok then
|
|
|
|
return "failed to set DER cert: " .. set_cert_err
|
|
|
|
end
|
|
|
|
|
|
|
|
local set_priv_key_ok, set_priv_key_err = ssl.set_der_priv_key(der_priv_key)
|
|
|
|
if not set_priv_key_ok then
|
|
|
|
return "failed to set DER private key: " .. set_priv_key_err
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-19 18:41:50 +00:00
|
|
|
local function get_pem_cert_uid(raw_hostname)
|
2019-07-04 21:30:25 +00:00
|
|
|
local hostname = re_sub(raw_hostname, "\\.$", "", "jo")
|
|
|
|
|
2020-02-19 18:41:50 +00:00
|
|
|
local uid = certificate_servers:get(hostname)
|
|
|
|
if uid then
|
|
|
|
return uid
|
2018-11-29 11:29:10 +00:00
|
|
|
end
|
|
|
|
|
2018-12-11 19:43:26 +00:00
|
|
|
local wildcard_hosatname, _, err = re_sub(hostname, "^[^\\.]+\\.", "*.", "jo")
|
2018-11-29 11:29:10 +00:00
|
|
|
if err then
|
|
|
|
ngx.log(ngx.ERR, "error: ", err)
|
2020-02-19 18:41:50 +00:00
|
|
|
return uid
|
2018-11-29 11:29:10 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if wildcard_hosatname then
|
2020-02-19 18:41:50 +00:00
|
|
|
uid = ngx.shared.certificate_servers:get(wildcard_hosatname)
|
2018-11-29 11:29:10 +00:00
|
|
|
end
|
2020-02-19 18:41:50 +00:00
|
|
|
|
|
|
|
return uid
|
2018-11-29 11:29:10 +00:00
|
|
|
end
|
|
|
|
|
2019-09-25 01:17:02 +00:00
|
|
|
function _M.configured_for_current_request()
|
2020-03-22 01:21:52 +00:00
|
|
|
if ngx.ctx.cert_configured_for_current_request == nil then
|
|
|
|
ngx.ctx.cert_configured_for_current_request = get_pem_cert_uid(ngx.var.host) ~= nil
|
2019-09-24 03:40:47 +00:00
|
|
|
end
|
|
|
|
|
2020-03-22 01:21:52 +00:00
|
|
|
return ngx.ctx.cert_configured_for_current_request
|
2019-09-24 03:40:47 +00:00
|
|
|
end
|
|
|
|
|
2018-06-05 13:51:22 +00:00
|
|
|
function _M.call()
|
|
|
|
local hostname, hostname_err = ssl.server_name()
|
|
|
|
if hostname_err then
|
2019-04-12 05:12:57 +00:00
|
|
|
ngx.log(ngx.ERR, "error while obtaining hostname: " .. hostname_err)
|
2018-06-05 13:51:22 +00:00
|
|
|
end
|
2019-04-12 03:35:37 +00:00
|
|
|
if not hostname then
|
2019-04-13 19:26:48 +00:00
|
|
|
ngx.log(ngx.INFO,
|
|
|
|
"obtained hostname is nil (the client does not support SNI?), falling back to default certificate")
|
2019-04-12 05:12:57 +00:00
|
|
|
hostname = DEFAULT_CERT_HOSTNAME
|
2019-04-12 03:35:37 +00:00
|
|
|
end
|
2018-06-05 13:51:22 +00:00
|
|
|
|
2020-02-19 18:41:50 +00:00
|
|
|
local pem_cert
|
|
|
|
local pem_cert_uid = get_pem_cert_uid(hostname)
|
|
|
|
if not pem_cert_uid then
|
|
|
|
pem_cert_uid = get_pem_cert_uid(DEFAULT_CERT_HOSTNAME)
|
|
|
|
end
|
|
|
|
if pem_cert_uid then
|
|
|
|
pem_cert = certificate_data:get(pem_cert_uid)
|
2018-06-05 13:51:22 +00:00
|
|
|
end
|
2020-02-19 18:41:50 +00:00
|
|
|
if not pem_cert then
|
2019-04-12 05:12:57 +00:00
|
|
|
ngx.log(ngx.ERR, "certificate not found, falling back to fake certificate for hostname: " .. tostring(hostname))
|
2019-04-12 03:35:37 +00:00
|
|
|
return
|
|
|
|
end
|
2018-06-05 13:51:22 +00:00
|
|
|
|
|
|
|
local clear_ok, clear_err = ssl.clear_certs()
|
|
|
|
if not clear_ok then
|
|
|
|
ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates: " .. clear_err)
|
|
|
|
return ngx.exit(ngx.ERROR)
|
|
|
|
end
|
|
|
|
|
2020-02-19 18:41:50 +00:00
|
|
|
local der_cert, der_priv_key, der_err = get_der_cert_and_priv_key(pem_cert)
|
2020-02-19 16:08:57 +00:00
|
|
|
if der_err then
|
|
|
|
ngx.log(ngx.ERR, der_err)
|
2018-06-05 13:51:22 +00:00
|
|
|
return ngx.exit(ngx.ERROR)
|
|
|
|
end
|
2020-02-19 16:08:57 +00:00
|
|
|
|
|
|
|
local set_der_err = set_der_cert_and_key(der_cert, der_priv_key)
|
|
|
|
if set_der_err then
|
|
|
|
ngx.log(ngx.ERR, set_der_err)
|
|
|
|
return ngx.exit(ngx.ERROR)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- TODO: based on `der_cert` find OCSP responder URL
|
|
|
|
-- make OCSP request and POST it there and get the response and staple it to
|
|
|
|
-- the current SSL connection if OCSP stapling is enabled
|
2018-06-05 13:51:22 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return _M
|