Skip to content

Commit

Permalink
feat(conf) improved lua_ssl_trusted_certificate (#6342)
Browse files Browse the repository at this point in the history
Adds two features to the `lua_ssl_trusted_certificate` option:

* It is now a comma-separated list instead of a single item. All the paths on the list are concatenated into a single file in a fixed location (`$PREFIX/.ca_combined`).
* It accepts a special system value, which is expanded to the "system default". This follows a very simple heuristic to try to use the most common cert in most popular distros. `system` can be used in conjunction with other paths.

Note that this PR involves modifying Kong's template - as a result, the `UPGRADE.md` file should include such changes when a new version is released.
  • Loading branch information
kikito committed Sep 28, 2020
1 parent 2186e56 commit 1e6afa1
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 31 deletions.
32 changes: 26 additions & 6 deletions kong.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -1224,12 +1224,32 @@
# See the lua-nginx-module documentation for more information:
# https://github.com/openresty/lua-nginx-module

#lua_ssl_trusted_certificate = # Absolute path to the certificate
# authority file for Lua cosockets in PEM
# format. This certificate will be the one
# used for verifying Kong's database
# connections, when `pg_ssl_verify` or
# `cassandra_ssl_verify` are enabled.

#lua_ssl_trusted_certificate = # Comma-separated list of paths to certificate
# authority files for Lua cosockets in PEM format.
#
# The special value `system` attempts to search for the
# "usual default" provided by each distro, according
# to an arbitrary heuristic. In the current implementation,
# The following pathnames will be tested in order,
# and the first one found will be used:
#
# * /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo)
# * /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6)
# * /etc/ssl/ca-bundle.pem (OpenSUSE)
# * /etc/pki/tls/cacert.pem (OpenELEC)
# * /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7)
# * /etc/ssl/cert.pem (OpenBSD, Alpine)
#
# If no file is found on any of these paths, an error will
# be raised.
#
# `system` can be used by itself or in conjunction with other
# CA filepaths.
#
# When `pg_ssl_verify` or `cassandra_ssl_verify`
# are enabled, these certificate authority files will be
# used for verifying Kong's database connections.
#
# See https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate

Expand Down
24 changes: 24 additions & 0 deletions kong/cmd/utils/prefix_handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ local function gen_default_ssl_cert(kong_config, target)
return true
end


local function gen_trusted_certs_combined_file(combined_filepath, paths)

log.verbose("generating trusted certs combined file in ",
combined_filepath)

local fd = assert(io.open(combined_filepath, "w"))

for _, path in ipairs(paths) do
fd:write(pl_file.read(path))
fd:write("\n")
end

io.close(fd)
end


local function get_ulimit()
local ok, _, stdout, stderr = pl_utils.executeex "ulimit -n"
if not ok then
Expand Down Expand Up @@ -304,6 +321,13 @@ local function prepare_prefix(kong_config, nginx_custom_template_path)
kong_config.status_ssl_cert_key = kong_config.status_ssl_cert_key_default
end

if kong_config.lua_ssl_trusted_certificate_combined then
gen_trusted_certs_combined_file(
kong_config.lua_ssl_trusted_certificate_combined,
kong_config.lua_ssl_trusted_certificate
)
end

-- check ulimit
local ulimit, err = get_ulimit()
if not ulimit then return nil, err
Expand Down
39 changes: 32 additions & 7 deletions kong/conf_loader/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ local CONF_INFERENCES = {
deprecated = { replacement = false }
},

lua_ssl_trusted_certificate = { typ = "array" },
lua_ssl_verify_depth = { typ = "number" },
lua_socket_pool_size = { typ = "number" },

Expand Down Expand Up @@ -801,11 +802,31 @@ local function check_and_infer(conf, opts)
end
end

if conf.lua_ssl_trusted_certificate and
not pl_path.exists(conf.lua_ssl_trusted_certificate)
then
errors[#errors + 1] = "lua_ssl_trusted_certificate: no such file at " ..
conf.lua_ssl_trusted_certificate
if conf.lua_ssl_trusted_certificate then
local new_paths = {}

for i, path in ipairs(conf.lua_ssl_trusted_certificate) do
if path == "system" then
local system_path, err = utils.get_system_trusted_certs_filepath()
if system_path then
path = system_path

else
errors[#errors + 1] =
"lua_ssl_trusted_certificate: unable to locate system bundle - " ..
err
end
end

if not pl_path.exists(path) then
errors[#errors + 1] = "lua_ssl_trusted_certificate: no such file at " ..
path
end

new_paths[i] = path
end

conf.lua_ssl_trusted_certificate = new_paths
end

if conf.ssl_cipher_suite ~= "custom" then
Expand Down Expand Up @@ -1513,9 +1534,13 @@ local function load(path, custom_conf, opts)
conf.admin_ssl_cert_key = pl_path.abspath(conf.admin_ssl_cert_key)
end

if conf.lua_ssl_trusted_certificate then
if conf.lua_ssl_trusted_certificate
and #conf.lua_ssl_trusted_certificate > 0 then
conf.lua_ssl_trusted_certificate =
pl_path.abspath(conf.lua_ssl_trusted_certificate)
tablex.map(pl_path.abspath, conf.lua_ssl_trusted_certificate)

conf.lua_ssl_trusted_certificate_combined =
pl_path.abspath(pl_path.join(conf.prefix, ".ca_combined"))
end

if conf.cluster_cert and conf.cluster_cert_key then
Expand Down
2 changes: 1 addition & 1 deletion kong/db/strategies/cassandra/connector.lua
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function CassandraConnector.new(kong_config)
max_schema_consensus_wait = kong_config.cassandra_schema_consensus_timeout,
ssl = kong_config.cassandra_ssl,
verify = kong_config.cassandra_ssl_verify,
cafile = kong_config.lua_ssl_trusted_certificate,
cafile = kong_config.lua_ssl_trusted_certificate_combined,
lock_timeout = 30,
silent = ngx.IS_CLI,
}
Expand Down
4 changes: 2 additions & 2 deletions kong/db/strategies/postgres/connector.lua
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ function _M.new(kong_config)
schema = kong_config.pg_schema or "",
ssl = kong_config.pg_ssl,
ssl_verify = kong_config.pg_ssl_verify,
cafile = kong_config.lua_ssl_trusted_certificate,
cafile = kong_config.lua_ssl_trusted_certificate_combined,
sem_max = kong_config.pg_max_concurrent_queries or 0,
sem_timeout = (kong_config.pg_semaphore_timeout or 60000) / 1000,
}
Expand Down Expand Up @@ -962,7 +962,7 @@ function _M.new(kong_config)
schema = kong_config.pg_ro_schema,
ssl = kong_config.pg_ro_ssl,
ssl_verify = kong_config.pg_ro_ssl_verify,
cafile = kong_config.lua_ssl_trusted_certificate,
cafile = kong_config.lua_ssl_trusted_certificate_combined,
sem_max = kong_config.pg_ro_max_concurrent_queries,
sem_timeout = kong_config.pg_ro_semaphore_timeout and
(kong_config.pg_ro_semaphore_timeout / 1000) or nil,
Expand Down
4 changes: 2 additions & 2 deletions kong/templates/nginx_kong.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ lua_socket_log_errors off;
lua_max_running_timers 4096;
lua_max_pending_timers 16384;
lua_ssl_verify_depth ${{LUA_SSL_VERIFY_DEPTH}};
> if lua_ssl_trusted_certificate then
lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE}}';
> if lua_ssl_trusted_certificate_combined then
lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE_COMBINED}}';
> end
lua_shared_dict kong 5m;
Expand Down
4 changes: 2 additions & 2 deletions kong/templates/nginx_kong_stream.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ lua_socket_log_errors off;
lua_max_running_timers 4096;
lua_max_pending_timers 16384;
lua_ssl_verify_depth ${{LUA_SSL_VERIFY_DEPTH}};
> if lua_ssl_trusted_certificate then
lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE}}';
> if lua_ssl_trusted_certificate_combined then
lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE_COMBINED}}';
> end
lua_shared_dict stream_kong 5m;
Expand Down
31 changes: 29 additions & 2 deletions kong/tools/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ local ffi = require "ffi"
local uuid = require "resty.jit-uuid"
local pl_stringx = require "pl.stringx"
local pl_stringio = require "pl.stringio"
local pl_utils = require "pl.utils"
local pl_path = require "pl.path"
local zlib = require "ffi-zlib"

local C = ffi.C
Expand Down Expand Up @@ -109,8 +111,6 @@ function _M.get_hostname()
end

do
local pl_utils = require "pl.utils"

local _system_infos

function _M.get_system_infos()
Expand All @@ -136,6 +136,33 @@ do
end
end

do
local trusted_certs_paths = {
"/etc/ssl/certs/ca-certificates.crt", -- Debian/Ubuntu/Gentoo
"/etc/pki/tls/certs/ca-bundle.crt", -- Fedora/RHEL 6
"/etc/ssl/ca-bundle.pem", -- OpenSUSE
"/etc/pki/tls/cacert.pem", -- OpenELEC
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", -- CentOS/RHEL 7
"/etc/ssl/cert.pem", -- OpenBSD, Alpine
}

function _M.get_system_trusted_certs_filepath()
for _, path in ipairs(trusted_certs_paths) do
if pl_path.exists(path) then
return path
end
end

return nil,
"Could not find trusted certs file in " ..
"any of the `system`-predefined locations. " ..
"Please install a certs file there or set " ..
"lua_ssl_trusted_certificate to an " ..
"specific filepath instead of `auto`"
end
end


local get_rand_bytes

do
Expand Down
37 changes: 37 additions & 0 deletions spec/01-unit/03-conf_loader_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local conf_loader = require "kong.conf_loader"
local utils = require "kong.tools.utils"
local helpers = require "spec.helpers"
local tablex = require "pl.tablex"
local pl_path = require "pl.path"


local function search_directive(tbl, directive_name, directive_value)
Expand Down Expand Up @@ -741,6 +743,41 @@ describe("Configuration loader", function()
assert.contains("lua_ssl_trusted_certificate: no such file at /path/cert.pem", errors)
assert.is_nil(conf)
end)
it("accepts several CA certs in lua_ssl_trusted_certificate, setting lua_ssl_trusted_certificate_combined", function()
local conf, _, errors = conf_loader(nil, {
lua_ssl_trusted_certificate = "spec/fixtures/kong_spec.crt,spec/fixtures/kong_clustering.crt",
})
assert.is_nil(errors)
assert.same({
pl_path.abspath("spec/fixtures/kong_spec.crt"),
pl_path.abspath("spec/fixtures/kong_clustering.crt"),
}, conf.lua_ssl_trusted_certificate)
assert.matches(".ca_combined", conf.lua_ssl_trusted_certificate_combined)
end)
it("expands the `system` property in lua_ssl_trusted_certificate", function()
local old_gstcf = utils.get_system_trusted_certs_filepath
local old_exists = pl_path.exists
finally(function()
utils.get_system_trusted_certs_filepath = old_gstcf
pl_path.exists = old_exists
end)
local system_path = "spec/fixtures/kong_spec.crt"
utils.get_system_trusted_certs_filepath = function()
return system_path
end
pl_path.exists = function(path)
return path == system_path or old_exists(path)
end

local conf, _, errors = conf_loader(nil, {
lua_ssl_trusted_certificate = "system",
})
assert.is_nil(errors)
assert.same({
pl_path.abspath(system_path),
}, conf.lua_ssl_trusted_certificate)
assert.matches(".ca_combined", conf.lua_ssl_trusted_certificate_combined)
end)
it("resolves SSL cert/key to absolute path", function()
local conf, err = conf_loader(nil, {
ssl_cert = "spec/fixtures/kong_spec.crt",
Expand Down
12 changes: 9 additions & 3 deletions spec/01-unit/04-prefix_handler_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,20 @@ describe("NGINX conf compiler", function()
local kong_nginx_conf = prefix_handler.compile_kong_conf(conf)
assert.not_matches("lua_ssl_trusted_certificate", kong_nginx_conf, nil, true)
end)
it("sets lua_ssl_trusted_certificate", function()
it("sets lua_ssl_trusted_certificate to a combined file (single entry)", function()
local conf = assert(conf_loader(helpers.test_conf_path, {
lua_ssl_trusted_certificate = "spec/fixtures/kong_spec.crt",
}))
local kong_nginx_conf = prefix_handler.compile_kong_conf(conf)
assert.matches("lua_ssl_trusted_certificate%s+.*spec/fixtures/kong_spec%.key", kong_nginx_conf)
assert.matches("lua_ssl_trusted_certificate%s+.*ca_combined", kong_nginx_conf)
end)
it("sets lua_ssl_trusted_certificate to a combined file (multiple entries)", function()
local conf = assert(conf_loader(helpers.test_conf_path, {
lua_ssl_trusted_certificate = "spec/fixtures/kong_clustering_ca.crt,spec/fixtures/kong_clustering.crt",
}))
local kong_nginx_conf = prefix_handler.compile_kong_conf(conf)
assert.matches("lua_ssl_trusted_certificate%s+.*ca_combined", kong_nginx_conf)
end)

it("defines the client_max_body_size by default", function()
local conf = assert(conf_loader(nil, {}))
local nginx_conf = prefix_handler.compile_kong_conf(conf)
Expand Down
35 changes: 35 additions & 0 deletions spec/01-unit/05-utils_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local utils = require "kong.tools.utils"
local pl_path = require "pl.path"

describe("Utils", function()

Expand All @@ -25,6 +26,40 @@ describe("Utils", function()
end)
end)

describe("get_system_trusted_certs_filepath()", function()
local old_exists = pl_path.exists
after_each(function()
pl_path.exists = old_exists
end)
local tests = {
Debian = "/etc/ssl/certs/ca-certificates.crt",
Fedora = "/etc/pki/tls/certs/ca-bundle.crt",
OpenSuse = "/etc/ssl/ca-bundle.pem",
OpenElec = "/etc/pki/tls/cacert.pem",
CentOS = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
Alpine = "/etc/ssl/cert.pem",
}

for distro, test_path in pairs(tests) do
it("retrieves the default filepath in " .. distro, function()
pl_path.exists = function(path)
return path == test_path
end
assert.same(test_path, utils.get_system_trusted_certs_filepath())
end)
end

it("errors if file is somewhere else", function()
pl_path.exists = function(path)
return path == "/some/unknown/location.crt"
end

local ok, err = utils.get_system_trusted_certs_filepath()
assert.is_nil(ok)
assert.matches("Could not find trusted certs file", err)
end)
end)

describe("is_valid_uuid()", function()
it("validates UUIDs from jit-uuid", function()
assert.True (utils.is_valid_uuid("cbb297c0-a956-486d-ad1d-f9b42df9465a"))
Expand Down
Loading

0 comments on commit 1e6afa1

Please sign in to comment.