diff --git a/kong/constants.lua b/kong/constants.lua index 2451995a49df..412ffce52a13 100644 --- a/kong/constants.lua +++ b/kong/constants.lua @@ -56,6 +56,7 @@ local protocols_with_subsystem = { tcp = "stream", tls = "stream", udp = "stream", + tls_passthrough = "stream", grpc = "http", grpcs = "http", } diff --git a/kong/db/schema/entities/routes.lua b/kong/db/schema/entities/routes.lua index 3ed33a8b189c..d4fc1e9b8f21 100644 --- a/kong/db/schema/entities/routes.lua +++ b/kong/db/schema/entities/routes.lua @@ -19,7 +19,8 @@ return { elements = typedefs.protocol, mutually_exclusive_subsets = { { "http", "https" }, - { "tcp", "tls", "udp", }, + { "tcp", "tls", "udp" }, + { "tls_passthrough" }, { "grpc", "grpcs" }, }, default = { "http", "https" }, -- TODO: different default depending on service's scheme @@ -57,10 +58,10 @@ return { entity_checks = { { conditional = { if_field = "protocols", - if_match = { elements = { type = "string", not_one_of = { "grpcs", "https", "tls" }}}, + if_match = { elements = { type = "string", not_one_of = { "grpcs", "https", "tls", "tls_passthrough" }}}, then_field = "snis", then_match = { len_eq = 0 }, - then_err = "'snis' can only be set when 'protocols' is 'grpcs', 'https' or 'tls'", + then_err = "'snis' can only be set when 'protocols' is 'grpcs', 'https', 'tls' or 'tls_passthrough'", }}, }, } diff --git a/kong/db/schema/entities/routes_subschemas.lua b/kong/db/schema/entities/routes_subschemas.lua index 5d2ff99aea9d..300f955d4d34 100644 --- a/kong/db/schema/entities/routes_subschemas.lua +++ b/kong/db/schema/entities/routes_subschemas.lua @@ -24,10 +24,10 @@ local stream_subschema = { name = "tcp", fields = { - { methods = typedefs.no_methods { err = "cannot set 'methods' when 'protocols' is 'tcp', 'tls' or 'udp'" } }, - { hosts = typedefs.no_hosts { err = "cannot set 'hosts' when 'protocols' is 'tcp', 'tls' or 'udp'" } }, - { paths = typedefs.no_paths { err = "cannot set 'paths' when 'protocols' is 'tcp', 'tls' or 'udp'" } }, - { headers = typedefs.no_headers { err = "cannot set 'headers' when 'protocols' is 'tcp', 'tls' or 'udp'" } }, + { methods = typedefs.no_methods { err = "cannot set 'methods' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'" } }, + { hosts = typedefs.no_hosts { err = "cannot set 'hosts' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'" } }, + { paths = typedefs.no_paths { err = "cannot set 'paths' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'" } }, + { headers = typedefs.no_headers { err = "cannot set 'headers' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'" } }, }, entity_checks = { { conditional_at_least_one_of = { if_field = "protocols", @@ -35,6 +35,11 @@ local stream_subschema = { then_at_least_one_of = { "sources", "destinations", "snis" }, then_err = "must set one of %s when 'protocols' is 'tcp', 'tls' or 'udp'", }}, + {conditional_at_least_one_of = { if_field = "protocols", + if_match = { elements = { type = "string", one_of = { "tls_passthrough" } } }, + then_at_least_one_of = { "snis" }, + then_err = "must set snis when 'protocols' is 'tls_passthrough'", + }}, }, } @@ -67,6 +72,7 @@ return { tcp = stream_subschema, tls = stream_subschema, udp = stream_subschema, + tls_passthrough = stream_subschema, grpc = grpc_subschema, grpcs = grpc_subschema, } diff --git a/kong/init.lua b/kong/init.lua index 245e9147a0f5..b87eb82a7fae 100644 --- a/kong/init.lua +++ b/kong/init.lua @@ -740,7 +740,13 @@ function Kong.preread() log_init_worker_errors(ctx) - runloop.preread.before(ctx) + local preread_terminate = runloop.preread.before(ctx) + + -- if proxying to a second layer TLS terminator is required + -- abort further execution and return back to Nginx + if preread_terminate then + return + end local plugins_iterator = runloop.get_updated_plugins_iterator() execute_plugins_iterator(plugins_iterator, "preread", ctx) diff --git a/kong/pdk/client.lua b/kong/pdk/client.lua index 119f916c2b19..fc56302de3b6 100644 --- a/kong/pdk/client.lua +++ b/kong/pdk/client.lua @@ -25,6 +25,8 @@ local AUTH_AND_LATER = phase_checker.new(PHASES.access, PHASES.log) local TABLE_OR_NIL = { ["table"] = true, ["nil"] = true } +local stream_subsystem = ngx.config.subsystem == "stream" + local function new(self) local _CLIENT = {} @@ -48,6 +50,14 @@ local function new(self) function _CLIENT.get_ip() check_not_phase(PHASES.init_worker) + -- when proxying TLS request in second layer or doing TLS passthrough + -- realip_remote_addr is always the previous layer of nginx thus always unix: + local tls_passthrough_block = ngx.var.kong_tls_passthrough_block + if stream_subsystem and + ((tls_passthrough_block and #tls_passthrough_block > 0) or ngx.var.ssl_protocol) then + return ngx.var.remote_addr + end + return ngx.var.realip_remote_addr or ngx.var.remote_addr end @@ -99,6 +109,14 @@ local function new(self) function _CLIENT.get_port() check_not_phase(PHASES.init_worker) + -- when proxying TLS request in second layer or doing TLS passthrough + -- realip_remote_addr is always the previous layer of nginx thus always unix: + local tls_passthrough_block = ngx.var.kong_tls_passthrough_block + if stream_subsystem and + ((tls_passthrough_block and #tls_passthrough_block > 0) or ngx.var.ssl_protocol) then + return tonumber(ngx.var.remote_port) + end + return tonumber(ngx.var.realip_remote_port or ngx.var.remote_port) end diff --git a/kong/router.lua b/kong/router.lua index b17de8c534f3..69266ad8205b 100644 --- a/kong/router.lua +++ b/kong/router.lua @@ -1897,6 +1897,10 @@ function _M.new(routes) or tonumber(var.server_port, 10) -- error value for non-TLS connections ignored intentionally local sni, _ = server_name() + -- fallback to preread SNI if current connection doesn't terminate TLS + if not sni then + sni = var.ssl_preread_server_name + end local scheme if var.protocol == "UDP" then @@ -1906,6 +1910,14 @@ function _M.new(routes) scheme = sni and "tls" or "tcp" end + -- when proxying TLS request in second layer or doing TLS passthrough + -- rewrite the dst_ip,port back to what specified in proxy_protocol + local tls_passthrough_block = var.kong_tls_passthrough_block + if (tls_passthrough_block and #tls_passthrough_block > 0) or var.ssl_protocol then + dst_ip = var.proxy_protocol_server_addr + dst_port = tonumber(var.proxy_protocol_server_port) + end + return find_route(nil, nil, nil, scheme, src_ip, src_port, dst_ip, dst_port, diff --git a/kong/runloop/handler.lua b/kong/runloop/handler.lua index e430e6b0efe8..16060fa06263 100644 --- a/kong/runloop/handler.lua +++ b/kong/runloop/handler.lua @@ -76,6 +76,9 @@ local get_updated_router, build_router, update_router local server_header = meta._SERVER_TOKENS local rebuild_router +local stream_tls_terminate_sock = "unix:" .. ngx.config.prefix() .. "/stream_tls_terminate.sock" +local stream_tls_passthrough_sock = "unix:" .. ngx.config.prefix() .. "/stream_tls_passthrough.sock" + -- for tests local _set_update_plugins_iterator local _set_update_router @@ -1121,9 +1124,31 @@ return { return exit(500) end + local route = match_t.route + -- if matched route doesn't do tls_passthrough and we are in the preread server block + -- this request should be TLS terminated; return immediately and not run further steps + -- (even bypassing the balancer) + local tls_preread_block = var.kong_tls_preread_block + if tls_preread_block and #tls_preread_block > 0 then + local protocols = route.protocols + if protocols and protocols.tls then + log(DEBUG, "TLS termination required, return to second layer proxying") + var.kong_tls_preread_block_upstream = stream_tls_terminate_sock + + elseif protocols and protocols.tls_passthrough then + var.kong_tls_preread_block_upstream = stream_tls_passthrough_sock + + else + log(ERR, "unexpected protocols in matched Route") + return exit(500) + end + + return true + end + + ctx.workspace = match_t.route and match_t.route.ws_id - local route = match_t.route local service = match_t.service local upstream_url_t = match_t.upstream_url_t diff --git a/kong/templates/nginx_kong_stream.lua b/kong/templates/nginx_kong_stream.lua index f2583996bdb6..a96d700b2949 100644 --- a/kong/templates/nginx_kong_stream.lua +++ b/kong/templates/nginx_kong_stream.lua @@ -86,10 +86,17 @@ upstream kong_upstream { } > if #stream_listeners > 0 then +# non-SSL listeners, and the SSL terminator server { > for _, entry in ipairs(stream_listeners) do +> if not entry.ssl then listen $(entry.listener); > end +> end + +> if stream_proxy_ssl_enabled then + listen unix:${{PREFIX}}/stream_tls_terminate.sock ssl proxy_protocol; +> end access_log ${{PROXY_STREAM_ACCESS_LOG}}; error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; @@ -97,6 +104,7 @@ server { > for _, ip in ipairs(trusted_ips) do set_real_ip_from $(ip); > end + set_real_ip_from unix:; # injected nginx_sproxy_* directives > for _, el in ipairs(nginx_sproxy_directives) do @@ -131,6 +139,69 @@ server { } } +> if stream_proxy_ssl_enabled then +# SSL listeners, but only preread the handshake here +server { +> for _, entry in ipairs(stream_listeners) do +> if entry.ssl then + listen $(entry.listener:gsub(" ssl", "")); +> end +> end + + access_log ${{PROXY_STREAM_ACCESS_LOG}}; + error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; + +> for _, ip in ipairs(trusted_ips) do + set_real_ip_from $(ip); +> end + + # injected nginx_sproxy_* directives +> for _, el in ipairs(nginx_sproxy_directives) do + $(el.name) $(el.value); +> end + + preread_by_lua_block { + Kong.preread() + } + + ssl_preread on; + + proxy_protocol on; + + set $kong_tls_preread_block 1; + set $kong_tls_preread_block_upstream ''; + proxy_pass $kong_tls_preread_block_upstream; +} + +server { + listen unix:${{PREFIX}}/stream_tls_passthrough.sock proxy_protocol; + + access_log ${{PROXY_STREAM_ACCESS_LOG}}; + error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; + + set_real_ip_from unix:; + + # injected nginx_sproxy_* directives +> for _, el in ipairs(nginx_sproxy_directives) do + $(el.name) $(el.value); +> end + + preread_by_lua_block { + Kong.preread() + } + + ssl_preread on; + + set $kong_tls_passthrough_block 1; + + proxy_pass kong_upstream; + + log_by_lua_block { + Kong.log() + } +} +> end -- stream_proxy_ssl_enabled + > if database == "off" then server { listen unix:${{PREFIX}}/stream_config.sock; diff --git a/spec/01-unit/01-db/01-schema/06-routes_spec.lua b/spec/01-unit/01-db/01-schema/06-routes_spec.lua index 44c79c21f273..bfa1b320d9d9 100644 --- a/spec/01-unit/01-db/01-schema/06-routes_spec.lua +++ b/spec/01-unit/01-db/01-schema/06-routes_spec.lua @@ -794,7 +794,7 @@ describe("routes schema", function() local ok, errs = Routes:validate(route) assert.falsy(ok) assert.same({ - paths = "cannot set 'paths' when 'protocols' is 'tcp', 'tls' or 'udp'", + paths = "cannot set 'paths' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'", }, errs) end end) @@ -811,7 +811,7 @@ describe("routes schema", function() local ok, errs = Routes:validate(route) assert.falsy(ok) assert.same({ - methods = "cannot set 'methods' when 'protocols' is 'tcp', 'tls' or 'udp'", + methods = "cannot set 'methods' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'", }, errs) end end) @@ -1010,7 +1010,7 @@ describe("routes schema", function() end end) - it("rejects specifying 'snis' if 'protocols' does not have 'https' or 'tls'", function() + it("rejects specifying 'snis' if 'protocols' does not have 'https', 'tls' or 'tls_passthrough'", function() local route = Routes:process_auto_fields({ protocols = { "tcp", "udp" }, snis = { "example.org" }, @@ -1020,7 +1020,7 @@ describe("routes schema", function() assert.falsy(ok) assert.same({ ["@entity"] = { - "'snis' can only be set when 'protocols' is 'grpcs', 'https' or 'tls'", + "'snis' can only be set when 'protocols' is 'grpcs', 'https', 'tls' or 'tls_passthrough'", }, snis = "length must be 0", }, errs) @@ -1179,4 +1179,32 @@ describe("routes schema", function() strip_path = "cannot set 'strip_path' when 'protocols' is 'grpc' or 'grpcs'" }, errs) end) + + it("errors if tls and tls_passthrough set on a same route", function() + local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" } + local route = Routes:process_auto_fields({ + snis = { "foo.grpc.com" }, + protocols = { "tls", "tls_passthrough" }, + service = s, + }, "insert") + local ok, errs = Routes:validate(route) + assert.falsy(ok) + assert.same({ + protocols = "these sets are mutually exclusive: ('tcp', 'tls', 'udp'), ('tls_passthrough')", + }, errs) + end) + + it("errors if snis is not set on tls_pasthrough", function() + local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" } + local route = Routes:process_auto_fields({ + sources = {{ ip = "127.0.0.1" }}, + protocols = { "tls_passthrough" }, + service = s, + }, "insert") + local ok, errs = Routes:validate(route) + assert.falsy(ok) + assert.same({ + ["@entity"] = { "must set snis when 'protocols' is 'tls_passthrough'" }, + }, errs) + end) end) diff --git a/spec/01-unit/01-db/01-schema/11-declarative_config/01-validate_spec.lua b/spec/01-unit/01-db/01-schema/11-declarative_config/01-validate_spec.lua index b65e11396463..785ddecd929e 100644 --- a/spec/01-unit/01-db/01-schema/11-declarative_config/01-validate_spec.lua +++ b/spec/01-unit/01-db/01-schema/11-declarative_config/01-validate_spec.lua @@ -190,7 +190,7 @@ describe("declarative config: validate", function() ["host"] = "expected a string", ["path"] = "must not have empty segments", ["port"] = "value should be between 0 and 65535", - ["protocol"] = "expected one of: grpc, grpcs, http, https, tcp, tls, udp", + ["protocol"] = "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp", ["retries"] = "value should be between 0 and 32767", } } diff --git a/spec/02-integration/02-cmd/02-start_stop_spec.lua b/spec/02-integration/02-cmd/02-start_stop_spec.lua index e064a43cdc3f..187ee7ce2b0f 100644 --- a/spec/02-integration/02-cmd/02-start_stop_spec.lua +++ b/spec/02-integration/02-cmd/02-start_stop_spec.lua @@ -532,7 +532,7 @@ describe("kong start/stop #" .. strategy, function() }) assert.falsy(ok) - assert.matches("in 'protocol': expected one of: grpc, grpcs, http, https, tcp, tls, udp", err, nil, true) + assert.matches("in 'protocol': expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp", err, nil, true) assert.matches("in 'name': invalid value '@gobo': the only accepted ascii characters are alphanumerics or ., -, _, and ~", err, nil, true) assert.matches("in entry 2 of 'hosts': invalid hostname: \\\\99", err, nil, true) end) diff --git a/spec/02-integration/03-db/02-db_core_entities_spec.lua b/spec/02-integration/03-db/02-db_core_entities_spec.lua index 3b40e9edfb67..6b927a10298a 100644 --- a/spec/02-integration/03-db/02-db_core_entities_spec.lua +++ b/spec/02-integration/03-db/02-db_core_entities_spec.lua @@ -1207,13 +1207,13 @@ for _, strategy in helpers.each_strategy() do strategy = strategy, message = unindent([[ 3 schema violations - ('snis' can only be set when 'protocols' is 'grpcs', 'https' or 'tls'; + ('snis' can only be set when 'protocols' is 'grpcs', 'https', 'tls' or 'tls_passthrough'; must set one of 'methods', 'hosts', 'headers', 'paths' when 'protocols' is 'http'; snis: length must be 0) ]], true, true), fields = { ["@entity"] = { - "'snis' can only be set when 'protocols' is 'grpcs', 'https' or 'tls'", + "'snis' can only be set when 'protocols' is 'grpcs', 'https', 'tls' or 'tls_passthrough'", "must set one of 'methods', 'hosts', 'headers', 'paths' when 'protocols' is 'http'", }, ["snis"] = "length must be 0", diff --git a/spec/02-integration/04-admin_api/09-routes_routes_spec.lua b/spec/02-integration/04-admin_api/09-routes_routes_spec.lua index c343a9135a30..61bfab0301ab 100644 --- a/spec/02-integration/04-admin_api/09-routes_routes_spec.lua +++ b/spec/02-integration/04-admin_api/09-routes_routes_spec.lua @@ -364,9 +364,9 @@ for _, strategy in helpers.each_strategy() do code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation " .. - "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, udp)", + "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)", fields = { - protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, udp" }, + protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" }, } }, cjson.decode(body)) @@ -384,12 +384,12 @@ for _, strategy in helpers.each_strategy() do code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "2 schema violations " .. - "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, udp; " .. - "service.protocol: expected one of: grpc, grpcs, http, https, tcp, tls, udp)", + "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp; " .. + "service.protocol: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)", fields = { - protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, udp" }, + protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" }, service = { - protocol = "expected one of: grpc, grpcs, http, https, tcp, tls, udp" + protocol = "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" } } }, cjson.decode(body)) @@ -928,9 +928,9 @@ for _, strategy in helpers.each_strategy() do code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation " .. - "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, udp)", + "(protocols.1: expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp)", fields = { - protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, udp" }, + protocols = { "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" }, } }, cjson.decode(body)) diff --git a/spec/02-integration/04-admin_api/10-services_routes_spec.lua b/spec/02-integration/04-admin_api/10-services_routes_spec.lua index 764b5984d299..e55e98004a19 100644 --- a/spec/02-integration/04-admin_api/10-services_routes_spec.lua +++ b/spec/02-integration/04-admin_api/10-services_routes_spec.lua @@ -822,7 +822,7 @@ for _, strategy in helpers.each_strategy() do }) body = assert.res_status(400, res) json = cjson.decode(body) - assert.same({ protocol = "expected one of: grpc, grpcs, http, https, tcp, tls, udp" }, json.fields) + assert.same({ protocol = "expected one of: grpc, grpcs, http, https, tcp, tls, tls_passthrough, udp" }, json.fields) end end) diff --git a/spec/02-integration/05-proxy/02-router_spec.lua b/spec/02-integration/05-proxy/02-router_spec.lua index e04aa7dec9f6..76661f5838c5 100644 --- a/spec/02-integration/05-proxy/02-router_spec.lua +++ b/spec/02-integration/05-proxy/02-router_spec.lua @@ -6,6 +6,7 @@ local path_handling_tests = require "spec.fixtures.router_path_handling_tests" local enable_buffering local enable_buffering_plugin +local stream_tls_listen_port = 9020 local function insert_routes(bp, routes) @@ -149,6 +150,7 @@ for _, strategy in helpers.each_strategy() do database = strategy, plugins = "bundled,enable-buffering", nginx_conf = "spec/fixtures/custom_nginx.template", + stream_listen = string.format("127.0.0.1:%d ssl", stream_tls_listen_port), }, nil, nil, fixtures)) end) @@ -1302,6 +1304,96 @@ for _, strategy in helpers.each_strategy() do end) end) + describe("tls_passthrough", function() + local routes + local proxy_ssl_client + + lazy_setup(function() + routes = insert_routes(bp, { + { + protocols = { "tls_passthrough" }, + snis = { "www.example.org" }, + service = { + name = "service_behind_www.example.org", + host = helpers.mock_upstream_ssl_host, + port = helpers.mock_upstream_ssl_port, + protocol = "tcp", + }, + }, + { + protocols = { "tls_passthrough" }, + snis = { "example.org" }, + service = { + name = "service_behind_example.org", + host = helpers.mock_upstream_ssl_host, + port = helpers.mock_upstream_ssl_port, + protocol = "tcp", + }, + }, + }) + end) + + lazy_teardown(function() + remove_routes(strategy, routes) + end) + + after_each(function() + if proxy_ssl_client then + proxy_ssl_client:close() + end + end) + + it("matches a Route based on its 'snis' attribute", function() + -- config propogates to stream subsystems not instantly + -- try up to 10 seconds with step of 2 seconds + -- in vagrant it takes around 6 seconds + helpers.wait_until(function() + proxy_ssl_client = helpers.http_client("127.0.0.1", stream_tls_listen_port) + local ok = proxy_ssl_client:ssl_handshake(nil, "www.example.org", false) -- explicit no-verify + if not ok then + proxy_ssl_client:close() + return false + end + return true + end, 10, 2) + + local res = assert(proxy_ssl_client:send { + method = "GET", + path = "/status/200", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(200, res) + + res = assert(proxy_ssl_client:send { + method = "GET", + path = "/status/201", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(201, res) + + proxy_ssl_client:close() + + proxy_ssl_client = helpers.http_client("127.0.0.1", stream_tls_listen_port) + assert(proxy_ssl_client:ssl_handshake(nil, "example.org", false)) -- explicit no-verify + + local res = assert(proxy_ssl_client:send { + method = "GET", + path = "/status/200", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(200, res) + + res = assert(proxy_ssl_client:send { + method = "GET", + path = "/status/201", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(201, res) + + proxy_ssl_client:close() + end) + end) + describe("[#headers]", function() local routes diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index b8fa9f991a71..ebf7640033ba 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -861,18 +861,27 @@ stream { } > if #stream_listeners > 0 then +# non-SSL listeners, and the SSL terminator server { > for _, entry in ipairs(stream_listeners) do +> if not entry.ssl then listen $(entry.listener); > end +> end - access_log ${{PROXY_ACCESS_LOG}} basic; - error_log ${{PROXY_ERROR_LOG}} ${{LOG_LEVEL}}; +> if stream_proxy_ssl_enabled then + listen unix:${{PREFIX}}/stream_tls_terminate.sock ssl proxy_protocol; +> end + + access_log ${{PROXY_STREAM_ACCESS_LOG}}; + error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; > for _, ip in ipairs(trusted_ips) do set_real_ip_from $(ip); > end - # injected nginx_sproxy_* directives + set_real_ip_from unix:; + + # injected nginx_sproxy_* directives > for _, el in ipairs(nginx_sproxy_directives) do $(el.name) $(el.value); > end @@ -887,6 +896,7 @@ stream { Kong.ssl_certificate() } > end + preread_by_lua_block { Kong.preread() } @@ -904,6 +914,70 @@ stream { } } +> if stream_proxy_ssl_enabled then +# SSL listeners, but only preread the handshake here + server { +> for _, entry in ipairs(stream_listeners) do +> if entry.ssl then + listen $(entry.listener:gsub(" ssl", "")); +> end +> end + + access_log ${{PROXY_STREAM_ACCESS_LOG}}; + error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; + +> for _, ip in ipairs(trusted_ips) do + set_real_ip_from $(ip); +> end + + # injected nginx_sproxy_* directives +> for _, el in ipairs(nginx_sproxy_directives) do + $(el.name) $(el.value); +> end + + preread_by_lua_block { + Kong.preread() + } + + ssl_preread on; + + proxy_protocol on; + + set $kong_tls_preread_block 1; + set $kong_tls_preread_block_upstream ''; + proxy_pass $kong_tls_preread_block_upstream; + } + +server { + listen unix:${{PREFIX}}/stream_tls_passthrough.sock proxy_protocol; + + access_log ${{PROXY_STREAM_ACCESS_LOG}}; + error_log ${{PROXY_STREAM_ERROR_LOG}} ${{LOG_LEVEL}}; + + set_real_ip_from unix:; + + # injected nginx_sproxy_* directives +> for _, el in ipairs(nginx_sproxy_directives) do + $(el.name) $(el.value); +> end + + preread_by_lua_block { + Kong.preread() + } + + ssl_preread on; + + set $kong_tls_passthrough_block 1; + + proxy_pass kong_upstream; + + log_by_lua_block { + Kong.log() + } + } +> end -- stream_proxy_ssl_enabled + + > if database == "off" then server { listen unix:${{PREFIX}}/stream_config.sock;