From ac2000d6c668befde4f3c3895cb11e86eaaeb320 Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Wed, 1 Mar 2017 17:49:03 -0800 Subject: [PATCH 01/15] feat(proxy) add real_ip configuration fields * Add real_ip_recursive and set_real_ip_from Kong configuration fields to configure ngx_http_realip_module directives. * Move the real_ip directives to the Kong proxy location block. * Add configuration building unit tests for those 2 new directives. Fix #1661 Deprecates #1662 --- kong.conf.default | 14 +++++++++++++ kong/conf_loader.lua | 2 ++ kong/templates/kong_defaults.lua | 2 ++ kong/templates/nginx_kong.lua | 8 ++++---- spec/01-unit/03-prefix_handler_spec.lua | 27 +++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/kong.conf.default b/kong.conf.default index 5acc2d75bb22..d20eb458ace8 100644 --- a/kong.conf.default +++ b/kong.conf.default @@ -115,6 +115,20 @@ # process. When this number is exceeded, the # least recently used connections are closed. +#real_ip_recursive = off # Sets the ngx_http_realip_module directive of + # the same name. +# Note: See http://nginx.org/en/docs/http/ngx_http_realip_module.html for a +# description of this directive. + +#set_real_ip_from = 0.0.0.0/0 # Defines trusted addresses that are known + # to send correct replacement addresses. + # If the special value unix: is specified, + # all UNIX-domain sockets will be trusted. + # This directive accepts a comma-separated + # list of values. +# Note: See http://nginx.org/en/docs/http/ngx_http_realip_module.html for a +# list of accepted values. + #------------------------------------------------------------------------------ # DATASTORE #------------------------------------------------------------------------------ diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index 831d386f224a..25c5c50d1625 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -61,6 +61,8 @@ local CONF_INFERENCES = { cluster_advertise = {typ = "string"}, nginx_worker_processes = {typ = "string"}, upstream_keepalive = {typ = "number"}, + real_ip_recursive = {typ = "ngx_boolean"}, + set_real_ip_from = {typ = "array"}, database = {enum = {"postgres", "cassandra"}}, pg_port = {typ = "number"}, diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 6e3851c3d537..38aa18c0114f 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -19,6 +19,8 @@ admin_ssl = on admin_ssl_cert = NONE admin_ssl_cert_key = NONE upstream_keepalive = 60 +real_ip_recursive = off +set_real_ip_from = NONE database = postgres pg_host = 127.0.0.1 diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index e689ce2369a9..eb5c35c66d59 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -23,10 +23,6 @@ client_max_body_size 0; proxy_ssl_server_name on; underscores_in_headers on; -real_ip_header X-Forwarded-For; -set_real_ip_from 0.0.0.0/0; -real_ip_recursive on; - lua_package_path '${{LUA_PACKAGE_PATH}};;'; lua_package_cpath '${{LUA_PACKAGE_CPATH}};;'; lua_code_cache ${{LUA_CODE_CACHE}}; @@ -100,6 +96,10 @@ server { kong.access() } + real_ip_recursive ${{REAL_IP_RECURSIVE}}; +> for i = 1, #set_real_ip_from do + set_real_ip_from $(set_real_ip_from[i]); +> end proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/spec/01-unit/03-prefix_handler_spec.lua b/spec/01-unit/03-prefix_handler_spec.lua index 993a96a347ec..c271ebea4ae6 100644 --- a/spec/01-unit/03-prefix_handler_spec.lua +++ b/spec/01-unit/03-prefix_handler_spec.lua @@ -121,6 +121,33 @@ describe("NGINX conf compiler", function() local nginx_conf = prefix_handler.compile_kong_conf(conf) assert.matches("error_log syslog:server=.+:61828 error;", nginx_conf) end) + + describe("ngx_http_realip_module settings", function() + it("defaults", function() + local conf = assert(conf_loader()) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("real_ip_recursive off;", nginx_conf, nil, true) + assert.not_matches("set_real_ip_from", nginx_conf, nil, true) + end) + + it("real_ip_recursive", function() + local conf = assert(conf_loader(nil, { + real_ip_recursive = true, + })) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("real_ip_recursive on;", nginx_conf, nil, true) + end) + + it("set_real_ip_from", function() + local conf = assert(conf_loader(nil, { + set_real_ip_from = "192.168.1.0/24,192.168.2.1,2001:0db8::/32" + })) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("set_real_ip_from 192.168.1.0/24", nginx_conf, nil, true) + assert.matches("set_real_ip_from 192.168.1.0", nginx_conf, nil, true) + assert.matches("set_real_ip_from 2001:0db8::/32", nginx_conf, nil, true) + end) + end) end) describe("compile_nginx_conf()", function() From 41934234c352f99e0f6246dee9038a4db544b949 Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Thu, 2 Mar 2017 16:10:38 -0800 Subject: [PATCH 02/15] feat(proxy) proper X-Forwarded-* upstream headers Partially fixes #2202 --- CHANGELOG.md | 2 + kong/templates/kong_defaults.lua | 2 +- kong/templates/nginx_kong.lua | 35 +++- .../05-proxy/02-real_ip_spec.lua | 191 ++++++++++++++++-- .../kong/plugins/headers-inspect/handler.lua | 23 +++ .../kong/plugins/headers-inspect/schema.lua | 3 + 6 files changed, 227 insertions(+), 29 deletions(-) create mode 100644 spec/fixtures/kong/plugins/headers-inspect/handler.lua create mode 100644 spec/fixtures/kong/plugins/headers-inspect/schema.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 458436335c3b..97366a5cd38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,8 @@ perform significantly better than any previous version. [#2069](https://github.com/Mashape/kong/pull/2069) - Support for `lua_socket_pool_size` property in configuration file. [#2109](https://github.com/Mashape/kong/pull/2109) +- Support for configuring the underlying ngx_http_realip_module directives + via new configuration properties: `real_ip_recursive` and `set_real_ip_from`. - Plugins: - :fireworks: **New AWS Lambda plugin**. Thanks Tim Erickson for his collaboration on this new addition. diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 38aa18c0114f..7ecad0a27f92 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -20,7 +20,7 @@ admin_ssl_cert = NONE admin_ssl_cert_key = NONE upstream_keepalive = 60 real_ip_recursive = off -set_real_ip_from = NONE +set_real_ip_from = 0.0.0.0/0 database = postgres pg_host = 127.0.0.1 diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index eb5c35c66d59..17c2c68b7123 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -70,6 +70,21 @@ map $http_upgrade $upstream_upgrade { websocket websocket; } +map $http_x_forwarded_proto $upstream_x_forwarded_proto { + default $http_x_forwarded_proto; + '' $scheme; +} + +map $http_x_forwarded_host $upstream_x_forwarded_host { + default $http_x_forwarded_host; + '' $http_host; +} + +map $http_x_forwarded_port $upstream_x_forwarded_port { + default $http_x_forwarded_port; + '' $server_port; +} + server { server_name kong; listen ${{PROXY_LISTEN}}; @@ -101,15 +116,17 @@ server { set_real_ip_from $(set_real_ip_from[i]); > end proxy_http_version 1.1; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $upstream_host; - proxy_set_header Upgrade $upstream_upgrade; - proxy_set_header Connection $upstream_connection; - - proxy_pass_header Server; - proxy_pass $upstream_scheme://kong_upstream; + proxy_set_header X-Real-IP $realip_remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto; + proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host; + proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port; + proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; + proxy_pass_header Server; + proxy_pass_header Date; + proxy_pass $upstream_scheme://kong_upstream; header_filter_by_lua_block { kong.header_filter() diff --git a/spec/02-integration/05-proxy/02-real_ip_spec.lua b/spec/02-integration/05-proxy/02-real_ip_spec.lua index 016a762ddbe5..ac4fca3c67cf 100644 --- a/spec/02-integration/05-proxy/02-real_ip_spec.lua +++ b/spec/02-integration/05-proxy/02-real_ip_spec.lua @@ -1,35 +1,188 @@ local helpers = require "spec.helpers" local cjson = require "cjson" -describe("Real IP proxying", function() +describe("Upstream headers", function() local client + setup(function() assert(helpers.dao.apis:insert { - name = "mockbin", - uris = { "/mockbin" }, - strip_uri = true, - upstream_url = "http://mockbin.com" + name = "headers-inspect", + uris = "/headers-inspect", + upstream_url = "http://placeholder.com", -- unused }) - assert(helpers.start_kong()) - client = helpers.proxy_client() + assert(helpers.dao.apis:insert { + name = "proxy-mock", + hosts = "proxy-mock.com", + upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", + }) + + assert(helpers.start_kong({ + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua" + })) + + local admin_client = helpers.admin_client() + + local res = assert(admin_client:send { + method = "POST", + path = "/apis/headers-inspect/plugins", + body = { + name = "headers-inspect" + }, + headers = { + ["Content-Type"] = "application/json" + } + }) + + assert.res_status(201, res) + + admin_client:close() end) teardown(function() - if client then client:close() end helpers.stop_kong() end) - it("X-Forwarded-* request headers", function() - local res = assert(client:send { - method = "GET", - path = "/mockbin/request", - }) - local body = assert.res_status(200, res) - local json = cjson.decode(body) - assert.matches("127.0.0.1", json.headers["x-forwarded-for"], nil, true) - assert.equal("80", json.headers["x-forwarded-port"]) - assert.equal("http", json.headers["x-forwarded-proto"]) - assert.equal("127.0.0.1", json.clientIPAddress) + before_each(function() + client = helpers.proxy_client() + end) + + after_each(function() + if client then + client:close() + end + end) + + describe("X-Forwarded-For", function() + it("if not present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("127.0.0.1", json["x-forwarded-for"]) + end) + + it("forwards value if present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "10.0.0.1", + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("10.0.0.1, 127.0.0.1", json["x-forwarded-for"]) + end) + end) + + describe("X-Forwarded-Proto", function() + it("if not present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("http", json["x-forwarded-proto"]) + end) + + it("if present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Proto"] = "https" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("https", json["x-forwarded-proto"]) + end) + end) + + describe("X-Forwarded-Host", function() + it("if not present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("proxy-mock.com", json["x-forwarded-host"]) + end) + + it("if present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Host"] = "example.com" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("example.com", json["x-forwarded-host"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("if not present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal(helpers.test_conf.proxy_port, tonumber(json["x-forwarded-port"])) + end) + + it("if present in request", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Port"] = "80" + } + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + + assert.equal("80", json["x-forwarded-port"]) + end) end) end) diff --git a/spec/fixtures/kong/plugins/headers-inspect/handler.lua b/spec/fixtures/kong/plugins/headers-inspect/handler.lua new file mode 100644 index 000000000000..dbcdd42d3837 --- /dev/null +++ b/spec/fixtures/kong/plugins/headers-inspect/handler.lua @@ -0,0 +1,23 @@ +local cjson = require "cjson" +local BasePlugin = require "kong.plugins.base_plugin" + + +local _M = BasePlugin:extend() + + +function _M:new() + _M.super.new(self, "headers-inspect") +end + + +function _M.access() + local headers = ngx.req.get_headers() + local json = cjson.encode(headers) + + ngx.status = 200 + ngx.say(json) + ngx.exit(200) +end + + +return _M diff --git a/spec/fixtures/kong/plugins/headers-inspect/schema.lua b/spec/fixtures/kong/plugins/headers-inspect/schema.lua new file mode 100644 index 000000000000..8b6db5093d13 --- /dev/null +++ b/spec/fixtures/kong/plugins/headers-inspect/schema.lua @@ -0,0 +1,3 @@ +return { + fields = {} +} From 8e2fcb1ec4ebeb1e9ec1be9f7e6041fa9a81e69f Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Thu, 16 Mar 2017 16:37:10 -0700 Subject: [PATCH 03/15] feat(proxy) add trusted_ips for forwarded headers * Introduce trusted_ips config property * Introduce real_ip_header * New lua-resty-mediador * Implement logic to validate client ips as trusted * Implement Lua logic for the X-Forwarded-For upstream header * Related tests (renamed real_ip test suite to upstream_headers) Fix #2202 --- CHANGELOG.md | 2 - kong-0.10.0-0.rockspec | 2 + kong.conf.default | 11 +- kong/conf_loader.lua | 27 +- kong/core/handler.lua | 20 +- kong/kong.lua | 2 + kong/singletons.lua | 1 + kong/templates/kong_defaults.lua | 3 +- kong/templates/nginx_kong.lua | 29 +- kong/tools/ip.lua | 56 ++ spec/01-unit/03-prefix_handler_spec.lua | 25 +- .../05-proxy/02-real_ip_spec.lua | 188 ------ .../05-proxy/02-upstream_headers_spec.lua | 610 ++++++++++++++++++ spec/03-plugins/17-jwt/03-access_spec.lua | 6 +- .../18-ip-restriction/02-access_spec.lua | 6 +- .../20-hmac-auth/03-access_spec.lua | 6 +- spec/fixtures/custom_nginx.template | 44 +- 17 files changed, 801 insertions(+), 237 deletions(-) create mode 100644 kong/tools/ip.lua delete mode 100644 spec/02-integration/05-proxy/02-real_ip_spec.lua create mode 100644 spec/02-integration/05-proxy/02-upstream_headers_spec.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 97366a5cd38a..458436335c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,8 +123,6 @@ perform significantly better than any previous version. [#2069](https://github.com/Mashape/kong/pull/2069) - Support for `lua_socket_pool_size` property in configuration file. [#2109](https://github.com/Mashape/kong/pull/2109) -- Support for configuring the underlying ngx_http_realip_module directives - via new configuration properties: `real_ip_recursive` and `set_real_ip_from`. - Plugins: - :fireworks: **New AWS Lambda plugin**. Thanks Tim Erickson for his collaboration on this new addition. diff --git a/kong-0.10.0-0.rockspec b/kong-0.10.0-0.rockspec index ac11c27ca30d..6c8f5fc021c1 100644 --- a/kong-0.10.0-0.rockspec +++ b/kong-0.10.0-0.rockspec @@ -30,6 +30,7 @@ dependencies = { "lua_pack == 1.0.4", "lua-resty-dns-client == 0.3.2", "lua-resty-worker-events == 0.3.0", + "lua-resty-mediador == 0.1.2", } build = { type = "builtin", @@ -79,6 +80,7 @@ build = { ["kong.api.routes.certificates"] = "kong/api/routes/certificates.lua", ["kong.api.routes.snis"] = "kong/api/routes/snis.lua", + ["kong.tools.ip"] = "kong/tools/ip.lua", ["kong.tools.dns"] = "kong/tools/dns.lua", ["kong.tools.utils"] = "kong/tools/utils.lua", ["kong.tools.public"] = "kong/tools/public.lua", diff --git a/kong.conf.default b/kong.conf.default index d20eb458ace8..6088f29b3cde 100644 --- a/kong.conf.default +++ b/kong.conf.default @@ -115,12 +115,17 @@ # process. When this number is exceeded, the # least recently used connections are closed. +#real_ip_header = X-Real-IP # Defines the request header field whose value + # will be used to replace the client address. +# Note: See http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header +# for a description of this directive. + #real_ip_recursive = off # Sets the ngx_http_realip_module directive of # the same name. -# Note: See http://nginx.org/en/docs/http/ngx_http_realip_module.html for a -# description of this directive. +# Note: See http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive +# for a description of this directive. -#set_real_ip_from = 0.0.0.0/0 # Defines trusted addresses that are known +#trusted_ips = # Defines trusted addresses that are known # to send correct replacement addresses. # If the special value unix: is specified, # all UNIX-domain sockets will be trusted. diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index 25c5c50d1625..cbacb2a28b7c 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -9,6 +9,7 @@ local pl_path = require "pl.path" local tablex = require "pl.tablex" local utils = require "kong.tools.utils" local log = require "kong.cmd.utils.log" +local ip = require "kong.tools.ip" local DEFAULT_PATHS = { "/etc/kong/kong.conf", @@ -61,8 +62,9 @@ local CONF_INFERENCES = { cluster_advertise = {typ = "string"}, nginx_worker_processes = {typ = "string"}, upstream_keepalive = {typ = "number"}, + real_ip_header = {typ = "string"}, real_ip_recursive = {typ = "ngx_boolean"}, - set_real_ip_from = {typ = "array"}, + trusted_ips = {typ = "array"}, database = {enum = {"postgres", "cassandra"}}, pg_port = {typ = "number"}, @@ -225,16 +227,16 @@ local function check_and_infer(conf) end end - local ip, port = utils.normalize_ipv4(conf.cluster_listen) - if not (ip and port) then + local address, port = utils.normalize_ipv4(conf.cluster_listen) + if not (address and port) then errors[#errors+1] = "cluster_listen must be in the form of IPv4:port" end - ip, port = utils.normalize_ipv4(conf.cluster_listen_rpc) - if not (ip and port) then + address, port = utils.normalize_ipv4(conf.cluster_listen_rpc) + if not (address and port) then errors[#errors+1] = "cluster_listen_rpc must be in the form of IPv4:port" end - ip, port = utils.normalize_ipv4(conf.cluster_advertise or "") - if conf.cluster_advertise and not (ip and port) then + address, port = utils.normalize_ipv4(conf.cluster_advertise or "") + if conf.cluster_advertise and not (address and port) then errors[#errors+1] = "cluster_advertise must be in the form of IPv4:port" end if conf.cluster_ttl_on_failure < 60 then @@ -244,6 +246,15 @@ local function check_and_infer(conf) conf.lua_package_cpath = "" end + -- Checking the trusted ips + for _, address in ipairs(conf.trusted_ips) do + if not ip.valid(address) and not address == "unix:" then + errors[#errors+1] = "trusted_ips must be a comma separated list in ".. + "the form of IPv4 or IPv6 address or CIDR ".. + "block or 'unix:', got '" .. address .. "'" + end + end + return #errors == 0, errors[1], errors end @@ -426,7 +437,7 @@ local function load(path, custom_conf) -- initialize the dns client, so the globally patched tcp.connect method -- will work from here onwards. assert(require("kong.tools.dns")(conf)) - + return setmetatable(conf, nil) -- remove Map mt end diff --git a/kong/core/handler.lua b/kong/core/handler.lua index da9e139880ca..f282f11900a8 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -19,6 +19,8 @@ local balancer_execute = require("kong.core.balancer").execute local router, router_err +local tostring = tostring +local ngx = ngx local ngx_now = ngx.now local server_header = _KONG._NAME.."/".._KONG._VERSION @@ -119,9 +121,25 @@ return { end -- if set `host_header` is the original header to be preserved - var.upstream_host = host_header or + var.upstream_host = host_header or balancer_address.hostname..":"..balancer_address.port + -- X-Forwarded Headers + local realip_remote_addr = var.realip_remote_addr + + if not singletons.ip.trusted(realip_remote_addr) then + var.upstream_x_forwarded_proto = var.scheme + var.upstream_x_forwarded_host = var.host + var.upstream_x_forwarded_port = var.server_port + end + + local http_x_forwarded_for = var.http_x_forwarded_for + if http_x_forwarded_for then + var.upstream_x_forwarded_for = http_x_forwarded_for .. ", " .. realip_remote_addr + + else + var.upstream_x_forwarded_for = realip_remote_addr + end end, -- Only executed if the `router` module found an API and allows nginx to proxy it. after = function() diff --git a/kong/kong.lua b/kong/kong.lua index 120714a33907..1f25e95fb0db 100644 --- a/kong/kong.lua +++ b/kong/kong.lua @@ -26,6 +26,7 @@ require("kong.core.globalpatches")() +local ip = require "kong.tools.ip" local dns = require "kong.tools.dns" local core = require "kong.core.handler" local Serf = require "kong.serf" @@ -134,6 +135,7 @@ function Kong.init() assert(dao:run_migrations()) -- migrating in case embedded in custom nginx -- populate singletons + singletons.ip = ip.init(config) singletons.dns = dns(config) singletons.loaded_plugins = assert(load_plugins(config, dao, events)) singletons.serf = Serf.new(config, dao) diff --git a/kong/singletons.lua b/kong/singletons.lua index faee14a42dd8..03f2f35349da 100644 --- a/kong/singletons.lua +++ b/kong/singletons.lua @@ -2,6 +2,7 @@ local _M = { configuration = nil, events = nil, dao = nil, + ip = nil, dns = nil, serf = nil, loaded_plugins = nil diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 7ecad0a27f92..7655723cde59 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -19,8 +19,9 @@ admin_ssl = on admin_ssl_cert = NONE admin_ssl_cert_key = NONE upstream_keepalive = 60 +real_ip_header = X-Real-IP real_ip_recursive = off -set_real_ip_from = 0.0.0.0/0 +trusted_ips = NONE database = postgres pg_host = 127.0.0.1 diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index 17c2c68b7123..e5d3eea51f59 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -61,28 +61,28 @@ upstream kong_upstream { } map $http_upgrade $upstream_connection { - default keep-alive; + default keep-alive; websocket upgrade; } map $http_upgrade $upstream_upgrade { - default ''; + default ''; websocket websocket; } map $http_x_forwarded_proto $upstream_x_forwarded_proto { default $http_x_forwarded_proto; - '' $scheme; + '' $scheme; } map $http_x_forwarded_host $upstream_x_forwarded_host { default $http_x_forwarded_host; - '' $http_host; + '' $host; } map $http_x_forwarded_port $upstream_x_forwarded_port { default $http_x_forwarded_port; - '' $server_port; + '' $server_port; } server { @@ -103,21 +103,24 @@ server { } > end + real_ip_header ${{REAL_IP_HEADER}}; + real_ip_recursive ${{REAL_IP_RECURSIVE}}; +> for i = 1, #trusted_ips do + set_real_ip_from $(trusted_ips[i]); +> end + location / { - set $upstream_host nil; - set $upstream_scheme nil; + set $upstream_host nil; + set $upstream_scheme nil; + set $upstream_x_forwarded_for nil; access_by_lua_block { kong.access() } - real_ip_recursive ${{REAL_IP_RECURSIVE}}; -> for i = 1, #set_real_ip_from do - set_real_ip_from $(set_real_ip_from[i]); -> end proxy_http_version 1.1; - proxy_set_header X-Real-IP $realip_remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $upstream_x_forwarded_for; proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto; proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host; proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port; diff --git a/kong/tools/ip.lua b/kong/tools/ip.lua new file mode 100644 index 000000000000..edc586487a66 --- /dev/null +++ b/kong/tools/ip.lua @@ -0,0 +1,56 @@ +local ip = require "resty.mediador.ip" +local px = require "resty.mediador.proxy" + + +local function trust_all() return true end +local function trust_none() return false end + + +local _M = { valid = ip.valid } + + +function _M.init(config) + local ips = config.trusted_ips + local count = #ips + local trusted_ips = {} + local trust_all_ipv4, trust_all_ipv6 + + -- This is because we don't support unix: that the ngx_http_realip module supports. + -- Also as an optimization we will only compile trusted ips if the Kong is not run + -- with the default 0.0.0.0/0, ::/0 aka trust all ip addresses settings. + for i = 1, count do + local address = ips[i] + + if ip.valid(address) then + trusted_ips[#trusted_ips+1] = address + if address == "0.0.0.0/0" then + trust_all_ipv4 = true + + elseif address == "::/0" then + trust_all_ipv6 = true + end + end + end + + if #trusted_ips == 0 then + return { + valid = ip.valid, + trusted = trust_none, + } + end + + if trust_all_ipv4 and trust_all_ipv6 then + return { + valid = ip.valid, + trusted = trust_all, + } + end + + return { + valid = ip.valid, + trusted = px.compile(trusted_ips), + } +end + + +return _M diff --git a/spec/01-unit/03-prefix_handler_spec.lua b/spec/01-unit/03-prefix_handler_spec.lua index c271ebea4ae6..b69fe4c6d370 100644 --- a/spec/01-unit/03-prefix_handler_spec.lua +++ b/spec/01-unit/03-prefix_handler_spec.lua @@ -126,26 +126,35 @@ describe("NGINX conf compiler", function() it("defaults", function() local conf = assert(conf_loader()) local nginx_conf = prefix_handler.compile_kong_conf(conf) - assert.matches("real_ip_recursive off;", nginx_conf, nil, true) - assert.not_matches("set_real_ip_from", nginx_conf, nil, true) + assert.matches("real_ip_header%s+X%-Real%-IP;", nginx_conf) + assert.matches("real_ip_recursive%s+off;", nginx_conf) + assert.not_matches("set_real_ip_from", nginx_conf) end) - it("real_ip_recursive", function() + it("real_ip_recursive on", function() local conf = assert(conf_loader(nil, { real_ip_recursive = true, })) local nginx_conf = prefix_handler.compile_kong_conf(conf) - assert.matches("real_ip_recursive on;", nginx_conf, nil, true) + assert.matches("real_ip_recursive%s+on;", nginx_conf) + end) + + it("real_ip_recursive off", function() + local conf = assert(conf_loader(nil, { + real_ip_recursive = false, + })) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("real_ip_recursive%s+off;", nginx_conf) end) it("set_real_ip_from", function() local conf = assert(conf_loader(nil, { - set_real_ip_from = "192.168.1.0/24,192.168.2.1,2001:0db8::/32" + trusted_ips = "192.168.1.0/24,192.168.2.1,2001:0db8::/32" })) local nginx_conf = prefix_handler.compile_kong_conf(conf) - assert.matches("set_real_ip_from 192.168.1.0/24", nginx_conf, nil, true) - assert.matches("set_real_ip_from 192.168.1.0", nginx_conf, nil, true) - assert.matches("set_real_ip_from 2001:0db8::/32", nginx_conf, nil, true) + assert.matches("set_real_ip_from%s+192.168.1.0/24", nginx_conf) + assert.matches("set_real_ip_from%s+192.168.1.0", nginx_conf) + assert.matches("set_real_ip_from%s+2001:0db8::/32", nginx_conf) end) end) end) diff --git a/spec/02-integration/05-proxy/02-real_ip_spec.lua b/spec/02-integration/05-proxy/02-real_ip_spec.lua deleted file mode 100644 index ac4fca3c67cf..000000000000 --- a/spec/02-integration/05-proxy/02-real_ip_spec.lua +++ /dev/null @@ -1,188 +0,0 @@ -local helpers = require "spec.helpers" -local cjson = require "cjson" - -describe("Upstream headers", function() - local client - - setup(function() - assert(helpers.dao.apis:insert { - name = "headers-inspect", - uris = "/headers-inspect", - upstream_url = "http://placeholder.com", -- unused - }) - - assert(helpers.dao.apis:insert { - name = "proxy-mock", - hosts = "proxy-mock.com", - upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", - }) - - assert(helpers.start_kong({ - custom_plugins = "headers-inspect", - lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua" - })) - - local admin_client = helpers.admin_client() - - local res = assert(admin_client:send { - method = "POST", - path = "/apis/headers-inspect/plugins", - body = { - name = "headers-inspect" - }, - headers = { - ["Content-Type"] = "application/json" - } - }) - - assert.res_status(201, res) - - admin_client:close() - end) - - teardown(function() - helpers.stop_kong() - end) - - before_each(function() - client = helpers.proxy_client() - end) - - after_each(function() - if client then - client:close() - end - end) - - describe("X-Forwarded-For", function() - it("if not present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("127.0.0.1", json["x-forwarded-for"]) - end) - - it("forwards value if present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com", - ["X-Forwarded-For"] = "10.0.0.1", - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("10.0.0.1, 127.0.0.1", json["x-forwarded-for"]) - end) - end) - - describe("X-Forwarded-Proto", function() - it("if not present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("http", json["x-forwarded-proto"]) - end) - - it("if present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com", - ["X-Forwarded-Proto"] = "https" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("https", json["x-forwarded-proto"]) - end) - end) - - describe("X-Forwarded-Host", function() - it("if not present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("proxy-mock.com", json["x-forwarded-host"]) - end) - - it("if present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com", - ["X-Forwarded-Host"] = "example.com" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("example.com", json["x-forwarded-host"]) - end) - end) - - describe("X-Forwarded-Port", function() - it("if not present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal(helpers.test_conf.proxy_port, tonumber(json["x-forwarded-port"])) - end) - - it("if present in request", function() - local res = assert(client:send { - method = "GET", - path = "/", - headers = { - ["Host"] = "proxy-mock.com", - ["X-Forwarded-Port"] = "80" - } - }) - - local body = assert.res_status(200, res) - local json = cjson.decode(body) - - assert.equal("80", json["x-forwarded-port"]) - end) - end) -end) diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua new file mode 100644 index 000000000000..de8c4065f355 --- /dev/null +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -0,0 +1,610 @@ +local helpers = require "spec.helpers" +local cjson = require "cjson" +local client + +local function insert_apis(arr) + if type(arr) ~= "table" then + return error("expected arg #1 to be a table", 2) + end + + helpers.dao:truncate_tables() + + for i = 1, #arr do + assert(helpers.dao.apis:insert(arr[i])) + end +end + +local function start_kong(config) + + insert_apis { + { + name = "headers-inspect", + uris = "/headers-inspect", + upstream_url = "http://placeholder.com", -- unused + }, + --[[ + { + name = "preserved", + hosts = "preserved.com", + preserve_host = true, + upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", + }, + --]] + { + name = "proxy-mock", + hosts = "proxy-mock.com", + upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", + } + } + + assert(helpers.start_kong(config)) + + local admin_client = helpers.admin_client() + + local res = assert(admin_client:send { + method = "POST", + path = "/apis/headers-inspect/plugins", + body = { + name = "headers-inspect", + }, + headers = { + ["Content-Type"] = "application/json", + } + }) + + assert.res_status(201, res) + + admin_client:close() +end + +local stop_kong = helpers.stop_kong + +local function request_headers(headers) + local res = assert(client:send { + method = "GET", + path = "/", + headers = headers, + }) + + local headers_json = assert.res_status(200, res) + + return cjson.decode(headers_json) +end + + +describe("Upstream header(s)", function() + + before_each(function() + client = helpers.proxy_client() + end) + + after_each(function() + if client then + client:close() + end + end) + + describe("(using the default configuration values)", function() + + setup(function() + start_kong { + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + } + end) + + teardown(stop_kong) + + describe("X-Real-IP", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Real-IP"] = "10.0.0.1", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + end) + end) + + describe("X-Forwarded-For", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + end) + + it("should be appended if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "10.0.0.1", + } + + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + end) + end) + + describe("X-Forwarded-Proto", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("http", headers["x-forwarded-proto"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Proto"] = "https", + } + + assert.equal("http", headers["x-forwarded-proto"]) + end) + end) + + describe("X-Forwarded-Host", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Host"] = "example.com", + } + + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + end) + + pending("with the downstream host preserved", function() +-- it("should be added if not present in request while preserving the downstream host", function() +-- local headers = request_headers { +-- ["Host"] = "preserved.com", +-- } +-- +-- assert.equal("preserved.com", headers["host"]) +-- assert.equal("127.0.0.1", headers["x-real-ip"]) +-- assert.equal("127.0.0.1", headers["x-forwarded-for"]) +-- assert.equal("http", headers["x-forwarded-proto"]) +-- assert.equal("preserved.com", headers["x-forwarded-host"]) +-- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) +-- end) +-- +-- it("should be added if present in request while preserving the downstream host", function() +-- local headers = request_headers { +-- ["Host"] = "preserved.com", +-- ["X-Real-IP"] = "10.0.0.1", +-- ["X-Forwarded-For"] = "10.0.0.1", +-- ["X-Forwarded-Proto"] = "https", +-- ["X-Forwarded-Host"] = "example.com", +-- ["X-Forwarded-Port"] = "80", +-- } +-- +-- assert.equal("preserved.com", headers["host"]) +-- assert.equal("127.0.0.1", headers["x-real-ip"]) +-- assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) +-- assert.equal("http", headers["x-forwarded-proto"]) +-- assert.equal("preserved.com", headers["x-forwarded-host"]) +-- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) +-- end) + end) + + describe("with the downstream host discarded", function() + it("should be added if not present in request while discarding the downstream host", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("if present in request while discarding the downstream host", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Real-IP"] = "10.0.0.1", + ["X-Forwarded-For"] = "10.0.0.1", + ["X-Forwarded-Proto"] = "https", + ["X-Forwarded-Host"] = "example.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + end) + + end) + + describe("(using the trusted configuration values)", function() + + setup(function() + start_kong { + trusted_ips = "127.0.0.1", + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + } + end) + + teardown(stop_kong) + + describe("X-Real-IP", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + end) + + it("should be forwarded if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Real-IP"] = "10.0.0.1", + } + + assert.equal("10.0.0.1", headers["x-real-ip"]) + end) + end) + + describe("X-Forwarded-For", function() + + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + end) + + it("should be appended if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "10.0.0.1", + } + + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + end) + + end) + + describe("X-Forwarded-Proto", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("http", headers["x-forwarded-proto"]) + end) + + it("should be forwarded if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Proto"] = "https", + } + + assert.equal("https", headers["x-forwarded-proto"]) + end) + end) + + describe("X-Forwarded-Host", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + end) + + it("should be forwarded if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Host"] = "example.com", + } + + assert.equal("example.com", headers["x-forwarded-host"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should be forwarded if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal("80", headers["x-forwarded-port"]) + end) + + end) + + end) + + describe("(using the non-trusted configuration values)", function() + + setup(function() + start_kong { + trusted_ips = "10.0.0.1", + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + } + end) + + teardown(stop_kong) + + describe("X-Real-IP", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Real-IP"] = "10.0.0.1", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + end) + end) + + describe("X-Forwarded-For", function() + + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + end) + + it("should be appended if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "10.0.0.1", + } + + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + end) + + end) + + describe("X-Forwarded-Proto", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("http", headers["x-forwarded-proto"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Proto"] = "https", + } + + assert.equal("http", headers["x-forwarded-proto"]) + end) + end) + + describe("X-Forwarded-Host", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Host"] = "example.com", + } + + assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should be replaced if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + end) + end) + + describe("(using the recursive trusted configuration values)", function() + + setup(function() + start_kong { + real_ip_header = "X-Forwarded-For", + real_ip_recursive = "on", + trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + } + end) + + teardown(stop_kong) + + describe("X-Real-IP and X-Forwarded-For", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + end) + + it("should be changed according to rules if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "127.0.0.1, 10.0.0.1, 192.168.0.1, 127.0.0.1, 172.16.0.1", + ["X-Real-IP"] = "10.0.0.2", + } + + assert.equal("10.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1, 10.0.0.1, 192.168.0.1, 127.0.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be forwarded even if X-Forwarded-For header has a port in it", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", + ["X-Real-IP"] = "10.0.0.2", + ["X-Forwarded-Port"] = "14", + } + + assert.equal("10.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(14, tonumber(headers["x-forwarded-port"])) + end) + + pending("should take a port from X-Forwarded-For header if it has a port in it", function() +-- local headers = request_headers { +-- ["Host"] = "proxy-mock.com", +-- ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", +-- ["X-Real-IP"] = "10.0.0.2", +-- } +-- +-- assert.equal("10.0.0.1", headers["x-real-ip"]) +-- assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) +-- assert.equal(16, tonumber(headers["x-forwarded-port"])) + end) + end) + end) + + describe("(using the recursice non-trusted configuration values)", function() + setup(function() + start_kong { + real_ip_header = "X-Forwarded-For", + real_ip_recursive = "on", + trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", + custom_plugins = "headers-inspect", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + } + end) + + teardown(stop_kong) + + describe("X-Real-IP and X-Forwarded-For", function() + it("should be added if not present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + end) + + it("should be changed according to rules if present in request", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1", + ["X-Real-IP"] = "10.0.0.2", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be replaced even if X-Forwarded-Port and X-Forwarded-For headers have a port in it", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", + ["X-Real-IP"] = "10.0.0.2", + ["X-Forwarded-Port"] = "14", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should not take a port from X-Forwarded-For header if it has a port in it", function() + local headers = request_headers { + ["Host"] = "proxy-mock.com", + ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", + ["X-Real-IP"] = "10.0.0.2", + } + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + end) + + end) +end) diff --git a/spec/03-plugins/17-jwt/03-access_spec.lua b/spec/03-plugins/17-jwt/03-access_spec.lua index 9e56becbd85a..075c1760d27e 100644 --- a/spec/03-plugins/17-jwt/03-access_spec.lua +++ b/spec/03-plugins/17-jwt/03-access_spec.lua @@ -51,7 +51,11 @@ describe("Plugin: jwt (access)", function() rsa_public_key = fixtures.rs256_public_key }) - assert(helpers.start_kong()) + assert(helpers.start_kong { + real_ip_header = "X-Forwarded-For", + real_ip_recursive = "on", + trusted_ips = "0.0.0.0/0, ::/0", + }) proxy_client = helpers.proxy_client() admin_client = helpers.admin_client() end) diff --git a/spec/03-plugins/18-ip-restriction/02-access_spec.lua b/spec/03-plugins/18-ip-restriction/02-access_spec.lua index 22a77720a166..df7cf152084e 100644 --- a/spec/03-plugins/18-ip-restriction/02-access_spec.lua +++ b/spec/03-plugins/18-ip-restriction/02-access_spec.lua @@ -97,7 +97,11 @@ describe("Plugin: ip-restriction (access)", function() } }) - assert(helpers.start_kong()) + assert(helpers.start_kong { + real_ip_header = "X-Forwarded-For", + real_ip_recursive = "on", + trusted_ips = "0.0.0.0/0, ::/0", + }) client = helpers.proxy_client() admin_client = helpers.admin_client() end) diff --git a/spec/03-plugins/20-hmac-auth/03-access_spec.lua b/spec/03-plugins/20-hmac-auth/03-access_spec.lua index 2ef70a9a95d5..47e885fcdbba 100644 --- a/spec/03-plugins/20-hmac-auth/03-access_spec.lua +++ b/spec/03-plugins/20-hmac-auth/03-access_spec.lua @@ -67,7 +67,11 @@ describe("Plugin: hmac-auth (access)", function() } }) - assert(helpers.start_kong()) + assert(helpers.start_kong { + real_ip_header = "X-Forwarded-For", + real_ip_recursive = "on", + trusted_ips = "0.0.0.0/0, ::/0", + }) client = helpers.proxy_client() end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index aa39ac4064dc..1bcff6fd43d9 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -75,15 +75,30 @@ http { } map $http_upgrade $upstream_connection { - default keep-alive; + default keep-alive; websocket upgrade; } map $http_upgrade $upstream_upgrade { - default ''; + default ''; websocket websocket; } + map $http_x_forwarded_proto $upstream_x_forwarded_proto { + default $http_x_forwarded_proto; + '' $scheme; + } + + map $http_x_forwarded_host $upstream_x_forwarded_host { + default $http_x_forwarded_host; + '' $host; + } + + map $http_x_forwarded_port $upstream_x_forwarded_port { + default $http_x_forwarded_port; + '' $server_port; + } + server { server_name kong; listen ${{PROXY_LISTEN}}; @@ -102,21 +117,30 @@ http { } > end + real_ip_header ${{REAL_IP_HEADER}}; + real_ip_recursive ${{REAL_IP_RECURSIVE}}; +> for i = 1, #trusted_ips do + set_real_ip_from $(trusted_ips[i]); +> end + location / { - set $upstream_host nil; - set $upstream_scheme nil; + set $upstream_host nil; + set $upstream_scheme nil; + set $upstream_x_forwarded_for nil; access_by_lua_block { kong.access() } proxy_http_version 1.1; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $upstream_host; - proxy_set_header Upgrade $upstream_upgrade; - proxy_set_header Connection $upstream_connection; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $upstream_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto; + proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host; + proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port; + proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; proxy_pass_header Server; proxy_pass $upstream_scheme://kong_upstream; From f885be228728cf32c87f6c6de653688803c7998f Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Thu, 23 Mar 2017 17:47:29 -0700 Subject: [PATCH 04/15] Added related entries and information to the CHANGELOG.md. --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 458436335c3b..34dbf542bc81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,19 @@ - Admin API: - Disable support for TLS/1.0. [#2212](https://github.com/Mashape/kong/pull/2212) +- :warning: The default configuration for `X-Forwarded-*` and `X-Real-IP` + upstream headers was changed considerably. Previously Kong trusted and + forwarded those headers by default. With this release we do **not** trust + those headers anymore by default. This is all configurable by now. Please + read more about this change from our + [0.10.x Proxy Reference](https://getkong.org/docs/0.10.x/proxy/#3-proxying-upstream-timeouts) + and + [0.10.x Configuration Reference](https://getkong.org/docs/0.10.x/configuration/#real_ip_header) ### Added +- :fireworks: Configurable `X-Forwarded-*` and `X-Real-IP` upstream headers. + [#2236](https://github.com/Mashape/kong/pull/2236) - Plugins: - cors: Support for configuring multiple Origin domains. [#2203](https://github.com/Mashape/kong/pull/2203) From 77b394b00fa4b788a9f32087ad5944793ad92218 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Fri, 24 Mar 2017 10:14:22 -0700 Subject: [PATCH 05/15] Added small notes to pending tests. --- spec/02-integration/05-proxy/02-upstream_headers_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua index de8c4065f355..9f024ac28bf8 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -190,7 +190,7 @@ describe("Upstream header(s)", function() end) end) - pending("with the downstream host preserved", function() + pending("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() -- it("should be added if not present in request while preserving the downstream host", function() -- local headers = request_headers { -- ["Host"] = "preserved.com", @@ -530,7 +530,7 @@ describe("Upstream header(s)", function() assert.equal(14, tonumber(headers["x-forwarded-port"])) end) - pending("should take a port from X-Forwarded-For header if it has a port in it", function() + pending("should take a port from X-Forwarded-For header if it has a port in it (pending: because it is a rare edge case)", function() -- local headers = request_headers { -- ["Host"] = "proxy-mock.com", -- ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", From 618c19b70a9bf287656c87d210ada3c2810ca703 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Mon, 27 Mar 2017 14:29:57 +0300 Subject: [PATCH 06/15] Add support for proxy protocol Fix #2240 --- kong/core/handler.lua | 2 +- kong/templates/nginx_kong.lua | 8 + spec/01-unit/03-prefix_handler_spec.lua | 9 + .../05-proxy/02-upstream_headers_spec.lua | 487 ++++++++++++------ spec/fixtures/custom_nginx.template | 18 +- 5 files changed, 369 insertions(+), 155 deletions(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index f282f11900a8..b742677c9ec1 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -138,7 +138,7 @@ return { var.upstream_x_forwarded_for = http_x_forwarded_for .. ", " .. realip_remote_addr else - var.upstream_x_forwarded_for = realip_remote_addr + var.upstream_x_forwarded_for = var.remote_addr .. ", " .. realip_remote_addr end end, -- Only executed if the `router` module found an API and allows nginx to proxy it. diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index e5d3eea51f59..bf469ea631a7 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -87,14 +87,22 @@ map $http_x_forwarded_port $upstream_x_forwarded_port { server { server_name kong; +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN}} proxy_protocol; +> else listen ${{PROXY_LISTEN}}; +> end error_page 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; access_log logs/access.log; > if ssl then +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN_SSL}} proxy_protocol ssl; +> else listen ${{PROXY_LISTEN_SSL}} ssl; +> end ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; diff --git a/spec/01-unit/03-prefix_handler_spec.lua b/spec/01-unit/03-prefix_handler_spec.lua index b69fe4c6d370..d79e1b112e4e 100644 --- a/spec/01-unit/03-prefix_handler_spec.lua +++ b/spec/01-unit/03-prefix_handler_spec.lua @@ -156,6 +156,15 @@ describe("NGINX conf compiler", function() assert.matches("set_real_ip_from%s+192.168.1.0", nginx_conf) assert.matches("set_real_ip_from%s+2001:0db8::/32", nginx_conf) end) + it("proxy_protocol", function() + local conf = assert(conf_loader(nil, { + real_ip_header = "proxy_protocol" + })) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("real_ip_header%s+proxy_protocol", nginx_conf) + assert.matches("listen 0.0.0.0:8000 proxy_protocol;", nginx_conf) + assert.matches("listen 0.0.0.0:8443 proxy_protocol ssl;", nginx_conf) + end) end) end) diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua index 9f024ac28bf8..a24df803a49f 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -15,46 +15,44 @@ local function insert_apis(arr) end local function start_kong(config) - - insert_apis { - { - name = "headers-inspect", - uris = "/headers-inspect", - upstream_url = "http://placeholder.com", -- unused - }, - --[[ - { - name = "preserved", - hosts = "preserved.com", - preserve_host = true, - upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", - }, - --]] - { - name = "proxy-mock", - hosts = "proxy-mock.com", - upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", + return function() + insert_apis { + { + name = "headers-inspect", + upstream_url = "http://localhost:9999/headers-inspect", + hosts = { + "headers-inspect.com", + } + }, + { + name = "preserved", + hosts = { + "preserved.com", + }, + preserve_host = true, + upstream_url = "http://localhost:9999/headers-inspect", + }, } - } - assert(helpers.start_kong(config)) + assert(helpers.start_kong(config)) - local admin_client = helpers.admin_client() + local admin_client = helpers.admin_client() - local res = assert(admin_client:send { - method = "POST", - path = "/apis/headers-inspect/plugins", - body = { - name = "headers-inspect", - }, - headers = { - ["Content-Type"] = "application/json", - } - }) + local res = assert(admin_client:send { + method = "POST", + path = "/apis/headers-inspect/plugins", + body = { + name = "headers-inspect", + }, + headers = { + ["Content-Type"] = "application/json", + } + }) - assert.res_status(201, res) + assert.res_status(201, res) - admin_client:close() + admin_client:close() + end end local stop_kong = helpers.stop_kong @@ -86,19 +84,17 @@ describe("Upstream header(s)", function() describe("(using the default configuration values)", function() - setup(function() - start_kong { - custom_plugins = "headers-inspect", + setup(start_kong { + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -106,7 +102,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -117,15 +113,15 @@ describe("Upstream header(s)", function() describe("X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) end) it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -136,7 +132,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -144,7 +140,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -155,26 +151,26 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) end) describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -182,7 +178,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -190,56 +186,56 @@ describe("Upstream header(s)", function() end) end) - pending("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() --- it("should be added if not present in request while preserving the downstream host", function() --- local headers = request_headers { --- ["Host"] = "preserved.com", --- } --- --- assert.equal("preserved.com", headers["host"]) --- assert.equal("127.0.0.1", headers["x-real-ip"]) --- assert.equal("127.0.0.1", headers["x-forwarded-for"]) --- assert.equal("http", headers["x-forwarded-proto"]) --- assert.equal("preserved.com", headers["x-forwarded-host"]) --- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) --- end) --- --- it("should be added if present in request while preserving the downstream host", function() --- local headers = request_headers { --- ["Host"] = "preserved.com", --- ["X-Real-IP"] = "10.0.0.1", --- ["X-Forwarded-For"] = "10.0.0.1", --- ["X-Forwarded-Proto"] = "https", --- ["X-Forwarded-Host"] = "example.com", --- ["X-Forwarded-Port"] = "80", --- } --- --- assert.equal("preserved.com", headers["host"]) --- assert.equal("127.0.0.1", headers["x-real-ip"]) --- assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) --- assert.equal("http", headers["x-forwarded-proto"]) --- assert.equal("preserved.com", headers["x-forwarded-host"]) --- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) --- end) + describe("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() + it("should be added if not present in request while preserving the downstream host", function() + local headers = request_headers { + ["Host"] = "preserved.com", + } + + assert.equal("preserved.com", headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("preserved.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should be added if present in request while preserving the downstream host", function() + local headers = request_headers { + ["Host"] = "preserved.com", + ["X-Real-IP"] = "10.0.0.1", + ["X-Forwarded-For"] = "10.0.0.1", + ["X-Forwarded-Proto"] = "https", + ["X-Forwarded-Host"] = "example.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal("preserved.com", headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("preserved.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) end) describe("with the downstream host discarded", function() it("should be added if not present in request while discarding the downstream host", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("localhost:9999", headers["host"]) assert.equal("127.0.0.1", headers["x-real-ip"]) - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) end) it("if present in request while discarding the downstream host", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", ["X-Forwarded-For"] = "10.0.0.1", ["X-Forwarded-Proto"] = "https", @@ -247,11 +243,11 @@ describe("Upstream header(s)", function() ["X-Forwarded-Port"] = "80", } - assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("localhost:9999", headers["host"]) assert.equal("127.0.0.1", headers["x-real-ip"]) assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) end) end) @@ -260,20 +256,18 @@ describe("Upstream header(s)", function() describe("(using the trusted configuration values)", function() - setup(function() - start_kong { - trusted_ips = "127.0.0.1", - custom_plugins = "headers-inspect", + setup(start_kong { + trusted_ips = "127.0.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -281,7 +275,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -293,15 +287,15 @@ describe("Upstream header(s)", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) end) it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -313,7 +307,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -321,7 +315,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -332,15 +326,15 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } @@ -351,7 +345,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -359,7 +353,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -372,20 +366,18 @@ describe("Upstream header(s)", function() describe("(using the non-trusted configuration values)", function() - setup(function() - start_kong { + setup(start_kong { trusted_ips = "10.0.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -393,7 +385,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -405,15 +397,15 @@ describe("Upstream header(s)", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) end) it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -425,7 +417,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -433,7 +425,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -444,26 +436,26 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) end) describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -471,7 +463,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -482,31 +474,29 @@ describe("Upstream header(s)", function() describe("(using the recursive trusted configuration values)", function() - setup(function() - start_kong { + setup(start_kong { real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP and X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) end) it("should be changed according to rules if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1, 10.0.0.1, 192.168.0.1, 127.0.0.1, 172.16.0.1", ["X-Real-IP"] = "10.0.0.2", } @@ -519,7 +509,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be forwarded even if X-Forwarded-For header has a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", ["X-Forwarded-Port"] = "14", @@ -532,7 +522,7 @@ describe("Upstream header(s)", function() pending("should take a port from X-Forwarded-For header if it has a port in it (pending: because it is a rare edge case)", function() -- local headers = request_headers { --- ["Host"] = "proxy-mock.com", +-- ["Host"] = "headers-inspect.com", -- ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", -- ["X-Real-IP"] = "10.0.0.2", -- } @@ -544,32 +534,30 @@ describe("Upstream header(s)", function() end) end) - describe("(using the recursice non-trusted configuration values)", function() - setup(function() - start_kong { + describe("(using the recursive non-trusted configuration values)", function() + setup(start_kong { real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP and X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) - assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) end) it("should be changed according to rules if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1", ["X-Real-IP"] = "10.0.0.2", } @@ -582,7 +570,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be replaced even if X-Forwarded-Port and X-Forwarded-For headers have a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", ["X-Forwarded-Port"] = "14", @@ -595,7 +583,7 @@ describe("Upstream header(s)", function() it("should not take a port from X-Forwarded-For header if it has a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", } @@ -607,4 +595,209 @@ describe("Upstream header(s)", function() end) end) + + describe("(using trusted proxy protocol configuration values)", function() + + setup(start_kong { + real_ip_header = "proxy_protocol", + real_ip_recursive = "on", + trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + }) + + teardown(stop_kong) + + describe("X-Real-IP, X-Forwarded-For and X-Forwarded-Port", function() + it("should be added if not present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("192.168.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + + it("should be changed according to rules if present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert(sock:close()) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be forwarded even if proxy protocol and X-Forwarded-For header has a port in it", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18\r\n" .. + "X-Forwarded-Port: 14\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(14, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + end) + end) + + describe("(using non-trusted proxy protocol configuration values)", function() + + setup(start_kong { + real_ip_header = "proxy_protocol", + real_ip_recursive = "on", + trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + }) + + teardown(stop_kong) + + describe("X-Real-IP, X-Forwarded-For and X-Forwarded-Port", function() + it("should be added if not present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + + it("should be changed according to rules if present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert(sock:close()) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be replaced even if proxy protocol, X-Forwarded-Port and X-Forwarded-For headers have a port in it", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18\r\n" .. + "X-Forwarded-Port: 14\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + end) + end) + end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 1bcff6fd43d9..35d8b39da25b 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -33,10 +33,6 @@ http { proxy_ssl_server_name on; underscores_in_headers on; - real_ip_header X-Forwarded-For; - set_real_ip_from 0.0.0.0/0; - real_ip_recursive on; - lua_package_path '${{LUA_PACKAGE_PATH}};;'; lua_package_cpath '${{LUA_PACKAGE_CPATH}};;'; lua_code_cache ${{LUA_CODE_CACHE}}; @@ -101,14 +97,22 @@ http { server { server_name kong; +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN}} proxy_protocol; +> else listen ${{PROXY_LISTEN}}; +> end error_page 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; access_log logs/access.log; > if ssl then +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN_SSL}} proxy_protocol ssl; +> else listen ${{PROXY_LISTEN_SSL}} ssl; +> end ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -141,9 +145,9 @@ http { proxy_set_header Host $upstream_host; proxy_set_header Upgrade $upstream_upgrade; proxy_set_header Connection $upstream_connection; - - proxy_pass_header Server; - proxy_pass $upstream_scheme://kong_upstream; + proxy_pass_header Server; + proxy_pass_header Date; + proxy_pass $upstream_scheme://kong_upstream; header_filter_by_lua_block { kong.header_filter() From ccc47819b52f59f9410ff9c9245a81e6330bd578 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Mon, 27 Mar 2017 14:29:57 +0300 Subject: [PATCH 07/15] Add support for proxy protocol Fix #2240 --- kong/core/handler.lua | 2 +- kong/templates/nginx_kong.lua | 8 + spec/01-unit/03-prefix_handler_spec.lua | 9 + .../05-proxy/02-upstream_headers_spec.lua | 475 ++++++++++++------ spec/fixtures/custom_nginx.template | 18 +- 5 files changed, 363 insertions(+), 149 deletions(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index f282f11900a8..995ff00fd226 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -138,7 +138,7 @@ return { var.upstream_x_forwarded_for = http_x_forwarded_for .. ", " .. realip_remote_addr else - var.upstream_x_forwarded_for = realip_remote_addr + var.upstream_x_forwarded_for = var.remote_addr end end, -- Only executed if the `router` module found an API and allows nginx to proxy it. diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index e5d3eea51f59..bf469ea631a7 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -87,14 +87,22 @@ map $http_x_forwarded_port $upstream_x_forwarded_port { server { server_name kong; +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN}} proxy_protocol; +> else listen ${{PROXY_LISTEN}}; +> end error_page 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; access_log logs/access.log; > if ssl then +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN_SSL}} proxy_protocol ssl; +> else listen ${{PROXY_LISTEN_SSL}} ssl; +> end ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; diff --git a/spec/01-unit/03-prefix_handler_spec.lua b/spec/01-unit/03-prefix_handler_spec.lua index b69fe4c6d370..d79e1b112e4e 100644 --- a/spec/01-unit/03-prefix_handler_spec.lua +++ b/spec/01-unit/03-prefix_handler_spec.lua @@ -156,6 +156,15 @@ describe("NGINX conf compiler", function() assert.matches("set_real_ip_from%s+192.168.1.0", nginx_conf) assert.matches("set_real_ip_from%s+2001:0db8::/32", nginx_conf) end) + it("proxy_protocol", function() + local conf = assert(conf_loader(nil, { + real_ip_header = "proxy_protocol" + })) + local nginx_conf = prefix_handler.compile_kong_conf(conf) + assert.matches("real_ip_header%s+proxy_protocol", nginx_conf) + assert.matches("listen 0.0.0.0:8000 proxy_protocol;", nginx_conf) + assert.matches("listen 0.0.0.0:8443 proxy_protocol ssl;", nginx_conf) + end) end) end) diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua index 9f024ac28bf8..cb1fb4e19502 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -15,46 +15,44 @@ local function insert_apis(arr) end local function start_kong(config) - - insert_apis { - { - name = "headers-inspect", - uris = "/headers-inspect", - upstream_url = "http://placeholder.com", -- unused - }, - --[[ - { - name = "preserved", - hosts = "preserved.com", - preserve_host = true, - upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", - }, - --]] - { - name = "proxy-mock", - hosts = "proxy-mock.com", - upstream_url = "http://" .. helpers.test_conf.proxy_listen .. "/headers-inspect", + return function() + insert_apis { + { + name = "headers-inspect", + upstream_url = "http://localhost:9999/headers-inspect", + hosts = { + "headers-inspect.com", + } + }, + { + name = "preserved", + hosts = { + "preserved.com", + }, + preserve_host = true, + upstream_url = "http://localhost:9999/headers-inspect", + }, } - } - assert(helpers.start_kong(config)) + assert(helpers.start_kong(config)) - local admin_client = helpers.admin_client() + local admin_client = helpers.admin_client() - local res = assert(admin_client:send { - method = "POST", - path = "/apis/headers-inspect/plugins", - body = { - name = "headers-inspect", - }, - headers = { - ["Content-Type"] = "application/json", - } - }) + local res = assert(admin_client:send { + method = "POST", + path = "/apis/headers-inspect/plugins", + body = { + name = "headers-inspect", + }, + headers = { + ["Content-Type"] = "application/json", + } + }) - assert.res_status(201, res) + assert.res_status(201, res) - admin_client:close() + admin_client:close() + end end local stop_kong = helpers.stop_kong @@ -86,19 +84,17 @@ describe("Upstream header(s)", function() describe("(using the default configuration values)", function() - setup(function() - start_kong { - custom_plugins = "headers-inspect", + setup(start_kong { + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -106,7 +102,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -117,7 +113,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-forwarded-for"]) @@ -125,7 +121,7 @@ describe("Upstream header(s)", function() it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -136,7 +132,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -144,7 +140,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -155,26 +151,26 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) end) describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -182,7 +178,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -190,56 +186,56 @@ describe("Upstream header(s)", function() end) end) - pending("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() --- it("should be added if not present in request while preserving the downstream host", function() --- local headers = request_headers { --- ["Host"] = "preserved.com", --- } --- --- assert.equal("preserved.com", headers["host"]) --- assert.equal("127.0.0.1", headers["x-real-ip"]) --- assert.equal("127.0.0.1", headers["x-forwarded-for"]) --- assert.equal("http", headers["x-forwarded-proto"]) --- assert.equal("preserved.com", headers["x-forwarded-host"]) --- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) --- end) --- --- it("should be added if present in request while preserving the downstream host", function() --- local headers = request_headers { --- ["Host"] = "preserved.com", --- ["X-Real-IP"] = "10.0.0.1", --- ["X-Forwarded-For"] = "10.0.0.1", --- ["X-Forwarded-Proto"] = "https", --- ["X-Forwarded-Host"] = "example.com", --- ["X-Forwarded-Port"] = "80", --- } --- --- assert.equal("preserved.com", headers["host"]) --- assert.equal("127.0.0.1", headers["x-real-ip"]) --- assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) --- assert.equal("http", headers["x-forwarded-proto"]) --- assert.equal("preserved.com", headers["x-forwarded-host"]) --- assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) --- end) + describe("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() + it("should be added if not present in request while preserving the downstream host", function() + local headers = request_headers { + ["Host"] = "preserved.com", + } + + assert.equal("preserved.com", headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("preserved.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) + + it("should be added if present in request while preserving the downstream host", function() + local headers = request_headers { + ["Host"] = "preserved.com", + ["X-Real-IP"] = "10.0.0.1", + ["X-Forwarded-For"] = "10.0.0.1", + ["X-Forwarded-Proto"] = "https", + ["X-Forwarded-Host"] = "example.com", + ["X-Forwarded-Port"] = "80", + } + + assert.equal("preserved.com", headers["host"]) + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal("http", headers["x-forwarded-proto"]) + assert.equal("preserved.com", headers["x-forwarded-host"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + end) end) describe("with the downstream host discarded", function() it("should be added if not present in request while discarding the downstream host", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("localhost:9999", headers["host"]) assert.equal("127.0.0.1", headers["x-real-ip"]) assert.equal("127.0.0.1", headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) end) it("if present in request while discarding the downstream host", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", ["X-Forwarded-For"] = "10.0.0.1", ["X-Forwarded-Proto"] = "https", @@ -247,11 +243,11 @@ describe("Upstream header(s)", function() ["X-Forwarded-Port"] = "80", } - assert.equal(helpers.test_conf.proxy_listen, headers["host"]) + assert.equal("localhost:9999", headers["host"]) assert.equal("127.0.0.1", headers["x-real-ip"]) assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) end) end) @@ -260,20 +256,18 @@ describe("Upstream header(s)", function() describe("(using the trusted configuration values)", function() - setup(function() - start_kong { - trusted_ips = "127.0.0.1", - custom_plugins = "headers-inspect", + setup(start_kong { + trusted_ips = "127.0.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -281,7 +275,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -293,7 +287,7 @@ describe("Upstream header(s)", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-forwarded-for"]) @@ -301,7 +295,7 @@ describe("Upstream header(s)", function() it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -313,7 +307,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -321,7 +315,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -332,15 +326,15 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } @@ -351,7 +345,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -359,7 +353,7 @@ describe("Upstream header(s)", function() it("should be forwarded if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -372,20 +366,18 @@ describe("Upstream header(s)", function() describe("(using the non-trusted configuration values)", function() - setup(function() - start_kong { + setup(start_kong { trusted_ips = "10.0.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -393,7 +385,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Real-IP"] = "10.0.0.1", } @@ -405,7 +397,7 @@ describe("Upstream header(s)", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-forwarded-for"]) @@ -413,7 +405,7 @@ describe("Upstream header(s)", function() it("should be appended if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1", } @@ -425,7 +417,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Proto", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("http", headers["x-forwarded-proto"]) @@ -433,7 +425,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Proto"] = "https", } @@ -444,26 +436,26 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Host", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Host"] = "example.com", } - assert.equal("proxy-mock.com", headers["x-forwarded-host"]) + assert.equal("headers-inspect.com", headers["x-forwarded-host"]) end) end) describe("X-Forwarded-Port", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) @@ -471,7 +463,7 @@ describe("Upstream header(s)", function() it("should be replaced if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-Port"] = "80", } @@ -482,22 +474,20 @@ describe("Upstream header(s)", function() describe("(using the recursive trusted configuration values)", function() - setup(function() - start_kong { + setup(start_kong { real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP and X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -506,7 +496,7 @@ describe("Upstream header(s)", function() it("should be changed according to rules if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1, 10.0.0.1, 192.168.0.1, 127.0.0.1, 172.16.0.1", ["X-Real-IP"] = "10.0.0.2", } @@ -519,7 +509,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be forwarded even if X-Forwarded-For header has a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", ["X-Forwarded-Port"] = "14", @@ -532,7 +522,7 @@ describe("Upstream header(s)", function() pending("should take a port from X-Forwarded-For header if it has a port in it (pending: because it is a rare edge case)", function() -- local headers = request_headers { --- ["Host"] = "proxy-mock.com", +-- ["Host"] = "headers-inspect.com", -- ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", -- ["X-Real-IP"] = "10.0.0.2", -- } @@ -544,23 +534,21 @@ describe("Upstream header(s)", function() end) end) - describe("(using the recursice non-trusted configuration values)", function() - setup(function() - start_kong { + describe("(using the recursive non-trusted configuration values)", function() + setup(start_kong { real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", - custom_plugins = "headers-inspect", + nginx_conf = "spec/fixtures/custom_nginx.template", lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", - } - end) + }) teardown(stop_kong) describe("X-Real-IP and X-Forwarded-For", function() it("should be added if not present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", } assert.equal("127.0.0.1", headers["x-real-ip"]) @@ -569,7 +557,7 @@ describe("Upstream header(s)", function() it("should be changed according to rules if present in request", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1", ["X-Real-IP"] = "10.0.0.2", } @@ -582,7 +570,7 @@ describe("Upstream header(s)", function() describe("X-Forwarded-Port", function() it("should be replaced even if X-Forwarded-Port and X-Forwarded-For headers have a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", ["X-Forwarded-Port"] = "14", @@ -595,7 +583,7 @@ describe("Upstream header(s)", function() it("should not take a port from X-Forwarded-For header if it has a port in it", function() local headers = request_headers { - ["Host"] = "proxy-mock.com", + ["Host"] = "headers-inspect.com", ["X-Forwarded-For"] = "127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18", ["X-Real-IP"] = "10.0.0.2", } @@ -607,4 +595,209 @@ describe("Upstream header(s)", function() end) end) + + describe("(using trusted proxy protocol configuration values)", function() + + setup(start_kong { + real_ip_header = "proxy_protocol", + real_ip_recursive = "on", + trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + }) + + teardown(stop_kong) + + describe("X-Real-IP, X-Forwarded-For and X-Forwarded-Port", function() + it("should be added if not present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("192.168.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + + it("should be changed according to rules if present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert(sock:close()) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be forwarded even if proxy protocol and X-Forwarded-For header has a port in it", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18\r\n" .. + "X-Forwarded-Port: 14\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("192.168.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(14, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + end) + end) + + describe("(using non-trusted proxy protocol configuration values)", function() + + setup(start_kong { + real_ip_header = "proxy_protocol", + real_ip_recursive = "on", + trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", + nginx_conf = "spec/fixtures/custom_nginx.template", + lua_package_path = "?/init.lua;./kong/?.lua;./spec/fixtures/?.lua", + }) + + teardown(stop_kong) + + describe("X-Real-IP, X-Forwarded-For and X-Forwarded-Port", function() + it("should be added if not present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + + it("should be changed according to rules if present in request", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("10.0.0.1, 127.0.0.2, 10.0.0.1, 192.168.0.1, 172.16.0.1, 127.0.0.1", headers["x-forwarded-for"]) + assert(sock:close()) + end) + end) + + describe("X-Forwarded-Port", function() + it("should be replaced even if proxy protocol, X-Forwarded-Port and X-Forwarded-For headers have a port in it", function() + local sock = ngx.socket.tcp() + local request = "PROXY TCP4 192.168.0.1 " .. helpers.test_conf.proxy_ip .. " 56324 " .. helpers.test_conf.proxy_port .. "\r\n" .. + "GET / HTTP/1.1\r\n" .. + "Host: headers-inspect.com\r\n" .. + "Connection: close\r\n" .. + "X-Real-IP: 10.0.0.2\r\n" .. + "X-Forwarded-For: 127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18\r\n" .. + "X-Forwarded-Port: 14\r\n" .. + "\r\n" + + assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:send(request)) + + local response, err = sock:receive "*a" + + assert(response, err) + + local json = string.match(response, "%b{}") + + assert.is_not_nil(json) + + local headers = cjson.decode(json) + + assert.equal("127.0.0.1", headers["x-real-ip"]) + assert.equal("127.0.0.1:14, 10.0.0.1:15, 192.168.0.1:16, 127.0.0.1:17, 172.16.0.1:18, 127.0.0.1", headers["x-forwarded-for"]) + assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert(sock:close()) + end) + end) + end) + end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 1bcff6fd43d9..35d8b39da25b 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -33,10 +33,6 @@ http { proxy_ssl_server_name on; underscores_in_headers on; - real_ip_header X-Forwarded-For; - set_real_ip_from 0.0.0.0/0; - real_ip_recursive on; - lua_package_path '${{LUA_PACKAGE_PATH}};;'; lua_package_cpath '${{LUA_PACKAGE_CPATH}};;'; lua_code_cache ${{LUA_CODE_CACHE}}; @@ -101,14 +97,22 @@ http { server { server_name kong; +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN}} proxy_protocol; +> else listen ${{PROXY_LISTEN}}; +> end error_page 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; access_log logs/access.log; > if ssl then +> if real_ip_header == "proxy_protocol" then + listen ${{PROXY_LISTEN_SSL}} proxy_protocol ssl; +> else listen ${{PROXY_LISTEN_SSL}} ssl; +> end ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -141,9 +145,9 @@ http { proxy_set_header Host $upstream_host; proxy_set_header Upgrade $upstream_upgrade; proxy_set_header Connection $upstream_connection; - - proxy_pass_header Server; - proxy_pass $upstream_scheme://kong_upstream; + proxy_pass_header Server; + proxy_pass_header Date; + proxy_pass $upstream_scheme://kong_upstream; header_filter_by_lua_block { kong.header_filter() From 514ff60ed3446a9cd2060d3e8ab06a1c2e3c9a6e Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Tue, 28 Mar 2017 08:06:19 +0300 Subject: [PATCH 08/15] Remove headers-inspect test plugin because it is not needed anymore. --- .../05-proxy/02-upstream_headers_spec.lua | 17 -------------- .../kong/plugins/headers-inspect/handler.lua | 23 ------------------- .../kong/plugins/headers-inspect/schema.lua | 3 --- 3 files changed, 43 deletions(-) delete mode 100644 spec/fixtures/kong/plugins/headers-inspect/handler.lua delete mode 100644 spec/fixtures/kong/plugins/headers-inspect/schema.lua diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua index cb1fb4e19502..7e05ee75792d 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -35,23 +35,6 @@ local function start_kong(config) } assert(helpers.start_kong(config)) - - local admin_client = helpers.admin_client() - - local res = assert(admin_client:send { - method = "POST", - path = "/apis/headers-inspect/plugins", - body = { - name = "headers-inspect", - }, - headers = { - ["Content-Type"] = "application/json", - } - }) - - assert.res_status(201, res) - - admin_client:close() end end diff --git a/spec/fixtures/kong/plugins/headers-inspect/handler.lua b/spec/fixtures/kong/plugins/headers-inspect/handler.lua deleted file mode 100644 index dbcdd42d3837..000000000000 --- a/spec/fixtures/kong/plugins/headers-inspect/handler.lua +++ /dev/null @@ -1,23 +0,0 @@ -local cjson = require "cjson" -local BasePlugin = require "kong.plugins.base_plugin" - - -local _M = BasePlugin:extend() - - -function _M:new() - _M.super.new(self, "headers-inspect") -end - - -function _M.access() - local headers = ngx.req.get_headers() - local json = cjson.encode(headers) - - ngx.status = 200 - ngx.say(json) - ngx.exit(200) -end - - -return _M diff --git a/spec/fixtures/kong/plugins/headers-inspect/schema.lua b/spec/fixtures/kong/plugins/headers-inspect/schema.lua deleted file mode 100644 index 8b6db5093d13..000000000000 --- a/spec/fixtures/kong/plugins/headers-inspect/schema.lua +++ /dev/null @@ -1,3 +0,0 @@ -return { - fields = {} -} From 89eaeea983d22265ab9f438017bdc243ecce7176 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Tue, 28 Mar 2017 15:35:59 +0300 Subject: [PATCH 09/15] Add a line about the PROXY protocol support in changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34dbf542bc81..fc75aa575f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ - :fireworks: Configurable `X-Forwarded-*` and `X-Real-IP` upstream headers. [#2236](https://github.com/Mashape/kong/pull/2236) +- :fireworks: Support for The PROXY protocol. + [#2236](https://github.com/Mashape/kong/pull/2236) - Plugins: - cors: Support for configuring multiple Origin domains. [#2203](https://github.com/Mashape/kong/pull/2203) From bad20fc483b9916cdd09e76ac986ef1292a7cb91 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Thu, 30 Mar 2017 14:24:55 +0300 Subject: [PATCH 10/15] Resolves merge conflicts with next. --- CHANGELOG.md | 60 +++++- CODE_OF_CONDUCT.md | 74 +++++++ UPGRADE.md | 3 + ....10.0-0.rockspec => kong-0.10.1-0.rockspec | 4 +- kong.conf.default | 9 +- kong/api/init.lua | 86 ++++---- kong/api/routes/upstreams.lua | 189 +++++++++++++----- kong/cmd/migrations.lua | 2 +- kong/conf_loader.lua | 1 + kong/core/router.lua | 28 +-- kong/meta.lua | 2 +- kong/plugins/aws-lambda/schema.lua | 2 +- kong/plugins/aws-lambda/v4.lua | 4 +- kong/plugins/ldap-auth/schema.lua | 1 + kong/templates/kong_defaults.lua | 1 + kong/templates/nginx.lua | 1 + kong/tools/database_cache.lua | 21 +- spec/01-unit/02-conf_loader_spec.lua | 3 + spec/01-unit/03-prefix_handler_spec.lua | 1 + spec/01-unit/11-router_spec.lua | 122 ++++++----- .../03-admin_api/01-kong_routes_spec.lua | 2 +- .../03-admin_api/09-targets_routes_spec.lua | 114 +++++++++++ .../05-proxy/01-router_spec.lua | 18 ++ spec/fixtures/custom_nginx.template | 1 + 24 files changed, 565 insertions(+), 184 deletions(-) create mode 100644 CODE_OF_CONDUCT.md rename kong-0.10.0-0.rockspec => kong-0.10.1-0.rockspec (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc75aa575f3a..e5b2f9959f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,14 @@ ### Changed -- Admin API: - - Disable support for TLS/1.0. - [#2212](https://github.com/Mashape/kong/pull/2212) - :warning: The default configuration for `X-Forwarded-*` and `X-Real-IP` upstream headers was changed considerably. Previously Kong trusted and forwarded those headers by default. With this release we do **not** trust those headers anymore by default. This is all configurable by now. Please read more about this change from our - [0.10.x Proxy Reference](https://getkong.org/docs/0.10.x/proxy/#3-proxying-upstream-timeouts) + [0.10.x Proxy Reference](https://getkong.org/docs/0.10.x/proxy/) and - [0.10.x Configuration Reference](https://getkong.org/docs/0.10.x/configuration/#real_ip_header) + [0.10.x Configuration Reference](https://getkong.org/docs/0.10.x/configuration/) ### Added @@ -20,29 +17,77 @@ [#2236](https://github.com/Mashape/kong/pull/2236) - :fireworks: Support for The PROXY protocol. [#2236](https://github.com/Mashape/kong/pull/2236) + +## [0.10.1] - 2017/03/27 + +### Changed + +- :warning: Serf has been downgraded to version 0.7 in our distributions, + although versions up to 0.8.1 are still supported. This fixes a problem when + automatically detecting the first non-loopback private IP address, which was + defaulted to `127.0.0.1` in Kong 0.10.0. Greater versions of Serf can still + be used, but the IP address needs to be manually specified in the + `cluster_advertise` configuration property. +- Admin API: + - Disable support for TLS/1.0. + [#2212](https://github.com/Mashape/kong/pull/2212) + +### Added + +- Admin API: + - Active targets can be pulled with `GET /upstreams/{name}/targets/active`. + [#2230](https://github.com/Mashape/kong/pull/2230) + - Provide a convenience endpoint to disable targets at: + `DELETE /upstreams/{name}/targets/{target}`. + Under the hood, this creates a new target with `weigth = 0` (the + correct way of disabling targets, which used to cause confusion). + [#2256](https://github.com/Mashape/kong/pull/2256) - Plugins: - cors: Support for configuring multiple Origin domains. [#2203](https://github.com/Mashape/kong/pull/2203) ### Fixed +- Use an LRU cache for Lua-land entities caching to avoid exhausting the Lua + VM memory in long-running instances. + [#2246](https://github.com/Mashape/kong/pull/2246) - Avoid potential deadlocks upon callback errors in the caching module for database entities. [#2197](https://github.com/Mashape/kong/pull/2197) - Relax multipart MIME type parsing. A space is allowed in between values of the Content-Type header. [#2215](https://github.com/Mashape/kong/pull/2215) +- Admin API: + - Better handling of non-supported HTTP methods on endpoints of the Admin + API. In some cases this used to throw an internal error. Calling any + endpoint with a non-supported HTTP method now always returns `405 Method + Not Allowed` as expected. + [#2213](https://github.com/Mashape/kong/pull/2213) - CLI: - Better error handling when missing Serf executable. [#2218](https://github.com/Mashape/kong/pull/2218) + - Fix a bug in the `kong migrations` command that would prevent it to run + correctly. + [#2238](https://github.com/Mashape/kong/pull/2238) + - Trim list values specified in the configuration file. + [#2206](https://github.com/Mashape/kong/pull/2206) + - Align the default configuration file's values to the actual, hard-coded + default values to avoid confusion. + [#2254](https://github.com/Mashape/kong/issues/2254) - Plugins: - hmac: Generate an HMAC secret value if none is provided. [#2158](https://github.com/Mashape/kong/pull/2158) - oauth2: Don't try to remove credential values from request bodies if the MIME type is multipart, since such attemps would result in an error. [#2176](https://github.com/Mashape/kong/pull/2176) + - ldap: This plugin should not be applied to a single Consumer, however, this + was not properly enforced. It is now impossible to apply this plugin to a + single Consumer (as per all authentication plugin). + [#2237](https://github.com/Mashape/kong/pull/2237) + - aws-lambda: Support for `us-west-2` region in schema. + [#2257](https://github.com/Mashape/kong/pull/2257) -## [0.10.0] - 2016/03/07 +## [0.10.0] - 2017/03/07 Kong 0.10 is one of most significant releases to this day. It ships with exciting new features that have been heavily requested for the last few months, @@ -1025,7 +1070,8 @@ First version running with Cassandra. - CLI `bin/kong` script. - Database migrations (using `db.lua`). -[unreleased]: https://github.com/mashape/kong/compare/0.10.0...next +[unreleased]: https://github.com/mashape/kong/compare/0.10.1...next +[0.10.1]: https://github.com/mashape/kong/compare/0.10.0...0.10.1 [0.10.0]: https://github.com/mashape/kong/compare/0.9.9...0.10.0 [0.9.9]: https://github.com/mashape/kong/compare/0.9.8...0.9.9 [0.9.8]: https://github.com/mashape/kong/compare/0.9.7...0.9.8 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..b1b9a25c2ec2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at support@mashape.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/UPGRADE.md b/UPGRADE.md index 1b8a8ee50efd..f20958922710 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -63,6 +63,9 @@ Kong 0.10 introduced the following breaking changes: - Dnsmasq is not a dependency anymore (However, be careful before removing it if you configured it to be your DNS name server via Kong's [`resolver` property](https://getkong.org/docs/0.9.x/configuration/#dns-resolver-section)) +- The `cassandra_contact_points` property does not allow specifying a port + anymore. All Cassandra nodes must listen on the same port, which can be + tweaked via the `cassandra_port` property. We recommend that you consult the full [0.10.0 Changelog](https://github.com/Mashape/kong/blob/master/CHANGELOG.md) for a full diff --git a/kong-0.10.0-0.rockspec b/kong-0.10.1-0.rockspec similarity index 99% rename from kong-0.10.0-0.rockspec rename to kong-0.10.1-0.rockspec index 6c8f5fc021c1..cf6371a50e48 100644 --- a/kong-0.10.0-0.rockspec +++ b/kong-0.10.1-0.rockspec @@ -1,9 +1,9 @@ package = "kong" -version = "0.10.0-0" +version = "0.10.1-0" supported_platforms = {"linux", "macosx"} source = { url = "git://github.com/Mashape/kong", - tag = "0.10.0" + tag = "0.10.1" } description = { summary = "Kong is a scalable and customizable API Management Layer built on top of Nginx.", diff --git a/kong.conf.default b/kong.conf.default index 6088f29b3cde..3ac163163923 100644 --- a/kong.conf.default +++ b/kong.conf.default @@ -68,6 +68,11 @@ # HTTPS requests to the admin API, if # `admin_ssl` is enabled. +#nginx_user = nobody # Defines user and group credentials used by + # worker processes. If group is omitted, a + # group whose name equals that of user is + # used. Ex: [user] [group]. + #nginx_worker_processes = auto # Determines the number of worker processes # spawned by Nginx. @@ -152,7 +157,7 @@ #pg_host = 127.0.0.1 # The PostgreSQL host to connect to. #pg_port = 5432 # The port to connect to. #pg_user = kong # The username to authenticate if required. -#pg_password = kong # The password to authenticate if required. +#pg_password = # The password to authenticate if required. #pg_database = kong # The database name to connect to. #pg_ssl = off # Toggles client-server TLS connections @@ -186,7 +191,7 @@ #cassandra_username = kong # Username when using the # `PasswordAuthenticator` scheme. -#cassandra_password = kong # Password when using the +#cassandra_password = # Password when using the # `PasswordAuthenticator` scheme. #cassandra_consistency = ONE # Consistency setting to use when reading/ diff --git a/kong/api/init.lua b/kong/api/init.lua index 4debfb9a7fc7..a03c31fe1fbd 100644 --- a/kong/api/init.lua +++ b/kong/api/init.lua @@ -1,131 +1,149 @@ local lapis = require "lapis" local utils = require "kong.tools.utils" +local tablex = require "pl.tablex" local responses = require "kong.tools.responses" local singletons = require "kong.singletons" local app_helpers = require "lapis.application" local api_helpers = require "kong.api.api_helpers" -local tablex = require "pl.tablex" + local find = string.find + local app = lapis.Application() -local needs_body = tablex.readonly({ PUT = 1, POST = 2, PATCH = 3 }) + +local NEEDS_BODY = tablex.readonly({ PUT = 1, POST = 2, PATCH = 3 }) + local function parse_params(fn) return app_helpers.json_params(function(self, ...) - if needs_body[ngx.req.get_method()] then + if NEEDS_BODY[ngx.req.get_method()] then local content_type = self.req.headers["content-type"] - if content_type and find(content_type:lower(), "application/json", nil, true) then + + if content_type and find(content_type:lower(), "application/json", + nil, true) then if not self.json then return responses.send_HTTP_BAD_REQUEST("Cannot parse JSON body") end end end + self.params = api_helpers.normalize_nested_params(self.params) + return fn(self, ...) end) end + local function on_error(self) local err = self.errors[1] + if type(err) == "table" then if err.db then return responses.send_HTTP_INTERNAL_SERVER_ERROR(err.message) + elseif err.unique then return responses.send_HTTP_CONFLICT(err.tbl) + elseif err.foreign then return responses.send_HTTP_NOT_FOUND(err.tbl) + else return responses.send_HTTP_BAD_REQUEST(err.tbl or err.message) end end end + app.default_route = function(self) local path = self.req.parsed_url.path:match("^(.*)/$") if path and self.app.router:resolve(path, self) then return - elseif self.app.router:resolve(self.req.parsed_url.path.."/", self) then + + elseif self.app.router:resolve(self.req.parsed_url.path .. "/", self) then return end return self.app.handle_404(self) end + app.handle_404 = function(self) return responses.send_HTTP_NOT_FOUND() end + app.handle_error = function(self, err, trace) - ngx.log(ngx.ERR, err.."\n"..trace) - -- We just logged the error so no need to give it to responses and log it twice + if err and find(err, "don't know how to respond to", nil, true) then + return responses.send_HTTP_METHOD_NOT_ALLOWED() + end + + ngx.log(ngx.ERR, err, "\n", trace) + + -- We just logged the error so no need to give it to responses and log it + -- twice return responses.send_HTTP_INTERNAL_SERVER_ERROR() end + app:before_filter(function(self) - if needs_body[ngx.req.get_method()] and not self.req.headers["content-type"] then + if NEEDS_BODY[ngx.req.get_method()] + and not self.req.headers["content-type"] then return responses.send_HTTP_UNSUPPORTED_MEDIA_TYPE() end end) + local handler_helpers = { responses = responses, yield_error = app_helpers.yield_error } + local function attach_routes(routes) for route_path, methods in pairs(routes) do - if not methods.on_error then - methods.on_error = on_error - end + methods.on_error = methods.on_error or on_error - for k, v in pairs(methods) do - local method = function(self) - return v(self, singletons.dao, handler_helpers) + for method_name, method_handler in pairs(methods) do + local wrapped_handler = function(self) + return method_handler(self, singletons.dao, handler_helpers) end - methods[k] = parse_params(method) + + methods[method_name] = parse_params(wrapped_handler) end app:match(route_path, route_path, app_helpers.respond_to(methods)) end end -local methods_to_define = { "PATCH", "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT" } -local function no_method(self, dao_factory, helpers) - return responses.send_HTTP_METHOD_NOT_ALLOWED() -end - --- insert 405's for undefined methods -local function insert_405(routes) - for route, methods in pairs(routes) do - for _, name in ipairs(methods_to_define) do - methods[name] = methods[name] or no_method - end - end - return routes -end ngx.log(ngx.DEBUG, "Loading Admin API endpoints") + -- Load core routes -for _, v in ipairs({"kong", "apis", "consumers", "plugins", "cache", "cluster", "certificates", "snis", "upstreams"}) do - local routes = require("kong.api.routes."..v) - attach_routes(insert_405(routes)) +for _, v in ipairs({"kong", "apis", "consumers", "plugins", "cache", "cluster", + "certificates", "snis", "upstreams"}) do + local routes = require("kong.api.routes." .. v) + attach_routes(routes) end + -- Loading plugins routes if singletons.configuration and singletons.configuration.plugins then for k in pairs(singletons.configuration.plugins) do - local loaded, mod = utils.load_module_if_exists("kong.plugins."..k..".api") + local loaded, mod = utils.load_module_if_exists("kong.plugins." .. k .. ".api") + if loaded then ngx.log(ngx.DEBUG, "Loading API endpoints for plugin: ", k) - attach_routes(insert_405(mod)) + attach_routes(mod) + else ngx.log(ngx.DEBUG, "No API endpoints loaded for plugin: ", k) end end end + return app diff --git a/kong/api/routes/upstreams.lua b/kong/api/routes/upstreams.lua index 5b97bb612b97..09b4a8c818de 100644 --- a/kong/api/routes/upstreams.lua +++ b/kong/api/routes/upstreams.lua @@ -1,4 +1,71 @@ local crud = require "kong.api.crud_helpers" +local app_helpers = require "lapis.application" +local responses = require "kong.tools.responses" + + +-- clean the target history for a given upstream +local function clean_history(upstream_id, dao_factory) + -- when to cleanup: invalid-entries > (valid-ones * cleanup_factor) + local cleanup_factor = 10 + + --cleaning up history, check if it's necessary... + local target_history = dao_factory.targets:find_all({ + upstream_id = upstream_id + }) + + if target_history then + -- sort the targets + for _,target in ipairs(target_history) do + target.order = target.created_at..":"..target.id + end + + -- sort table in reverse order + table.sort(target_history, function(a,b) return a.order>b.order end) + -- do clean up + local cleaned = {} + local delete = {} + + for _, entry in ipairs(target_history) do + if cleaned[entry.target] then + -- we got a newer entry for this target than this, so this one can go + delete[#delete+1] = entry + + else + -- haven't got this one, so this is the last one for this target + cleaned[entry.target] = true + cleaned[#cleaned+1] = entry + if entry.weight == 0 then + delete[#delete+1] = entry + end + end + end + + -- do we need to cleanup? + -- either nothing left, or when 10x more outdated than active entries + if (#cleaned == 0 and #delete > 0) or + (#delete >= (math.max(#cleaned,1)*cleanup_factor)) then + + ngx.log(ngx.INFO, "[admin api] Starting cleanup of target table for upstream ", + tostring(upstream_id)) + local cnt = 0 + for _, entry in ipairs(delete) do + -- not sending update events, one event at the end, based on the + -- post of the new entry should suffice to reload only once + dao_factory.targets:delete( + { id = entry.id }, + { quiet = true } + ) + -- ignoring errors here, deleted by id, so should not matter + -- in case another kong-node does the same cleanup simultaneously + cnt = cnt + 1 + end + + ngx.log(ngx.INFO, "[admin api] Finished cleanup of target table", + " for upstream ", tostring(upstream_id), + " removed ", tostring(cnt), " target entries") + end + end +end return { ["/upstreams/"] = { @@ -44,67 +111,81 @@ return { end, POST = function(self, dao_factory, helpers) - -- when to cleanup: invalid-entries > (valid-ones * cleanup_factor) - local cleanup_factor = 10 - - --cleaning up history, check if it's necessary... - local target_history = dao_factory.targets:find_all( - { upstream_id = self.params.upstream_id }) - - if target_history then --ignoring errors here, will be caught when posting below - -- sort the targets - for _,target in ipairs(target_history) do - target.order = target.created_at..":"..target.id - end + clean_history(self.params.upstream_id, dao_factory) + + crud.post(self.params, dao_factory.targets) + end, + }, + + ["/upstreams/:name_or_id/targets/active/"] = { + before = function(self, dao_factory, helpers) + crud.find_upstream_by_name_or_id(self, dao_factory, helpers) + self.params.upstream_id = self.upstream.id + end, + + GET = function(self, dao_factory) + self.params.active = nil - -- sort table in reverse order - table.sort(target_history, function(a,b) return a.order>b.order end) - -- do clean up - local cleaned = {} - local delete = {} + local target_history, err = dao_factory.targets:find_all({ + upstream_id = self.params.upstream_id, + }) + if not target_history then + return app_helpers.yield_error(err) + end + + --sort and walk based on target and creation time + for _, target in ipairs(target_history) do + target.order = target.target .. ":" .. + target.created_at .. ":" ..target.id + end + table.sort(target_history, function(a, b) return a.order > b.order end) - for _, entry in ipairs(target_history) do - if cleaned[entry.target] then - -- we got a newer entry for this target than this, so this one can go - delete[#delete+1] = entry + local ignored = {} + local found = {} + local found_n = 0 + + for _, entry in ipairs(target_history) do + if not found[entry.target] and not ignored[entry.target] then + if entry.weight ~= 0 then + entry.order = nil -- dont show our order key to the client + found_n = found_n + 1 + found[found_n] = entry else - -- haven't got this one, so this is the last one for this target - cleaned[entry.target] = true - cleaned[#cleaned+1] = entry - if entry.weight == 0 then - delete[#delete+1] = entry - end + ignored[entry.target] = true end end - - -- do we need to cleanup? - -- either nothing left, or when 10x more outdated than active entries - if (#cleaned == 0 and #delete > 0) or - (#delete >= (math.max(#cleaned,1)*cleanup_factor)) then - - ngx.log(ngx.INFO, "[admin api] Starting cleanup of target table for upstream ", - tostring(self.params.upstream_id)) - local cnt = 0 - for _, entry in ipairs(delete) do - -- not sending update events, one event at the end, based on the - -- post of the new entry should suffice to reload only once - dao_factory.targets:delete( - { id = entry.id }, - { quiet = true } - ) - -- ignoring errors here, deleted by id, so should not matter - -- in case another kong-node does the same cleanup simultaneously - cnt = cnt + 1 - end - - ngx.log(ngx.INFO, "[admin api] Finished cleanup of target table", - " for upstream ", tostring(self.params.upstream_id), - " removed ", tostring(cnt), " target entries") - end end - crud.post(self.params, dao_factory.targets) - end, + -- for now lets not worry about rolling our own pagination + -- we also end up returning a "backwards" list of targets because + -- of how we sorted- do we care? + return responses.send_HTTP_OK { + total = found_n, + data = found, + } + end }, + + ["/upstreams/:name_or_id/targets/:target"] = { + before = function(self, dao_factory, helpers) + crud.find_upstream_by_name_or_id(self, dao_factory, helpers) + end, + + DELETE = function(self, dao_factory) + clean_history(self.upstream.id, dao_factory) + + -- this is just a wrapper around POSTing a new target with weight=0 + local data, err = dao_factory.targets:insert({ + target = self.params.target, + upstream_id = self.upstream.id, + weight = 0, + }) + if err then + return app_helpers.yield_error(err) + end + + return responses.send_HTTP_NO_CONTENT() + end + } } diff --git a/kong/cmd/migrations.lua b/kong/cmd/migrations.lua index b6d2ffe1c1f1..7ddd97a33e19 100644 --- a/kong/cmd/migrations.lua +++ b/kong/cmd/migrations.lua @@ -28,7 +28,7 @@ end local function execute(args) local conf = assert(conf_loader(args.conf)) - local dao = assert(DAOFactory.new(conf, conf.plugins)) + local dao = assert(DAOFactory.new(conf)) if args.command == "up" then assert(dao:run_migrations()) diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index cbacb2a28b7c..4e04f42d0a1a 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -60,6 +60,7 @@ local CONF_INFERENCES = { cluster_listen = {typ = "string"}, cluster_listen_rpc = {typ = "string"}, cluster_advertise = {typ = "string"}, + nginx_user = {typ = "string"}, nginx_worker_processes = {typ = "string"}, upstream_keepalive = {typ = "number"}, real_ip_header = {typ = "string"}, diff --git a/kong/core/router.lua b/kong/core/router.lua index ba34b948d866..fb9f2962e05b 100644 --- a/kong/core/router.lua +++ b/kong/core/router.lua @@ -446,25 +446,24 @@ function _M.new(apis) end) - local grab_headers = #wildcard_hosts > 0 or next(indexes.plain_hosts) ~= nil + local grab_host = #wildcard_hosts > 0 or next(indexes.plain_hosts) ~= nil - local function find_api(method, uri, headers) + local function find_api(method, uri, host) if type(method) ~= "string" then return error("arg #1 method must be a string") end if type(uri) ~= "string" then return error("arg #2 uri must be a string") end - if type(headers) ~= "table" then - return error("arg #3 headers must be a table") + if host and type(host) ~= "string" then + return error("arg #3 host must be a string") end method = upper(method) - local host = headers["host"] or headers["Host"] if host then -- strip port number if given local m, err = re_match(host, "^([^:]+)", "jo") @@ -574,21 +573,18 @@ function _M.new(apis) local uri = ngx.var.uri local new_uri = uri local host_header - local headers + local req_host - --print("grab headers: ", grab_headers) + --print("grab host header: ", grab_host) - if grab_headers then - headers = ngx.req.get_headers() - - else - headers = empty_t + if grab_host then + req_host = ngx.var.http_host end - local api_t = find_api(method, uri, headers) + local api_t = find_api(method, uri, req_host) if not api_t then return nil end @@ -615,11 +611,7 @@ function _M.new(apis) if api_t.preserve_host then - if not headers then - headers = ngx.req.get_headers() - end - - host_header = headers["host"] + host_header = req_host end diff --git a/kong/meta.lua b/kong/meta.lua index 22fc7b0404e3..282deaa46a7a 100644 --- a/kong/meta.lua +++ b/kong/meta.lua @@ -1,7 +1,7 @@ local version = setmetatable({ major = 0, minor = 10, - patch = 0, + patch = 1, --pre_release = "" }, { __tostring = function(t) diff --git a/kong/plugins/aws-lambda/schema.lua b/kong/plugins/aws-lambda/schema.lua index 3e881772a792..c67359c213b9 100644 --- a/kong/plugins/aws-lambda/schema.lua +++ b/kong/plugins/aws-lambda/schema.lua @@ -5,7 +5,7 @@ return { aws_key = {type = "string", required = true}, aws_secret = {type = "string", required = true}, aws_region = {type = "string", required = true, enum = { - "us-east-1", "us-east-2", "ap-northeast-1", "ap-northeast-2", + "us-east-1", "us-east-2", "ap-northeast-1", "ap-northeast-2", "us-west-2", "ap-southeast-1", "ap-southeast-2", "eu-central-1", "eu-west-1"}}, function_name = {type="string", required = true}, qualifier = {type = "string"}, diff --git a/kong/plugins/aws-lambda/v4.lua b/kong/plugins/aws-lambda/v4.lua index d843808d3ad4..0c4555278407 100644 --- a/kong/plugins/aws-lambda/v4.lua +++ b/kong/plugins/aws-lambda/v4.lua @@ -109,10 +109,10 @@ local function prepare_awsv4_request(tbl) return nil, "either 'signing_key' or 'secret_key' must be provided" end end - local timestamp = tbl.timestamp or os.time() local tls = tbl.tls if tls == nil then tls = true end local port = tbl.port or (tls and 443 or 80) + local timestamp = tbl.timestamp or ngx.time() local req_date = os.date("!%Y%m%dT%H%M%SZ", timestamp) local date = os.date("!%Y%m%d", timestamp) @@ -225,4 +225,4 @@ local function prepare_awsv4_request(tbl) } end -return prepare_awsv4_request \ No newline at end of file +return prepare_awsv4_request diff --git a/kong/plugins/ldap-auth/schema.lua b/kong/plugins/ldap-auth/schema.lua index 28406dcd55ea..ad30be0182c9 100644 --- a/kong/plugins/ldap-auth/schema.lua +++ b/kong/plugins/ldap-auth/schema.lua @@ -9,6 +9,7 @@ local function check_user(anonymous) end return { + no_consumer = true, fields = { ldap_host = {required = true, type = "string"}, ldap_port = {required = true, type = "number"}, diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 7655723cde59..a569dc2d8b4c 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -8,6 +8,7 @@ proxy_listen = 0.0.0.0:8000 proxy_listen_ssl = 0.0.0.0:8443 admin_listen = 0.0.0.0:8001 admin_listen_ssl = 0.0.0.0:8444 +nginx_user = nobody nginx_worker_processes = auto nginx_optimizations = on nginx_daemon = on diff --git a/kong/templates/nginx.lua b/kong/templates/nginx.lua index 750eae6c088d..77d17cb8dd6d 100644 --- a/kong/templates/nginx.lua +++ b/kong/templates/nginx.lua @@ -1,4 +1,5 @@ return [[ +user ${{NGINX_USER}}; worker_processes ${{NGINX_WORKER_PROCESSES}}; daemon ${{NGINX_DAEMON}}; diff --git a/kong/tools/database_cache.lua b/kong/tools/database_cache.lua index 4bed08602654..a45dcf768680 100644 --- a/kong/tools/database_cache.lua +++ b/kong/tools/database_cache.lua @@ -2,12 +2,19 @@ local utils = require "kong.tools.utils" local resty_lock = require "resty.lock" local json_encode = require("cjson.safe").encode local json_decode = require("cjson.safe").decode +local lrucache = require "resty.lrucache" local cache = ngx.shared.cache local ngx_log = ngx.log local gettime = ngx.now local pack = utils.pack local unpack = utils.unpack +-- Lets calculate our LRU cache size based on some memory assumptions... +local ITEM_SIZE = 1024 -- estimated bytes used per cache entry (probably less) +local MEM_SIZE = 500 -- megabytes to allocate for maximum cache size +-- defaults 1024/500 above ---> size = 512000 entries +local LRU_SIZE = math.floor((MEM_SIZE * 1024 * 1024) / ITEM_SIZE) + local TTL_EXPIRE_KEY = "___expire_ttl" local CACHE_KEYS = { @@ -65,7 +72,7 @@ end -- Local Memory -local DATA = {} +local DATA = lrucache.new(LRU_SIZE) function _M.set(key, value, exptime) exptime = exptime or 0 @@ -77,7 +84,7 @@ function _M.set(key, value, exptime) } end - DATA[key] = value + DATA:set(key, value) -- Save into Shared Dictionary local _, err = _M.sh_set(key, json_encode(value), exptime) @@ -90,7 +97,7 @@ function _M.get(key) local now = gettime() -- check local memory, and verify ttl - local value = DATA[key] + local value = DATA:get(key) if value ~= nil then if type(value) ~= "table" or not value[TTL_EXPIRE_KEY] then -- found non-ttl value, just return it @@ -100,7 +107,7 @@ function _M.get(key) return value.value end -- value with expired ttl, delete it - DATA[key] = nil + DATA:delete(key) end -- nothing found yet, get it from Shared Dictionary @@ -110,7 +117,7 @@ function _M.get(key) return nil end value = json_decode(value) - DATA[key] = value -- store in memory, so we don't need to deserialize next time + DATA:set(key, value) -- store in memory, so we don't need to deserialize next time if type(value) ~= "table" or not value[TTL_EXPIRE_KEY] then -- found non-ttl value, just return it @@ -122,12 +129,12 @@ function _M.get(key) end function _M.delete(key) - DATA[key] = nil + DATA:delete(key) _M.sh_delete(key) end function _M.delete_all() - DATA = {} + DATA = lrucache.new(LRU_SIZE) _M.sh_delete_all() end diff --git a/spec/01-unit/02-conf_loader_spec.lua b/spec/01-unit/02-conf_loader_spec.lua index 5cb923c118d8..bd15db40c2cf 100644 --- a/spec/01-unit/02-conf_loader_spec.lua +++ b/spec/01-unit/02-conf_loader_spec.lua @@ -5,6 +5,7 @@ describe("Configuration loader", function() it("loads the defaults", function() local conf = assert(conf_loader()) assert.is_string(conf.lua_package_path) + assert.equal("nobody", conf.nginx_user) assert.equal("auto", conf.nginx_worker_processes) assert.equal("0.0.0.0:8001", conf.admin_listen) assert.equal("0.0.0.0:8000", conf.proxy_listen) @@ -21,6 +22,7 @@ describe("Configuration loader", function() -- defaults assert.equal("on", conf.nginx_daemon) -- overrides + assert.equal("nobody", conf.nginx_user) assert.equal("1", conf.nginx_worker_processes) assert.equal("0.0.0.0:9001", conf.admin_listen) assert.equal("0.0.0.0:9000", conf.proxy_listen) @@ -39,6 +41,7 @@ describe("Configuration loader", function() -- defaults assert.equal("on", conf.nginx_daemon) -- overrides + assert.equal("nobody", conf.nginx_user) assert.equal("auto", conf.nginx_worker_processes) assert.equal("127.0.0.1:9001", conf.admin_listen) assert.equal("0.0.0.0:9000", conf.proxy_listen) diff --git a/spec/01-unit/03-prefix_handler_spec.lua b/spec/01-unit/03-prefix_handler_spec.lua index d79e1b112e4e..6b50d6d32f6c 100644 --- a/spec/01-unit/03-prefix_handler_spec.lua +++ b/spec/01-unit/03-prefix_handler_spec.lua @@ -171,6 +171,7 @@ describe("NGINX conf compiler", function() describe("compile_nginx_conf()", function() it("compiles a main NGINX conf", function() local nginx_conf = prefix_handler.compile_nginx_conf(helpers.test_conf) + assert.matches("user nobody;", nginx_conf, nil, true) assert.matches("worker_processes 1;", nginx_conf, nil, true) assert.matches("daemon on;", nginx_conf, nil, true) end) diff --git a/spec/01-unit/11-router_spec.lua b/spec/01-unit/11-router_spec.lua index bad59299889a..afd76b68373a 100644 --- a/spec/01-unit/11-router_spec.lua +++ b/spec/01-unit/11-router_spec.lua @@ -81,56 +81,56 @@ describe("Router", function() it("[host]", function() -- host - local api_t = router.select("GET", "/", { ["host"] = "domain-1.org" }) + local api_t = router.select("GET", "/", "domain-1.org") assert.truthy(api_t) assert.same(use_case[1], api_t.api) end) it("[host] ignores port", function() -- host - local api_t = router.select("GET", "/", { ["host"] = "domain-1.org:123" }) + local api_t = router.select("GET", "/", "domain-1.org:123") assert.truthy(api_t) assert.same(use_case[1], api_t.api) end) it("[uri]", function() -- uri - local api_t = router.select("GET", "/my-api", {}) + local api_t = router.select("GET", "/my-api") assert.truthy(api_t) assert.same(use_case[3], api_t.api) end) it("[method]", function() -- method - local api_t = router.select("TRACE", "/", {}) + local api_t = router.select("TRACE", "/") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) it("[host + uri]", function() -- host + uri - local api_t = router.select("GET", "/api-4", { ["host"] = "domain-1.org" }) + local api_t = router.select("GET", "/api-4", "domain-1.org") assert.truthy(api_t) assert.same(use_case[4], api_t.api) end) it("[host + method]", function() -- host + method - local api_t = router.select("POST", "/", { ["host"] = "domain-1.org" }) + local api_t = router.select("POST", "/", "domain-1.org") assert.truthy(api_t) assert.same(use_case[5], api_t.api) end) it("[uri + method]", function() -- uri + method - local api_t = router.select("PUT", "/api-6", {}) + local api_t = router.select("PUT", "/api-6") assert.truthy(api_t) assert.same(use_case[6], api_t.api) end) it("[host + uri + method]", function() -- uri + method - local api_t = router.select("PUT", "/my-api-uri", { ["host"] = "domain-with-uri-2.org" }) + local api_t = router.select("PUT", "/my-api-uri", "domain-with-uri-2.org") assert.truthy(api_t) assert.same(use_case[7], api_t.api) end) @@ -138,7 +138,7 @@ describe("Router", function() describe("[uri] as a prefix", function() it("matches when given [uri] is in request URI prefix", function() -- uri prefix - local api_t = router.select("GET", "/my-api/some/path", {}) + local api_t = router.select("GET", "/my-api/some/path") assert.truthy(api_t) assert.same(use_case[3], api_t.api) end) @@ -157,19 +157,19 @@ describe("Router", function() local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/my-api/hello", {}) + local api_t = router.select("GET", "/my-api/hello") assert.truthy(api_t) assert.same(use_case[1], api_t.api) - api_t = router.select("GET", "/my-api/hello/world", {}) + api_t = router.select("GET", "/my-api/hello/world") assert.truthy(api_t) assert.same(use_case[1], api_t.api) - api_t = router.select("GET", "/my-api", {}) + api_t = router.select("GET", "/my-api") assert.truthy(api_t) assert.same(use_case[2], api_t.api) - api_t = router.select("GET", "/my-api/world", {}) + api_t = router.select("GET", "/my-api/world") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) @@ -194,13 +194,13 @@ describe("Router", function() local router = assert(Router.new(use_case)) it("matches leftmost wildcards", function() - local api_t = router.select("GET", "/", { ["host"] = "foo.api.com" }) + local api_t = router.select("GET", "/", "foo.api.com") assert.truthy(api_t) assert.same(use_case[1], api_t.api) end) it("matches rightmost wildcards", function() - local api_t = router.select("GET", "/", { ["host"] = "api.org" }) + local api_t = router.select("GET", "/", "api.org") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) @@ -224,19 +224,19 @@ describe("Router", function() router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/", { ["host"] = "api.com" }) + local api_t = router.select("GET", "/", "api.com") assert.truthy(api_t) assert.same(use_case[4], api_t.api) - api_t = router.select("GET", "/", { ["host"] = "api.org" }) + api_t = router.select("GET", "/", "api.org") assert.truthy(api_t) assert.same(use_case[3], api_t.api) - api_t = router.select("GET", "/", { ["host"] = "plain.api.com" }) + api_t = router.select("GET", "/", "plain.api.com") assert.truthy(api_t) assert.same(use_case[1], api_t.api) - api_t = router.select("GET", "/", { ["host"] = "foo.api.com" }) + api_t = router.select("GET", "/", "foo.api.com") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) @@ -256,18 +256,18 @@ describe("Router", function() router = assert(Router.new(use_case)) - local api_t = router.select("POST", "/path", { ["host"] = "foo.domain.com" }) + local api_t = router.select("POST", "/path", "foo.domain.com") assert.is_nil(api_t) - api_t = router.select("GET", "/path", { ["host"] = "foo.domain.com" }) + api_t = router.select("GET", "/path", "foo.domain.com") assert.truthy(api_t) assert.same(use_case[#use_case], api_t.api) - api_t = router.select("TRACE", "/path", { ["host"] = "example.com" }) + api_t = router.select("TRACE", "/path", "example.com") assert.truthy(api_t) assert.same(use_case[#use_case], api_t.api) - api_t = router.select("POST", "/path", { ["host"] = "foo.domain.com" }) + api_t = router.select("POST", "/path", "foo.domain.com") assert.is_nil(api_t) end) end) @@ -275,12 +275,12 @@ describe("Router", function() describe("edge-cases", function() it("[host] and [uri] have higher priority than [method]", function() -- host - local api_t = router.select("TRACE", "/", { ["host"] = "domain-2.org" }) + local api_t = router.select("TRACE", "/", "domain-2.org") assert.truthy(api_t) assert.same(use_case[1], api_t.api) -- uri - local api_t = router.select("TRACE", "/my-api", {}) + local api_t = router.select("TRACE", "/my-api") assert.truthy(api_t) assert.same(use_case[3], api_t.api) end) @@ -299,25 +299,25 @@ describe("Router", function() it("routes with GET /", function() local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/", {}) + local api_t = router.select("GET", "/") assert.truthy(api_t) assert.same(use_case[1], api_t.api) end) it("does not superseds another API", function() local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/my-api", {}) + local api_t = router.select("GET", "/my-api") assert.truthy(api_t) assert.same(use_case[4], api_t.api) - api_t = router.select("GET", "/my-api/hello/world", {}) + api_t = router.select("GET", "/my-api/hello/world") assert.truthy(api_t) assert.same(use_case[4], api_t.api) end) it("acts as a catch-all", function() local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/foobar/baz", {}) + local api_t = router.select("GET", "/foobar/baz") assert.truthy(api_t) assert.same(use_case[1], api_t.api) end) @@ -335,11 +335,11 @@ describe("Router", function() } local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/httpbin", {}) + local api_t = router.select("GET", "/httpbin") assert.truthy(api_t) assert.same(use_case[2], api_t.api) - api_t = router.select("GET", "/httpbin/status/200", {}) + api_t = router.select("GET", "/httpbin/status/200") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) @@ -359,11 +359,11 @@ describe("Router", function() } local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/", {}) + local api_t = router.select("GET", "/") assert.truthy(api_t) assert.same(use_case[1], api_t.api) - api_t = router.select("GET", "/", { ["host"] = "domain.com" }) + api_t = router.select("GET", "/", "domain.com") assert.truthy(api_t) assert.same(use_case[2], api_t.api) end) @@ -405,7 +405,7 @@ describe("Router", function() it("matches correct API", function() local router = assert(Router.new(use_case)) - local api_t = router.select("GET", "/my-target-uri", { ["host"] = "domain.org" }) + local api_t = router.select("GET", "/my-target-uri", "domain.org") assert.truthy(api_t) assert.same(use_case[#use_case], api_t.api) end) @@ -414,27 +414,27 @@ describe("Router", function() describe("misses", function() it("invalid [host]", function() - assert.is_nil(router.select("GET", "/", { ["host"] = "domain-3.org" })) + assert.is_nil(router.select("GET", "/", "domain-3.org")) end) it("invalid host in [host + uri]", function() - assert.is_nil(router.select("GET", "/api-4", { ["host"] = "domain-3.org" })) + assert.is_nil(router.select("GET", "/api-4", "domain-3.org")) end) it("invalid host in [host + method]", function() - assert.is_nil(router.select("GET", "/", { ["host"] = "domain-3.org" })) + assert.is_nil(router.select("GET", "/", "domain-3.org")) end) it("invalid method in [host + uri + method]", function() - assert.is_nil(router.select("GET", "/some-uri", { ["host"] = "domain-with-uri-2.org" })) + assert.is_nil(router.select("GET", "/some-uri", "domain-with-uri-2.org")) end) it("invalid uri in [host + uri + method]", function() - assert.is_nil(router.select("PUT", "/some-uri-foo", { ["host"] = "domain-with-uri-2.org" })) + assert.is_nil(router.select("PUT", "/some-uri-foo", "domain-with-uri-2.org")) end) it("does not match when given [uri] is in URI but not in prefix", function() - local api_t = router.select("GET", "/some-other-prefix/my-api", {}) + local api_t = router.select("GET", "/some-other-prefix/my-api") assert.is_nil(api_t) end) end) @@ -470,7 +470,7 @@ describe("Router", function() end) it("takes < 1ms", function() - local api_t = router.select("GET", "/", { ["host"] = target_domain }) + local api_t = router.select("GET", "/", target_domain) assert.truthy(api_t) assert.same(benchmark_use_cases[#benchmark_use_cases], api_t.api) end) @@ -515,7 +515,7 @@ describe("Router", function() end) it("takes < 1ms", function() - local api_t = router.select("POST", target_uri, { ["host"] = target_domain }) + local api_t = router.select("POST", target_uri, target_domain) assert.truthy(api_t) assert.same(benchmark_use_cases[#benchmark_use_cases], api_t.api) end) @@ -558,7 +558,7 @@ describe("Router", function() end) it("takes < 1ms", function() - local api_t = router.select("GET", target_uri, { ["host"] = target_domain }) + local api_t = router.select("GET", target_uri, target_domain) assert.truthy(api_t) assert.same(benchmark_use_cases[#benchmark_use_cases], api_t.api) end) @@ -576,20 +576,32 @@ describe("Router", function() end, "arg #2 uri must be a string", nil, true) assert.error_matches(function() - router.select("GET", "/") - end, "arg #3 headers must be a table", nil, true) + router.select("GET", "/", 1) + end, "arg #3 host must be a string", nil, true) end) end) end) describe("exec()", function() + local spy_stub = { + nop = function() end + } + local function mock_ngx(method, uri, headers) local _ngx _ngx = { re = ngx.re, - var = { - uri = uri - }, + var = setmetatable({ + uri = uri, + http_kong_debug = headers.kong_debug + }, { + __index = function(_, key) + if key == "http_host" then + spy_stub.nop() + return headers.host + end + end + }), req = { set_uri = function(uri) _ngx.var.uri = uri @@ -687,18 +699,20 @@ describe("Router", function() } } - it("does not read request headers if not required", function() + it("does not read Host header if not required", function() local _ngx = mock_ngx("GET", "/my-api", {}) - spy.on(_ngx.req, "get_headers") + + local var_mt = getmetatable(_ngx.var) + spy.on(spy_stub, "nop") local router = assert(Router.new(use_case_apis)) local api = router.exec(_ngx) assert.same(use_case_apis[1], api) - assert.spy(_ngx.req.get_headers).was.not_called() + assert.spy(spy_stub.nop).was.not_called() end) - it("reads request headers if required", function() + it("reads Host header if required", function() table.insert(use_case_apis, { name = "api-2", uris = { "/my-api" }, @@ -708,13 +722,13 @@ describe("Router", function() }) local _ngx = mock_ngx("GET", "/my-api", { host = "my-api.com" }) - spy.on(_ngx.req, "get_headers") + spy.on(spy_stub, "nop") local router = assert(Router.new(use_case_apis)) local api = router.exec(_ngx) assert.same(use_case_apis[2], api) - assert.spy(_ngx.req.get_headers).was.called(1) + assert.spy(spy_stub.nop).was.called(1) end) end) diff --git a/spec/02-integration/03-admin_api/01-kong_routes_spec.lua b/spec/02-integration/03-admin_api/01-kong_routes_spec.lua index 9db819b6e52d..dcc9e79ae9c0 100644 --- a/spec/02-integration/03-admin_api/01-kong_routes_spec.lua +++ b/spec/02-integration/03-admin_api/01-kong_routes_spec.lua @@ -38,7 +38,7 @@ describe("Admin API", function() assert.is_nil(res.headers.via) -- Via is only set for proxied requests end) it("returns 405 on invalid method", function() - local methods = {"POST", "PUT", "DELETE", "PATCH"} + local methods = {"POST", "PUT", "DELETE", "PATCH", "GEEEET"} for i = 1, #methods do local res = assert(client:send { method = methods[i], diff --git a/spec/02-integration/03-admin_api/09-targets_routes_spec.lua b/spec/02-integration/03-admin_api/09-targets_routes_spec.lua index cfd5b16d0dd0..18806d8aa28c 100644 --- a/spec/02-integration/03-admin_api/09-targets_routes_spec.lua +++ b/spec/02-integration/03-admin_api/09-targets_routes_spec.lua @@ -259,4 +259,118 @@ describe("Admin API", function() end) end) end) + + describe("/upstreams/{upstream}/targets/active/", function() + describe("only shows active targets", function() + local upstream_name3 = "example.com" + + before_each(function() + local upstream3 = assert(helpers.dao.upstreams:insert { + name = upstream_name3, + }) + + -- two target inserts, resulting in a 'down' target + assert(helpers.dao.targets:insert { + target = "api-1:80", + weight = 10, + upstream_id = upstream3.id, + }) + assert(helpers.dao.targets:insert { + target = "api-1:80", + weight = 0, + upstream_id = upstream3.id, + }) + + -- three target inserts, resulting in an 'up' target + assert(helpers.dao.targets:insert { + target = "api-2:80", + weight = 10, + upstream_id = upstream3.id, + }) + assert(helpers.dao.targets:insert { + target = "api-2:80", + weight = 0, + upstream_id = upstream3.id, + }) + assert(helpers.dao.targets:insert { + target = "api-2:80", + weight = 10, + upstream_id = upstream3.id, + }) + + -- and an insert of a separate active target + assert(helpers.dao.targets:insert { + target = "api-3:80", + weight = 10, + upstream_id = upstream3.id, + }) + end) + + it("only shows active targets", function() + local res = assert(client:send { + method = "GET", + path = "/upstreams/" .. upstream_name3 .. "/targets/active/", + }) + assert.response(res).has.status(200) + local json = assert.response(res).has.jsonbody() + assert.equal(2, #json.data) + assert.equal(2, json.total) + assert.equal("api-3:80", json.data[1].target) + assert.equal("api-2:80", json.data[2].target) + end) + end) + end) + + describe("/upstreams/{upstream}/targets/{target}", function() + describe("DELETE", function() + local target + local upstream_name4 = "example4.com" + + before_each(function() + local upstream4 = assert(helpers.dao.upstreams:insert { + name = upstream_name4, + }) + + assert(helpers.dao.targets:insert { + target = "api-1:80", + weight = 10, + upstream_id = upstream4.id, + }) + + -- predefine the target to mock delete + target = assert(helpers.dao.targets:insert { + target = "api-2:80", + weight = 10, + upstream_id = upstream4.id, + }) + end) + + it("acts as a sugar method to POST a target with 0 weight", function() + local res = assert(client:send { + method = "DELETE", + path = "/upstreams/" .. upstream_name4 .. "/targets/" .. target.target + }) + assert.response(res).has.status(204) + + local targets = assert(client:send { + method = "GET", + path = "/upstreams/" .. upstream_name4 .. "/targets/", + }) + assert.response(targets).has.status(200) + local json = assert.response(targets).has.jsonbody() + assert.equal(3, #json.data) + assert.equal(3, json.total) + + local active = assert(client:send { + method = "GET", + path = "/upstreams/" .. upstream_name4 .. "/targets/active/", + }) + assert.response(active).has.status(200) + json = assert.response(active).has.jsonbody() + assert.equal(1, #json.data) + assert.equal(1, json.total) + assert.equal("api-1:80", json.data[1].target) + end) + end) + end) end) diff --git a/spec/02-integration/05-proxy/01-router_spec.lua b/spec/02-integration/05-proxy/01-router_spec.lua index c62a8b6f5fac..4a5ed55af0bd 100644 --- a/spec/02-integration/05-proxy/01-router_spec.lua +++ b/spec/02-integration/05-proxy/01-router_spec.lua @@ -250,6 +250,12 @@ describe("Router", function() upstream_url = "http://localhost:9999/headers-inspect", hosts = "discarded.com", }, + { + name = "api-3", + preserve_host = true, + upstream_url = "http://localhost:9999/headers-inspect", + uris = { "/api-3" }, + } } assert(helpers.start_kong { @@ -323,6 +329,18 @@ describe("Router", function() local json = cjson.decode(body) assert.equal("preserved.com:123", json.host) end) + + it("forwards request Host if matching without 'hosts' rules", function() + local res = assert(client:send { + method = "GET", + path = "/api-3", + headers = { ["Host"] = "keep-me.com" }, + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal("keep-me.com", json.host) + end) end) end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 35d8b39da25b..b1a9522e660d 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -1,5 +1,6 @@ # This is a custom nginx configuration template for Kong specs +user ${{NGINX_USER}}; worker_processes ${{NGINX_WORKER_PROCESSES}}; daemon ${{NGINX_DAEMON}}; From 1f8b67b5648d94977c0eef46c07af63b4cd42994 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Mon, 3 Apr 2017 22:26:32 +0300 Subject: [PATCH 11/15] Fix review comment: https://github.com/Mashape/kong/pull/2236#discussion_r109083704 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b2f9959f68..cd129593a8ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ - :fireworks: Configurable `X-Forwarded-*` and `X-Real-IP` upstream headers. [#2236](https://github.com/Mashape/kong/pull/2236) -- :fireworks: Support for The PROXY protocol. +- :fireworks: Support for the PROXY protocol. [#2236](https://github.com/Mashape/kong/pull/2236) ## [0.10.1] - 2017/03/27 From d966e41d618ef602545e5932b721e0ec30911242 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Tue, 11 Apr 2017 22:43:07 +0300 Subject: [PATCH 12/15] Fix: https://github.com/Mashape/kong/pull/2236#discussion_r110847266 --- spec/02-integration/05-proxy/02-upstream_headers_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua index 7e05ee75792d..3ab205a52489 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -169,7 +169,7 @@ describe("Upstream header(s)", function() end) end) - describe("with the downstream host preserved (pending: because the test fails to connect to API when preserve_host = true)", function() + describe("with the downstream host preserved", function() it("should be added if not present in request while preserving the downstream host", function() local headers = request_headers { ["Host"] = "preserved.com", From 79c27439300a90ad13c6cd3c2224de1dc1fedf0f Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Wed, 12 Apr 2017 00:01:41 +0300 Subject: [PATCH 13/15] Fix: https://github.com/Mashape/kong/pull/2236#discussion_r110846358 Fix: https://github.com/Mashape/kong/pull/2236#discussion_r110848464 --- kong/core/handler.lua | 34 ++++++++++++++++++---- kong/templates/nginx_kong.lua | 44 ++++++++--------------------- spec/fixtures/custom_nginx.template | 44 ++++++++--------------------- 3 files changed, 52 insertions(+), 70 deletions(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index 995ff00fd226..cd9b906e31b9 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -124,22 +124,44 @@ return { var.upstream_host = host_header or balancer_address.hostname..":"..balancer_address.port - -- X-Forwarded Headers - local realip_remote_addr = var.realip_remote_addr + -- Keep-Alive and WebWocket Protocol Upgrade Headers + if var.http_upgrade == "websocket" then + var.upstream_connection = "upgrade" + var.upstream_upgrade = "websocket" - if not singletons.ip.trusted(realip_remote_addr) then - var.upstream_x_forwarded_proto = var.scheme - var.upstream_x_forwarded_host = var.host - var.upstream_x_forwarded_port = var.server_port + else + var.upstream_connection = "keep-alive" end + -- X-Forwarded Headers + -- + -- We could use $proxy_add_x_forwarded_for, but it does not work properly + -- with the realip module. The realip module overrides $remote_addr and it + -- is okay for us to use it in case no X-Forwarded-For header was present. + -- But in case it was given, we will append the $realip_remote_addr that + -- contains the IP that was originally in $remote_addr before realip module + -- overrode that (aka the client that connected us). + + local realip_remote_addr = var.realip_remote_addr local http_x_forwarded_for = var.http_x_forwarded_for + if http_x_forwarded_for then var.upstream_x_forwarded_for = http_x_forwarded_for .. ", " .. realip_remote_addr else var.upstream_x_forwarded_for = var.remote_addr end + + if singletons.ip.trusted(realip_remote_addr) then + var.upstream_x_forwarded_proto = var.http_x_forwarded_proto or var.scheme + var.upstream_x_forwarded_host = var.http_x_forwarded_host or var.host + var.upstream_x_forwarded_port = var.http_x_forwarded_port or var.server_port + + else + var.upstream_x_forwarded_proto = var.scheme + var.upstream_x_forwarded_host = var.host + var.upstream_x_forwarded_port = var.server_port + end end, -- Only executed if the `router` module found an API and allows nginx to proxy it. after = function() diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index bf469ea631a7..695ee09c011c 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -60,31 +60,6 @@ upstream kong_upstream { keepalive ${{UPSTREAM_KEEPALIVE}}; } -map $http_upgrade $upstream_connection { - default keep-alive; - websocket upgrade; -} - -map $http_upgrade $upstream_upgrade { - default ''; - websocket websocket; -} - -map $http_x_forwarded_proto $upstream_x_forwarded_proto { - default $http_x_forwarded_proto; - '' $scheme; -} - -map $http_x_forwarded_host $upstream_x_forwarded_host { - default $http_x_forwarded_host; - '' $host; -} - -map $http_x_forwarded_port $upstream_x_forwarded_port { - default $http_x_forwarded_port; - '' $server_port; -} - server { server_name kong; > if real_ip_header == "proxy_protocol" then @@ -118,23 +93,28 @@ server { > end location / { - set $upstream_host nil; - set $upstream_scheme nil; - set $upstream_x_forwarded_for nil; + set $upstream_host ''; + set $upstream_upgrade ''; + set $upstream_connection ''; + set $upstream_scheme ''; + set $upstream_x_forwarded_for ''; + set $upstream_x_forwarded_proto ''; + set $upstream_x_forwarded_host ''; + set $upstream_x_forwarded_port ''; access_by_lua_block { kong.access() } proxy_http_version 1.1; - proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; proxy_set_header X-Forwarded-For $upstream_x_forwarded_for; proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto; proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host; proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port; - proxy_set_header Host $upstream_host; - proxy_set_header Upgrade $upstream_upgrade; - proxy_set_header Connection $upstream_connection; + proxy_set_header X-Real-IP $remote_addr; proxy_pass_header Server; proxy_pass_header Date; proxy_pass $upstream_scheme://kong_upstream; diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index b1a9522e660d..7d3d0e2dd55e 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -71,31 +71,6 @@ http { keepalive ${{UPSTREAM_KEEPALIVE}}; } - map $http_upgrade $upstream_connection { - default keep-alive; - websocket upgrade; - } - - map $http_upgrade $upstream_upgrade { - default ''; - websocket websocket; - } - - map $http_x_forwarded_proto $upstream_x_forwarded_proto { - default $http_x_forwarded_proto; - '' $scheme; - } - - map $http_x_forwarded_host $upstream_x_forwarded_host { - default $http_x_forwarded_host; - '' $host; - } - - map $http_x_forwarded_port $upstream_x_forwarded_port { - default $http_x_forwarded_port; - '' $server_port; - } - server { server_name kong; > if real_ip_header == "proxy_protocol" then @@ -129,23 +104,28 @@ http { > end location / { - set $upstream_host nil; - set $upstream_scheme nil; - set $upstream_x_forwarded_for nil; + set $upstream_host ''; + set $upstream_upgrade ''; + set $upstream_connection ''; + set $upstream_scheme ''; + set $upstream_x_forwarded_for ''; + set $upstream_x_forwarded_proto ''; + set $upstream_x_forwarded_host ''; + set $upstream_x_forwarded_port ''; access_by_lua_block { kong.access() } proxy_http_version 1.1; - proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; proxy_set_header X-Forwarded-For $upstream_x_forwarded_for; proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto; proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host; proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port; - proxy_set_header Host $upstream_host; - proxy_set_header Upgrade $upstream_upgrade; - proxy_set_header Connection $upstream_connection; + proxy_set_header X-Real-IP $remote_addr; proxy_pass_header Server; proxy_pass_header Date; proxy_pass $upstream_scheme://kong_upstream; From f4d7859d90360604f8138fe11c5a01b4131a6da2 Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Fri, 14 Apr 2017 00:20:56 +0300 Subject: [PATCH 14/15] Fixed a typo in WebSocket. --- kong/core/handler.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index cd9b906e31b9..5ee9c82551de 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -124,7 +124,7 @@ return { var.upstream_host = host_header or balancer_address.hostname..":"..balancer_address.port - -- Keep-Alive and WebWocket Protocol Upgrade Headers + -- Keep-Alive and WebSocket Protocol Upgrade Headers if var.http_upgrade == "websocket" then var.upstream_connection = "upgrade" var.upstream_upgrade = "websocket" From 5045b3d778dec76400af75d1db2c46eac6bc2adc Mon Sep 17 00:00:00 2001 From: Aapo Talvensaari Date: Fri, 14 Apr 2017 13:22:18 +0300 Subject: [PATCH 15/15] fix: https://github.com/Mashape/kong/pull/2236#discussion_r111505419 --- kong/core/handler.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index 5ee9c82551de..7ec81f3b29d7 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -20,6 +20,7 @@ local balancer_execute = require("kong.core.balancer").execute local router, router_err local tostring = tostring +local lower = string.lower local ngx = ngx local ngx_now = ngx.now local server_header = _KONG._NAME.."/".._KONG._VERSION @@ -125,7 +126,7 @@ return { balancer_address.hostname..":"..balancer_address.port -- Keep-Alive and WebSocket Protocol Upgrade Headers - if var.http_upgrade == "websocket" then + if var.http_upgrade and lower(var.http_upgrade) == "websocket" then var.upstream_connection = "upgrade" var.upstream_upgrade = "websocket"