Files
2026-07-13 12:32:21 +08:00

1428 lines
45 KiB
Lua

local helpers = require "spec.helpers"
local ssl_fixtures = require "spec.fixtures.ssl"
local other_ca_cert = [[
-----BEGIN CERTIFICATE-----
MIIEvjCCAqagAwIBAgIJALabx/Nup200MA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCFlvbG80Mi4xMCAXDTE5MDkxNTE2Mjc1M1oYDzIxMTkwODIyMTYyNzUzWjAT
MREwDwYDVQQDDAhZb2xvNDIuMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBANIW67Ay0AtTeBY2mORaGet/VPL5jnBRz0zkZ4Jt7fEq3lbxYaJBnFI8wtz3
bHLtLsxkvOFujEMY7HVd+iTqbJ7hLBtK0AdgXDjf+HMmoWM7x0PkZO+3XSqyRBbI
YNoEaQvYBNIXrKKJbXIU6higQaXYszeN8r3+RIbcTIlZxy28msivEGfGTrNujQFc
r/eyf+TLHbRqh0yg4Dy/U/T6fqamGhFrjupRmOMugwF/BHMH2JHhBYkkzuZLgV2u
7Yh1S5FRlh11am5vWuRSbarnx72hkJ99rUb6szOWnJKKew8RSn3CyhXbS5cb0QRc
ugRc33p/fMucJ4mtCJ2Om1QQe83G1iV2IBn6XJuCvYlyWH8XU0gkRxWD7ZQsl0bB
8AFTkVsdzb94OM8Y6tWI5ybS8rwl8b3r3fjyToIWrwK4WDJQuIUx4nUHObDyw+KK
+MmqwpAXQWbNeuAc27FjuJm90yr/163aGuInNY5Wiz6CM8WhFNAi/nkEY2vcxKKx
irSdSTkbnrmLFAYrThaq0BWTbW2mwkOatzv4R2kZzBUOiSjRLPnbyiPhI8dHLeGs
wMxiTXwyPi8iQvaIGyN4DPaSEiZ1GbexyYFdP7sJJD8tG8iccbtJYquq3cDaPTf+
qv5M6R/JuMqtUDheLSpBNK+8vIe5e3MtGFyrKqFXdynJtfHVAgMBAAGjEzARMA8G
A1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggIBAK0BmL5B1fPSMbFy8Hbc
/ESEunt4HGaRWmZZSa/aOtTjhKyDXLLJZz3C4McugfOf9BvvmAOZU4uYjfHTnNH2
Z3neBkdTpQuJDvrBPNoCtJns01X/nuqFaTK/Tt9ZjAcVeQmp51RwhyiD7nqOJ/7E
Hp2rC6gH2ABXeexws4BDoZPoJktS8fzGWdFBCHzf4mCJcb4XkI+7GTYpglR818L3
dMNJwXeuUsmxxKScBVH6rgbgcEC/6YwepLMTHB9VcH3X5VCfkDIyPYLWmvE0gKV7
6OU91E2Rs8PzbJ3EuyQpJLxFUQp8ohv5zaNBlnMb76UJOPR6hXfst5V+e7l5Dgwv
Dh4CeO46exmkEsB+6R3pQR8uOFtubH2snA0S3JA1ji6baP5Y9Wh9bJ5McQUgbAPE
sCRBFoDLXOj3EgzibohC5WrxN3KIMxlQnxPl3VdQvp4gF899mn0Z9V5dAsGPbxRd
quE+DwfXkm0Sa6Ylwqrzu2OvSVgbMliF3UnWbNsDD5KcHGIaFxVC1qkwK4cT3pyS
58i/HAB2+P+O+MltQUDiuw0OSUFDC0IIjkDfxLVffbF+27ef9C5NG81QlwTz7TuN
zeigcsBKooMJTszxCl6dtxSyWTj7hJWXhy9pXsm1C1QulG6uT4RwCa3m0QZoO7G+
6Wu6lP/kodPuoNubstIuPdi2
-----END CERTIFICATE-----
]]
local fixtures = {
http_mock = {
upstream_mtls = [[
server {
server_name example.com;
listen 16798 ssl;
ssl_certificate ../spec/fixtures/mtls_certs/example.com.crt;
ssl_certificate_key ../spec/fixtures/mtls_certs/example.com.key;
ssl_client_certificate ../spec/fixtures/mtls_certs/ca.crt;
ssl_verify_client on;
ssl_session_tickets off;
ssl_session_cache off;
keepalive_requests 0;
location = / {
add_header 'X-Cert' $ssl_client_escaped_cert;
echo 'it works';
}
}
]],
upstream_tls = [[
server {
server_name example.com;
listen 16799 ssl;
ssl_certificate ../spec/fixtures/mtls_certs/example.com.crt;
ssl_certificate_key ../spec/fixtures/mtls_certs/example.com.key;
ssl_session_tickets off;
ssl_session_cache off;
keepalive_requests 0;
location = / {
echo 'it works';
}
}
]]
},
}
local function reload_router(flavor)
helpers = require("spec.internal.module").reload_helpers(flavor)
fixtures.dns_mock = helpers.dns_mock.new({ mocks_only = true })
fixtures.dns_mock:A {
name = "example.com",
address = "127.0.0.1",
}
end
-- TODO: remove it when we confirm it is not needed
local function gen_route(flavor, r)
return r
end
local function gen_plugin(route)
return {
name = "pre-function",
route = { id = route.id },
config = {
access = {
[[
kong.service.request.enable_buffering()
]]
}
}
}
end
local function get_proxy_client(subsystems, stream_port)
if subsystems == "http" then
return assert(helpers.proxy_client())
else
return assert(helpers.proxy_client(20000, stream_port))
end
end
local function wait_for_all_config_update(subsystems)
local opt = {}
if subsystems == "stream" then
opt.stream_enabled = true
opt.stream_port = 19003
end
helpers.wait_for_all_config_update(opt)
end
for _, flavor in ipairs({ "traditional", "traditional_compatible", "expressions" }) do
for _, strategy in helpers.each_strategy() do
describe("overriding upstream TLS parameters for database [#" .. strategy .. ", flavor = " .. flavor .. "]", function()
local admin_client
local bp
local service_mtls, service_tls
local certificate, certificate_bad, ca_certificate
local upstream
local service_mtls_upstream
local tls_service_mtls, tls_service_tls
local tls_upstream
local tls_service_mtls_upstream
local route_mtls_buffered_proxying, route_tls_buffered_proxying, route_mtls_upstream_buffered_proxying
reload_router(flavor)
lazy_setup(function()
bp = helpers.get_db_utils(strategy, {
"routes",
"services",
"certificates",
"ca_certificates",
"upstreams",
"targets",
})
service_mtls = assert(bp.services:insert({
name = "protected-service-mtls",
url = "https://127.0.0.1:16798/",
}))
service_tls = assert(bp.services:insert({
name = "protected-service",
url = "https://example.com:16799/", -- domain name needed for hostname check
}))
upstream = assert(bp.upstreams:insert({
name = "backend-mtls",
}))
assert(bp.targets:insert({
upstream = { id = upstream.id, },
target = "127.0.0.1:16798",
}))
service_mtls_upstream = assert(bp.services:insert({
name = "protected-service-mtls-upstream",
url = "https://backend-mtls/",
}))
certificate = assert(bp.certificates:insert({
cert = ssl_fixtures.cert_client,
key = ssl_fixtures.key_client,
}))
certificate_bad = assert(bp.certificates:insert({
cert = ssl_fixtures.cert, -- this cert is *not* trusted by upstream
key = ssl_fixtures.key,
}))
ca_certificate = assert(bp.ca_certificates:insert({
cert = ssl_fixtures.cert_ca,
}))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_mtls.id, },
hosts = { "example.com", },
paths = { "/mtls", },
})))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_tls.id, },
hosts = { "example.com", },
paths = { "/tls", },
})))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_mtls_upstream.id, },
hosts = { "example.com", },
paths = { "/mtls-upstream", },
})))
route_mtls_buffered_proxying = assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_mtls.id, },
hosts = { "example.com", },
paths = { "/mtls-buffered-proxying", },
})))
route_tls_buffered_proxying = assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_tls.id, },
hosts = { "example.com", },
paths = { "/tls-buffered-proxying", },
})))
route_mtls_upstream_buffered_proxying = assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_mtls_upstream.id, },
hosts = { "example.com", },
paths = { "/mtls-upstream-buffered-proxying", },
})))
-- use pre-function to enable buffered_proxying in order to trigger the
-- `ngx.location.capture("/kong_buffered_http")` in `Kong.response()`
assert(bp.plugins:insert(gen_plugin(route_mtls_buffered_proxying)))
assert(bp.plugins:insert(gen_plugin(route_tls_buffered_proxying)))
assert(bp.plugins:insert(gen_plugin(route_mtls_upstream_buffered_proxying)))
-- tls
tls_service_mtls = assert(bp.services:insert({
name = "tls-protected-service-mtls",
url = "tls://127.0.0.1:16798",
}))
tls_service_tls = assert(bp.services:insert({
name = "tls-protected-service",
url = "tls://example.com:16799", -- domain name needed for hostname check
}))
tls_upstream = assert(bp.upstreams:insert({
name = "tls-backend-mtls",
}))
assert(bp.targets:insert({
upstream = { id = tls_upstream.id, },
target = "example.com:16798",
}))
tls_service_mtls_upstream = assert(bp.services:insert({
name = "tls-protected-service-mtls-upstream",
url = "tls://tls-backend-mtls",
host = "example.com"
}))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = tls_service_mtls.id, },
destinations = {
{
port = 19000,
},
},
protocols = {
"tls",
},
})))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = tls_service_tls.id, },
destinations = {
{
port = 19001,
},
},
protocols = {
"tls",
},
})))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = tls_service_mtls_upstream.id, },
destinations = {
{
port = 19002,
},
},
protocols = {
"tls",
},
})))
assert(helpers.start_kong({
router_flavor = flavor,
database = strategy,
nginx_conf = "spec/fixtures/custom_nginx.template",
stream_listen = helpers.get_proxy_ip(false) .. ":19000,"
.. helpers.get_proxy_ip(false) .. ":19001,"
.. helpers.get_proxy_ip(false) .. ":19002,"
.. helpers.get_proxy_ip(false) .. ":19003",
}, nil, nil, fixtures))
admin_client = assert(helpers.admin_client())
end)
lazy_teardown(function()
helpers.stop_kong()
end)
local function get_tls_service_id(subsystems)
if subsystems == "http" then
return service_mtls.id
else
return tls_service_mtls.id
end
end
for _, subsystems in pairs({"http", "stream"}) do
describe(subsystems .. " mutual TLS authentication against upstream with Service object", function()
describe("no client certificate supplied", function()
it("accessing protected upstream", function()
local proxy_client = get_proxy_client(subsystems, 19000)
local res = assert(proxy_client:send {
path = "/mtls",
headers = {
["Host"] = "example.com",
}
})
local body = assert.res_status(400, res)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
assert(proxy_client:close())
end)
-- buffered_proxying
if subsystems == "http" then
it("accessing protected upstream, buffered_proxying = true", function()
local proxy_client = get_proxy_client(subsystems, 19000)
local res = assert(proxy_client:send {
path = "/mtls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
local body = assert.res_status(400, res)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
assert(proxy_client:close())
end)
end
end)
describe(subsystems .. " #db client certificate supplied via service.client_certificate", function()
lazy_setup(function()
local service_id = get_tls_service_id(subsystems)
local res = assert(admin_client:patch("/services/" .. service_id, {
body = {
client_certificate = { id = certificate.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
end)
it("accessing protected upstream", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19000)
local path
if subsystems == "http" then
path = "/mtls"
else
path = "/"
end
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
-- buffered_proxying
if subsystems == "http" then
it("accessing protected upstream, buffered_proxying = true", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19000)
local path = "/mtls-buffered-proxying"
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
end
it("send updated client certificate", function ()
local proxy_client = get_proxy_client(subsystems, 19000)
local path
if subsystems == "http" then
path = "/mtls"
else
path = "/"
end
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
assert.res_status(200, res)
local res_cert = res.headers["X-Cert"]
assert(proxy_client:close())
-- buffered_proxying
local res_cert_buffered
if subsystems == "http" then
local proxy_client = get_proxy_client(subsystems, 19000)
local res = assert(proxy_client:send {
path = "/mtls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
assert.res_status(200, res)
res_cert_buffered = res.headers["X-Cert"]
assert(proxy_client:close())
end
res = admin_client:patch("/certificates/" .. certificate.id, {
body = {
cert = ssl_fixtures.cert_client2,
key = ssl_fixtures.key_client2,
},
headers = { ["Content-Type"] = "application/json" }
})
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local proxy_client2 = get_proxy_client(subsystems, 19000)
if subsystems == "http" then
path = "/mtls"
else
path = "/"
end
res = assert(proxy_client2:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
assert.res_status(200, res)
local res_cert2 = res.headers["X-Cert"]
assert.not_equals(res_cert, res_cert2)
-- buffered_proxying
local res_cert2_buffered
if subsystems == "http" then
res = assert(proxy_client2:send {
path = "/mtls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
assert.res_status(200, res)
res_cert2_buffered = res.headers["X-Cert"]
assert.not_equals(res_cert_buffered, res_cert2_buffered)
end
-- restore old
res = admin_client:patch("/certificates/" .. certificate.id, {
body = {
cert = ssl_fixtures.cert_client,
key = ssl_fixtures.key_client,
},
headers = { ["Content-Type"] = "application/json" }
})
assert.res_status(200, res)
assert(proxy_client2:close())
end)
it("remove client_certificate removes access", function()
local service_id = get_tls_service_id(subsystems)
local res = assert(admin_client:patch("/services/" .. service_id, {
body = {
client_certificate = ngx.null,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
local body
helpers.wait_until(function()
local proxy_client= get_proxy_client(subsystems, 19000)
res = assert(proxy_client:send {
path = "/mtls",
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(400, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client= get_proxy_client(subsystems, 19000)
res = assert(proxy_client:send {
path = "/mtls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(400, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
end
end)
end)
end)
describe(subsystems .. " mutual TLS authentication against upstream with Upstream object", function()
describe("no client certificate supplied", function()
it("accessing protected upstream", function()
local proxy_client= get_proxy_client(subsystems, 19002)
local res = assert(proxy_client:send {
path = "/mtls-upstream",
headers = {
["Host"] = "example.com",
}
})
local body = assert.res_status(400, res)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
assert(proxy_client:close())
end)
-- buffered_proxying
if subsystems == "http" then
it("accessing protected upstream, buffered_proxying = true", function()
local proxy_client= get_proxy_client(subsystems, 19002)
local res = assert(proxy_client:send {
path = "/mtls-upstream-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
local body = assert.res_status(400, res)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
assert(proxy_client:close())
end)
end
end)
describe("#db client certificate supplied via upstream.client_certificate", function()
lazy_setup(function()
local upstream_id
if subsystems == "http" then
upstream_id = upstream.id
else
upstream_id = tls_upstream.id
end
local res = assert(admin_client:patch("/upstreams/" .. upstream_id, {
body = {
client_certificate = { id = certificate.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
end)
it("accessing protected upstream", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
local path
if subsystems == "http" then
path = "/mtls-upstream"
else
path = "/"
end
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
-- buffered_proxying
if subsystems == "http" then
it("accessing protected upstream, buffered_proxying = true", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
local path = "/mtls-upstream-buffered-proxying"
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
end
it("remove client_certificate removes access", function()
local upstream_id
if subsystems == "http" then
upstream_id = upstream.id
else
upstream_id = tls_upstream.id
end
local res = assert(admin_client:patch("/upstreams/" .. upstream_id, {
body = {
client_certificate = ngx.null,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
res = assert(proxy_client:send {
path = "/mtls-upstream",
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(400, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
res = assert(proxy_client:send {
path = "/mtls-upstream-buffered-proxying",
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(400, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("400 No required SSL certificate was sent", body, nil, true)
end
end)
end)
describe("#db when both Service.client_certificate and Upstream.client_certificate are set, Service.client_certificate takes precedence", function()
lazy_setup(function()
local upstream_id
local service_mtls_upstream_id
if subsystems == "http" then
upstream_id = upstream.id
service_mtls_upstream_id = service_mtls_upstream.id
else
upstream_id = tls_upstream.id
service_mtls_upstream_id = tls_service_mtls_upstream.id
end
local res = assert(admin_client:patch("/upstreams/" .. upstream_id, {
body = {
client_certificate = { id = certificate_bad.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
res = assert(admin_client:patch("/services/" .. service_mtls_upstream_id, {
body = {
client_certificate = { id = certificate.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
end)
it("access is allowed because Service.client_certificate overrides Upstream.client_certificate", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
local path
if subsystems == "http" then
path = "/mtls-upstream"
else
path = "/"
end
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
-- buffered_proxying
if subsystems == "http" then
it("access is allowed because Service.client_certificate overrides Upstream.client_certificate, buffered_proxy = true", function()
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19002)
local path = "/mtls-upstream-buffered-proxying"
local res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end)
end
end)
end)
describe(subsystems .. " TLS verification options against upstream", function()
describe("tls_verify", function()
it("default is off", function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
-- buffered_proxying
if subsystems == "http" then
it("default is off, buffered_proxying = true", function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end
it("#db turn it on, request is blocked", function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify = true,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local err
res, err = proxy_client:send {
path = "/tls",
headers = {
["Host"] = "example.com",
}
}
if subsystems == "http" then
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
else
return pcall(function()
assert.equals("connection reset by peer", err)
assert(proxy_client:close())
end)
end
end, 10)
if subsystems == "http" then
assert.matches("An invalid response was received from the upstream server", body)
end
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
res = proxy_client:send {
path = "/tls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("An invalid response was received from the upstream server", body)
end
end)
end)
describe("ca_certificates", function()
it("#db request is allowed through once correct CA certificate is set", function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify = true,
ca_certificates = { ca_certificate.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
end
end)
it("#db request is not allowed through once the CA certificate is updated to other ca", function()
local res = assert(admin_client:patch("/ca_certificates/" .. ca_certificate.id, {
body = {
cert = other_ca_cert,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
local res, err = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
if subsystems == "http" then
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
else
return pcall(function()
assert.equals("connection reset by peer", err)
assert(proxy_client:close())
end)
end
end, 10)
if subsystems == "http" then
assert.matches("An invalid response was received from the upstream server", body)
end
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("An invalid response was received from the upstream server", body)
end
end)
it("#db request is allowed through once the CA certificate is updated back to the correct ca", function()
local res = assert(admin_client:patch("/ca_certificates/" .. ca_certificate.id, {
body = {
cert = ssl_fixtures.cert_ca,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
end
end)
end)
describe("#db tls_verify_depth", function()
lazy_setup(function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify = true,
ca_certificates = { ca_certificate.id, },
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
end)
it("request is not allowed through if depth limit is too low", function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify_depth = 0,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local res, err = proxy_client:send {
path = "/tls",
headers = {
["Host"] = "example.com",
}
}
if subsystems == "http" then
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
else
return pcall(function()
assert.equals("connection reset by peer", err)
assert(proxy_client:close())
end)
end
end, 10)
if subsystems == "http" then
assert.matches("An invalid response was received from the upstream server", body)
end
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local res = proxy_client:send {
path = "/tls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
body = assert.res_status(502, res)
assert(proxy_client:close())
end)
end, 10)
assert.matches("An invalid response was received from the upstream server", body)
end
end)
it("request is allowed through if depth limit is sufficient", function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify_depth = 1,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local body
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
res = assert(proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
})
return pcall(function()
body = assert.res_status(200, res)
assert(proxy_client:close())
end)
end, 10)
assert.equals("it works", body)
end
end)
end)
end)
end
end)
end
end -- for flavor
for _, flavor in ipairs({ "traditional", "traditional_compatible", "expressions" }) do
for _, strategy in helpers.each_strategy() do
describe("overriding upstream TLS parameters for database [#" .. strategy .. ", flavor = " .. flavor .. "] (nginx_proxy_proxy_ssl_verify: on, nginx_sproxy_proxy_ssl_verify: on)", function()
local admin_client
local bp
local service_tls
local tls_service_tls
local route_tls_buffered_proxying
reload_router(flavor)
lazy_setup(function()
bp = helpers.get_db_utils(strategy, {
"routes",
"services",
"certificates",
"ca_certificates",
"upstreams",
"targets",
})
service_tls = assert(bp.services:insert({
name = "protected-service",
url = "https://example.com:16799/", -- domain name needed for hostname check
}))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_tls.id, },
hosts = { "example.com", },
paths = { "/tls", },
})))
route_tls_buffered_proxying = assert(bp.routes:insert(gen_route(flavor,{
service = { id = service_tls.id, },
hosts = { "example.com", },
paths = { "/tls-buffered-proxying", },
})))
-- use pre-function to enable buffered_proxying in order to trigger the
-- `ngx.location.capture("/kong_buffered_http")` in `Kong.response()`
assert(bp.plugins:insert(gen_plugin(route_tls_buffered_proxying)))
-- tls
tls_service_tls = assert(bp.services:insert({
name = "tls-protected-service",
url = "tls://example.com:16799", -- domain name needed for hostname check
}))
assert(bp.routes:insert(gen_route(flavor,{
service = { id = tls_service_tls.id, },
destinations = {
{
port = 19001,
},
},
protocols = {
"tls",
},
})))
assert(helpers.start_kong({
router_flavor = flavor,
database = strategy,
nginx_conf = "spec/fixtures/custom_nginx.template",
stream_listen = helpers.get_proxy_ip(false) .. ":19000,"
.. helpers.get_proxy_ip(false) .. ":19001,"
.. helpers.get_proxy_ip(false) .. ":19002,"
.. helpers.get_proxy_ip(false) .. ":19003",
nginx_proxy_proxy_ssl_verify = "on",
-- An unrelated ca, just used as a placeholder to prevent nginx from reporting errors
nginx_proxy_proxy_ssl_trusted_certificate = "../spec/fixtures/kong_clustering_ca.crt",
nginx_sproxy_proxy_ssl_verify = "on",
nginx_sproxy_proxy_ssl_trusted_certificate = "../spec/fixtures/kong_clustering_ca.crt",
}, nil, nil, fixtures))
admin_client = assert(helpers.admin_client())
end)
lazy_teardown(function()
helpers.stop_kong()
end)
for _, subsystems in pairs({"http", "stream"}) do
describe(subsystems .. " TLS verification options against upstream", function()
describe("tls_verify", function()
it("default is on, request is blocked", function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
local res, err = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
if subsystems == "http" then
local body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
else
assert.equals("connection reset by peer", err)
end
assert(proxy_client:close())
end)
-- buffered_proxying
if subsystems == "http" then
it("default is on, buffered_proxying = true, request is blocked", function()
local proxy_client = get_proxy_client(subsystems, 19001)
local path = "/tls-buffered-proxying"
local res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
local body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
assert(proxy_client:close())
end)
end
it("#db turn it off, request is allowed", function()
local service_tls_id
if subsystems == "http" then
service_tls_id = service_tls.id
else
service_tls_id = tls_service_tls.id
end
local res = assert(admin_client:patch("/services/" .. service_tls_id, {
body = {
tls_verify = false,
},
headers = { ["Content-Type"] = "application/json" },
}))
assert.res_status(200, res)
wait_for_all_config_update(subsystems)
local path
if subsystems == "http" then
path = "/tls"
else
path = "/"
end
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
res = proxy_client:send {
path = path,
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
-- buffered_proxying
if subsystems == "http" then
helpers.wait_until(function()
local proxy_client = get_proxy_client(subsystems, 19001)
res = proxy_client:send {
path = "/tls-buffered-proxying",
headers = {
["Host"] = "example.com",
}
}
return pcall(function()
local body = assert.res_status(200, res)
assert.equals("it works", body)
assert(proxy_client:close())
end)
end, 10)
end
end)
end)
end)
end
end)
end
end -- for flavor