diff --git a/kong/cmd/utils/prefix_handler.lua b/kong/cmd/utils/prefix_handler.lua index 788d0fdce42f..67f9872aaba9 100644 --- a/kong/cmd/utils/prefix_handler.lua +++ b/kong/cmd/utils/prefix_handler.lua @@ -465,12 +465,17 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ end end - -- create certs files and assign paths if needed + -- create certs files and assign paths when they are passed as content do - local function propagate_dhparam(path) - kong_config["nginx_http_ssl_dhparam"] = path - kong_config["nginx_stream_ssl_dhparam"] = path + local function set_dhparam_path(path) + if kong_config["nginx_http_ssl_dhparam"] then + kong_config["nginx_http_ssl_dhparam"] = path + end + + if kong_config["nginx_stream_ssl_dhparam"] then + kong_config["nginx_stream_ssl_dhparam"] = path + end for _, directive in ipairs(kong_config["nginx_http_directives"]) do if directive.name == "ssl_dhparam" and directive.value then @@ -496,7 +501,9 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ }) end - local function write_file_set_path( + -- ensure the property value is a "content" (not a path), + -- write the content to a file and set the path in the configuration + local function write_content_set_path( contents, format, write_func, @@ -513,11 +520,11 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ write_func(path, contents) kong_config[config_key] = path if target == "ssl-dhparam" then - propagate_dhparam(path) + set_dhparam_path(path) end end - else + elseif type(contents) == "table" then for i, content in ipairs(contents) do if not exists(content) then if not exists(ssl_path) then @@ -531,17 +538,16 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ end end + local ssl_path = join(kong_config.prefix, "ssl") for _, target in ipairs({ "proxy", "admin", "status", "client", "cluster", - "ssl-dhparam", "lua-ssl-trusted", "cluster-ca" }) do - local ssl_path = join(kong_config.prefix, "ssl") local cert_name local key_name local ssl_cert @@ -555,8 +561,6 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ key_name = target .. "_cert_key" elseif target == "cluster-ca" then cert_name = "cluster_ca_cert" - elseif target == "ssl-dhparam" then - cert_name = "ssl_dhparam" elseif target == "lua-ssl-trusted" then cert_name = "lua_ssl_trusted_certificate" else @@ -564,24 +568,28 @@ local function prepare_prefix(kong_config, nginx_custom_template_path, skip_writ key_name = target .. "_ssl_cert_key" end - if cert_name and (cert_name ~= "ssl_dhparam" or - not is_predefined_dhgroup(kong_config[cert_name])) then - ssl_cert = kong_config[cert_name] - end + ssl_cert = cert_name and kong_config[cert_name] ssl_key = key_name and kong_config[key_name] if ssl_cert and #ssl_cert > 0 then - write_file_set_path(ssl_cert, ".crt", write_ssl_cert, ssl_path, target, - cert_name) + write_content_set_path(ssl_cert, ".crt", write_ssl_cert, ssl_path, + target, cert_name) end if ssl_key and #ssl_key > 0 then - write_file_set_path(ssl_key, ".key", write_ssl_cert_key, ssl_path, - target, key_name) + write_content_set_path(ssl_key, ".key", write_ssl_cert_key, ssl_path, + target, key_name) end end + + local dhparam_value = kong_config["ssl_dhparam"] + if dhparam_value and not is_predefined_dhgroup(dhparam_value) then + write_content_set_path(dhparam_value, ".pem", write_ssl_cert, ssl_path, + "ssl-dhparam", "ssl_dhparam") + end end + if kong_config.lua_ssl_trusted_certificate_combined then gen_trusted_certs_combined_file( kong_config.lua_ssl_trusted_certificate_combined, diff --git a/kong/conf_loader/init.lua b/kong/conf_loader/init.lua index 634dab470960..d97c3895f69c 100644 --- a/kong/conf_loader/init.lua +++ b/kong/conf_loader/init.lua @@ -625,30 +625,32 @@ local function infer_value(value, typ, opts) end -local function try_base64_decode(vals) - -- names that we should not attempt decoding - local decode_blacklist = { - system = "system" - } +local function decode_base64_str(str) + if type(str) == "string" then + return decode_base64(str) + or decode_base64url(str) + or nil, "base64 decoding failed: invalid input" - if type(vals) == "table" then - for i, v in ipairs(vals) do - vals[i] = decode_blacklist[v] - or decode_base64(v) - or decode_base64url(v) - or v + else + return nil, "base64 decoding failed: not a string" + end +end + + +local function try_decode_base64(value) + if type(value) == "table" then + for i, v in ipairs(value) do + value[i] = decode_base64_str(v) or v end - return vals + + return value end - if type(vals) == "string" then - return decode_blacklist[vals] - or decode_base64(vals) - or decode_base64url(vals) - or vals + if type(value) == "string" then + return decode_base64_str(value) or value end - return vals + return value end @@ -676,25 +678,6 @@ local function check_and_infer(conf, opts) conf[k] = value end - -- decode base64 for supported properties - for cert_name, has_key in pairs({ - ssl_cert = true, - admin_ssl_cert = true, - status_ssl_cert = true, - client_ssl_cert = true, - cluster_cert = true, - ssl_dhparam = false, - lua_ssl_trusted_certificate = false, - cluster_ca_cert = false - }) do - conf[cert_name] = try_base64_decode(conf[cert_name]) - - if has_key then - local key_name = cert_name .. "_key" - conf[key_name] = try_base64_decode(conf[key_name]) - end - end - --------------------- -- custom validations --------------------- @@ -797,25 +780,31 @@ local function check_and_infer(conf, opts) end if ssl_cert then - for _, cert in ipairs(ssl_cert) do + for i, cert in ipairs(ssl_cert) do if not exists(cert) then + cert = try_decode_base64(cert) + ssl_cert[i] = cert local _, err = openssl_x509.new(cert) if err then errors[#errors + 1] = prefix .. "ssl_cert: failed loading certificate from " .. cert end end end + conf[prefix .. "ssl_cert"] = ssl_cert end if ssl_cert_key then - for _, cert_key in ipairs(ssl_cert_key) do + for i, cert_key in ipairs(ssl_cert_key) do if not exists(cert_key) then + cert_key = try_decode_base64(cert_key) + ssl_cert_key[i] = cert_key local _, err = openssl_pkey.new(cert_key) if err then errors[#errors + 1] = prefix .. "ssl_cert_key: failed loading key from " .. cert_key end end end + conf[prefix .. "ssl_cert_key"] = ssl_cert_key end end end @@ -832,6 +821,8 @@ local function check_and_infer(conf, opts) end if client_ssl_cert and not exists(client_ssl_cert) then + client_ssl_cert = try_decode_base64(client_ssl_cert) + conf.client_ssl_cert = client_ssl_cert local _, err = openssl_x509.new(client_ssl_cert) if err then errors[#errors + 1] = "client_ssl_cert: failed loading certificate from " .. client_ssl_cert @@ -839,6 +830,8 @@ local function check_and_infer(conf, opts) end if client_ssl_cert_key and not exists(client_ssl_cert_key) then + client_ssl_cert_key = try_decode_base64(client_ssl_cert_key) + conf.client_ssl_cert_key = client_ssl_cert_key local _, err = openssl_pkey.new(client_ssl_cert_key) if err then errors[#errors + 1] = "client_ssl_cert_key: failed loading key from " .. @@ -865,11 +858,12 @@ local function check_and_infer(conf, opts) if trusted_cert ~= "system" then if not exists(trusted_cert) then + trusted_cert = try_decode_base64(trusted_cert) local _, err = openssl_x509.new(trusted_cert) if err then errors[#errors + 1] = "lua_ssl_trusted_certificate: " .. - "failed loading certificate from " .. - trusted_cert + "failed loading certificate from " .. + trusted_cert end end @@ -906,6 +900,7 @@ local function check_and_infer(conf, opts) if conf.ssl_dhparam then if not is_predefined_dhgroup(conf.ssl_dhparam) and not exists(conf.ssl_dhparam) then + conf.ssl_dhparam = try_decode_base64(conf.ssl_dhparam) local _, err = openssl_pkey.new( { type = "DH", @@ -1076,6 +1071,8 @@ local function check_and_infer(conf, opts) else if not exists(cluster_cert) then + cluster_cert = try_decode_base64(cluster_cert) + conf.cluster_cert = cluster_cert local _, err = openssl_x509.new(cluster_cert) if err then errors[#errors + 1] = "cluster_cert: failed loading certificate from " .. cluster_cert @@ -1083,6 +1080,8 @@ local function check_and_infer(conf, opts) end if not exists(cluster_cert_key) then + cluster_cert_key = try_decode_base64(cluster_cert_key) + conf.cluster_cert_key = cluster_cert_key local _, err = openssl_pkey.new(cluster_cert_key) if err then errors[#errors + 1] = "cluster_cert_key: failed loading key from " .. cluster_cert_key @@ -1091,6 +1090,8 @@ local function check_and_infer(conf, opts) end if cluster_ca_cert and not exists(cluster_ca_cert) then + cluster_ca_cert = try_decode_base64(cluster_ca_cert) + conf.cluster_ca_cert = cluster_ca_cert local _, err = openssl_x509.new(cluster_ca_cert) if err then errors[#errors + 1] = "cluster_ca_cert: failed loading certificate from " .. diff --git a/spec/01-unit/03-conf_loader_spec.lua b/spec/01-unit/03-conf_loader_spec.lua index 2043189afae8..7f0a8b9c7bbe 100644 --- a/spec/01-unit/03-conf_loader_spec.lua +++ b/spec/01-unit/03-conf_loader_spec.lua @@ -750,14 +750,18 @@ describe("Configuration loader", function() ssl_dhparam = dhparam, lua_ssl_trusted_certificate = cacert } - local conf_params = { - ssl_cipher_suite = "old" + ssl_cipher_suite = "old", + client_ssl = "on", + role = "control_plane", + status_listen = "127.0.0.1:123 ssl", + proxy_listen = "127.0.0.1:456 ssl", + admin_listen = "127.0.0.1:789 ssl" } + for n, v in pairs(properties) do conf_params[n] = ngx.encode_base64(v) end - local conf, err = conf_loader(nil, conf_params) assert.is_nil(err) diff --git a/spec/01-unit/04-prefix_handler_spec.lua b/spec/01-unit/04-prefix_handler_spec.lua index c6bf789fe504..550d15a85aeb 100644 --- a/spec/01-unit/04-prefix_handler_spec.lua +++ b/spec/01-unit/04-prefix_handler_spec.lua @@ -2,6 +2,8 @@ local helpers = require "spec.helpers" local conf_loader = require "kong.conf_loader" local prefix_handler = require "kong.cmd.utils.prefix_handler" local ffi = require "ffi" +local tablex = require "pl.tablex" +local ssl_fixtures = require "spec.fixtures.ssl" local exists = helpers.path.exists local join = helpers.path.join @@ -965,50 +967,82 @@ describe("NGINX conf compiler", function() assert.truthy(exists(join(conf.prefix, "ssl", conf.nginx_http_ssl_dhparam .. ".pem"))) assert.truthy(exists(join(conf.prefix, "ssl", conf.nginx_stream_ssl_dhparam .. ".pem"))) end) - it("accepts and stores values passed as 'content', reconfigures valid paths", function() - local ssl_fixtures = require "spec.fixtures.ssl" - local cert = ssl_fixtures.cert - local cacert = ssl_fixtures.cert_ca - local key = ssl_fixtures.key - local dhparam = ssl_fixtures.dhparam - - local params = { - ssl_cipher_suite = "old", - prefix = tmp_config.prefix, - ssl_cert = cert, - ssl_cert_key = key, - admin_ssl_cert = cert, - admin_ssl_cert_key = key, - status_ssl_cert = cert, - status_ssl_cert_key = key, - client_ssl_cert = cert, - client_ssl_cert_key = key, - cluster_cert = cert, - cluster_cert_key = key, - cluster_ca_cert = cacert, - ssl_dhparam = dhparam, - lua_ssl_trusted_certificate = cacert - } - - local conf, err = conf_loader(nil, params) - assert(prefix_handler.prepare_prefix(conf)) - assert.is_nil(err) - assert.is_table(conf) - - for name, _ in pairs(params) do - if name ~= "ssl_cipher_suite" and name ~= "prefix" then + describe("accept raw content for configuration properties", function() + it("writes files and re-configures valid paths", function() + local cert = ssl_fixtures.cert + local cacert = ssl_fixtures.cert_ca + local key = ssl_fixtures.key + local dhparam = ssl_fixtures.dhparam + + local params = { + ssl_cipher_suite = "old", + prefix = tmp_config.prefix, + } + local ssl_params = { + ssl_cert = cert, + ssl_cert_key = key, + admin_ssl_cert = cert, + admin_ssl_cert_key = key, + status_ssl_cert = cert, + status_ssl_cert_key = key, + client_ssl_cert = cert, + client_ssl_cert_key = key, + cluster_cert = cert, + cluster_cert_key = key, + cluster_ca_cert = cacert, + ssl_dhparam = dhparam, + lua_ssl_trusted_certificate = cacert + } + + local conf, err = conf_loader(nil, tablex.merge(params, ssl_params, true)) + assert(prefix_handler.prepare_prefix(conf)) + assert.is_nil(err) + assert.is_table(conf) + + for name, input_content in pairs(ssl_params) do local paths = conf[name] if type(paths) == "table" then for i = 1, #paths do assert.truthy(exists(paths[i])) + local configured_content = assert(helpers.file.read(paths[i])) + assert.equals(input_content, configured_content) end end if type(paths) == "string" then assert.truthy(exists(paths)) + local configured_content = assert(helpers.file.read(paths)) + assert.equals(input_content, configured_content) end end - end + end) + it("sets lua_ssl_trusted_certificate to a combined file" .. + "(multiple content entries)", function() + local cacerts = string.format( + "%s,%s", + ssl_fixtures.cert_ca, + ssl_fixtures.cert_ca + ) + local conf = assert(conf_loader(nil, { + lua_ssl_trusted_certificate = cacerts, + prefix = tmp_config.prefix + })) + assert(prefix_handler.prepare_prefix(conf)) + assert.is_table(conf) + local trusted_certificates = conf["lua_ssl_trusted_certificate"] + assert.equal(2, #trusted_certificates) + local combined = assert( + helpers.file.read(conf["lua_ssl_trusted_certificate_combined"]) + ) + assert.equal( + combined, + string.format( + "%s\n%s\n", + ssl_fixtures.cert_ca, + ssl_fixtures.cert_ca + ) + ) + end) end) end) diff --git a/spec/fixtures/ssl.lua b/spec/fixtures/ssl.lua index 1ae6d6689b91..db731bc8282d 100644 --- a/spec/fixtures/ssl.lua +++ b/spec/fixtures/ssl.lua @@ -601,5 +601,5 @@ MIIBDAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD ssbzSibBsu/6iGtCOGEoXJf//////////wIBAgICAOE= ------END DH PARAMETERS-----]] +-----END DH PARAMETERS-----]], }