diff --git a/kong.conf.default b/kong.conf.default index 604a7c41865..b069c57ff34 100644 --- a/kong.conf.default +++ b/kong.conf.default @@ -72,25 +72,31 @@ # NGINX #------------------------------------------------------------------------------ -#proxy_listen = 0.0.0.0:8000 # Address and port on which Kong will accept - # HTTP requests. - # This is the public-facing entrypoint of - # Kong, to which your consumers will make - # requests. +#proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443 ssl + # Comma-separated list of addresses and ports on + # which the proxy server should listen to. The proxy + # server is the public entrypoint of Kong, which + # proxies traffic from your consumers. The `ssl` + # suffix requires that all connections through this + # port be made through SSL, and `http2` allows for + # HTTP/2 connections. And finally the `proxy_protocol` + # flag is allowed. + # If set to `off` the proxy will be disabled. # Note: See http://nginx.org/en/docs/http/ngx_http_core_module.html#listen for # a description of the accepted formats for this and other *_listen values. +# +# See https://www.nginx.com/resources/admin-guide/proxy-protocol/ for more +# details about the `proxy_protocol` parameter. -#proxy_listen_ssl = 0.0.0.0:8443 # Address and port on which Kong will accept - # HTTPS requests if `ssl` is enabled. - -#admin_listen = 127.0.0.1:8001 # Address and port on which Kong will expose - # an entrypoint to the Admin API. - # This API lets you configure and manage Kong, - # and should be kept private and secured. - -#admin_listen_ssl = 127.0.0.1:8444 # Address and port on which Kong will - # accept HTTPS requests to the admin API, - # if `admin_ssl` is enabled. +#admin_listen = 127.0.0.1:8000, 127.0.0.1:8443 ssl + # Comma-separated list of addresses and ports on + # which the admin interface is listening. + # This API lets you configure and manage Kong, + # and should be kept private and secured. + # The `ssl` suffix requires that all connections + # through this port be made through SSL, and `http2` + # allows for HTTP/2 connections. + # If set to `off` the Admin API will be disabled. #nginx_user = nobody nobody # Defines user and group credentials used by # worker processes. If group is omitted, a @@ -110,12 +116,6 @@ # `m`, with a minimum recommended value of # a few MBs. -#ssl = on # Determines if Nginx should be listening for - # HTTPS traffic on the `proxy_listen_ssl` - # address. If disabled, Nginx will only bind - # itself on `proxy_listen`, and all SSL - # settings will be ignored. - #ssl_cipher_suite = modern # Defines the TLS ciphers served by Nginx. # Accepted values are `modern`, `intermediate`, # `old`, or `custom`. @@ -128,16 +128,13 @@ # This value is ignored if `ssl_cipher_suite` # is not `custom`. -#ssl_cert = # If `ssl` is enabled, the absolute path to +#ssl_cert = # If ssl is enabled, the absolute path to # the SSL certificate for the - # `proxy_listen_ssl` address. - -#ssl_cert_key = # If `ssl` is enabled, the absolute path to - # the SSL key for the `proxy_listen_ssl` - # address. + # `proxy_listen` ssl addresses. -#http2 = off # Enables HTTP2 support for HTTPS traffic on - # the `proxy_listen_ssl` address. +#ssl_cert_key = # If ssl is enabled, the absolute path to + # the SSL key for the `proxy_listen` ssl + # addresses. #client_ssl = off # Determines if Nginx should send client-side # SSL certificates when proxying requests. @@ -156,22 +153,13 @@ # and currently cannot be configured on a # per-API basis. -#admin_ssl = on # Determines if Nginx should be listening for - # HTTPS traffic on the `admin_listen_ssl` - # address. If disabled, Nginx will only bind - # itself on `admin_listen`, and all SSL - # settings will be ignored. +#admin_ssl_cert = # If ssl is enabled on the admin interface, + # the absolute path to the SSL certificate + # for the `admin_listen` addresses. -#admin_ssl_cert = # If `admin_ssl` is enabled, the absolute path - # to the SSL certificate for the - # `admin_listen_ssl` address. - -#admin_ssl_cert_key = # If `admin_ssl` is enabled, the absolute path - # to the SSL key for the `admin_listen_ssl` - # address. - -#admin_http2 = off # Enables HTTP2 support for HTTPS traffic on - # the `admin_listen_ssl` address. +#admin_ssl_cert_key = # If ssl is enabled on the admin interface, + # the absolute path to the SSL key for the + # `admin_listen` addresses. #upstream_keepalive = 60 # Sets the maximum number of idle keepalive # connections to upstream servers that are @@ -216,18 +204,13 @@ # This value sets the ngx_http_realip_module # directive of the same name in the Nginx # configuration. - # - # If this value receives `proxy_protocol`, the - # `proxy_protocol` parameter will be appended - # to the `listen` directive of the Nginx - # template. + # If set to `proxy_protocol` then at least one + # of the `proxy_listen` entries should have + # the `proxy_protocol` flag enabled. # Note: # # See http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header # for a description of this directive. -# -# See https://www.nginx.com/resources/admin-guide/proxy-protocol/ for more -# details about the `proxy_protocol` parameter. #real_ip_recursive = off # This value sets the ngx_http_realip_module # directive of the same name in the Nginx @@ -492,3 +475,4 @@ #lua_socket_pool_size = 30 # Specifies the size limit for every cosocket # connection pool associated with every remote # server + diff --git a/kong/cmd/utils/prefix_handler.lua b/kong/cmd/utils/prefix_handler.lua index d1d54ac13a0..267eedad8fd 100644 --- a/kong/cmd/utils/prefix_handler.lua +++ b/kong/cmd/utils/prefix_handler.lua @@ -73,7 +73,7 @@ local function get_ulimit() end end -local function gather_system_infos(compile_env) +local function gather_system_infos() local infos = {} local ulimit, err = get_ulimit() @@ -110,10 +110,6 @@ local function compile_conf(kong_config, conf_template) compile_env = pl_tablex.merge(compile_env, kong_config, true) -- union compile_env.dns_resolver = table.concat(compile_env.dns_resolver, " ") - compile_env.http2 = kong_config.http2 and " http2" or "" - compile_env.admin_http2 = kong_config.admin_http2 and " http2" or "" - compile_env.proxy_protocol = kong_config.real_ip_header == "proxy_protocol" and " proxy_protocol" or "" - local post_template = pl_template.substitute(conf_template, compile_env) return string.gsub(post_template, "(${%b{}})", function(w) local name = w:sub(4, -3) @@ -202,7 +198,8 @@ local function prepare_prefix(kong_config, nginx_custom_template_path) end -- generate default SSL certs if needed - if kong_config.ssl and not kong_config.ssl_cert and not kong_config.ssl_cert_key then + if kong_config.proxy_ssl_enabled and not kong_config.ssl_cert and + not kong_config.ssl_cert_key then log.verbose("SSL enabled, no custom certificate set: using default certificate") local ok, err = gen_default_ssl_cert(kong_config) if not ok then @@ -211,7 +208,8 @@ local function prepare_prefix(kong_config, nginx_custom_template_path) kong_config.ssl_cert = kong_config.ssl_cert_default kong_config.ssl_cert_key = kong_config.ssl_cert_key_default end - if kong_config.admin_ssl and not kong_config.admin_ssl_cert and not kong_config.admin_ssl_cert_key then + if kong_config.admin_ssl_enabled and not kong_config.admin_ssl_cert and + not kong_config.admin_ssl_cert_key then log.verbose("Admin SSL enabled, no custom certificate set: using default certificate") local ok, err = gen_default_ssl_cert(kong_config, true) if not ok then @@ -266,7 +264,12 @@ local function prepare_prefix(kong_config, nginx_custom_template_path) for k, v in pairs(kong_config) do if type(v) == "table" then - v = table.concat(v, ",") + if (getmetatable(v) or {}).__tostring then + -- the 'tostring' meta-method knows how to serialize + v = tostring(v) + else + v = table.concat(v, ",") + end end if v ~= "" then buf[#buf+1] = k .. " = " .. tostring(v) diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index 3e2d5575103..2f592cefe66 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -53,10 +53,8 @@ local PREFIX_PATHS = { -- `array`: a comma-separated list local CONF_INFERENCES = { -- forced string inferences (or else are retrieved as numbers) - proxy_listen = {typ = "string"}, - proxy_listen_ssl = {typ = "string"}, - admin_listen = {typ = "string"}, - admin_listen_ssl = {typ = "string"}, + proxy_listen = {typ = "array"}, + admin_listen = {typ = "array"}, db_update_frequency = { typ = "number" }, db_update_propagation = { typ = "number" }, db_cache_ttl = { typ = "number" }, @@ -102,11 +100,7 @@ local CONF_INFERENCES = { dns_error_ttl = {typ = "number"}, dns_no_sync = {typ = "boolean"}, - http2 = {typ = "boolean"}, - admin_http2 = {typ = "boolean"}, - ssl = {typ = "boolean"}, client_ssl = {typ = "boolean"}, - admin_ssl = {typ = "boolean"}, proxy_access_log = {typ = "string"}, proxy_error_log = {typ = "string"}, @@ -230,7 +224,7 @@ local function check_and_infer(conf) end end - if conf.ssl then + if (table.concat(conf.proxy_listen, ",") .. " "):find("%sssl[%s,]") then if conf.ssl_cert and not conf.ssl_cert_key then errors[#errors+1] = "ssl_cert_key must be specified" elseif conf.ssl_cert_key and not conf.ssl_cert then @@ -260,7 +254,7 @@ local function check_and_infer(conf) end end - if conf.admin_ssl then + if (table.concat(conf.admin_listen, ",") .. " "):find("%sssl[%s,]") then if conf.admin_ssl_cert and not conf.admin_ssl_cert_key then errors[#errors+1] = "admin_ssl_cert_key must be specified" elseif conf.admin_ssl_cert_key and not conf.admin_ssl_cert then @@ -357,6 +351,85 @@ local function overrides(k, default_v, file_conf, arg_conf) return value, k end +-- @param value The options string to check for flags (whitespace separated) +-- @param flags List of boolean flags to check for. +-- @returns 1) remainder string after all flags removed, 2) table with flag +-- booleans, 3) sanitized flags string +local function parse_option_flags(value, flags) + assert(type(value) == "string") + + value = " " .. value .. " " + + local sanitized = "" + local result = {} + + for _, flag in ipairs(flags) do + local count + local patt = "%s" .. flag .. "%s" + + value, count = value:gsub(patt, " ") + + if count > 0 then + result[flag] = true + sanitized = sanitized .. " " .. flag + + else + result[flag] = false + end + end + + return pl_stringx.strip(value), result, pl_stringx.strip(sanitized) +end + +-- Parses a listener address line. +-- Supports multiple (comma separated) addresses, with 'ssl' and 'http2' flags. +-- Pre- and postfixed whitespace as well as comma's are allowed. +-- "off" as a first entry will return empty tables. +-- @value list of entries (strings) +-- @return list of parsed entries, each entry having fields `ip` (normalized string) +-- `port` (number), `ssl` (bool), `http2` (bool), `listener` (string, full listener) +local function parse_listeners(values) + local list = {} + local flags = { "ssl", "http2", "proxy_protocol" } + local usage = "must be of form: [off] | : [" .. + table.concat(flags, "] [") .. "], [... next entry ...]" + + if pl_stringx.strip(values[1]) == "off" then + return list + end + + for _, entry in ipairs(values) do + -- parse the flags + local remainder, listener, cleaned_flags = parse_option_flags(entry, flags) + + -- verify IP for remainder + local ip + + if utils.hostname_type(remainder) == "name" then + -- it's not an IP address, so a name/wildcard/regex + ip = {} + ip.host, ip.port = remainder:match("(.+):([%d]+)$") + + else + -- It's an IPv4 or IPv6, just normalize it + ip = utils.normalize_ip(remainder) + end + + if not ip or not ip.port then + return nil, usage + end + + listener.ip = ip.host + listener.port = ip.port + listener.listener = ip.host .. ":" .. ip.port .. + (#cleaned_flags == 0 and "" or " " .. cleaned_flags) + + table.insert(list, listener) + end + + return list +end + --- Load Kong configuration -- The loaded configuration will have all properties from the default config -- merged with the (optionally) specified config file, environment variables @@ -478,24 +551,40 @@ local function load(path, custom_conf) -- extract ports/listen ips do - local ip_port_pat = "(.+):([%d]+)$" - local admin_ip, admin_port = string.match(conf.admin_listen, ip_port_pat) - local admin_ssl_ip, admin_ssl_port = string.match(conf.admin_listen_ssl, ip_port_pat) - local proxy_ip, proxy_port = string.match(conf.proxy_listen, ip_port_pat) - local proxy_ssl_ip, proxy_ssl_port = string.match(conf.proxy_listen_ssl, ip_port_pat) - - if not admin_port then return nil, "admin_listen must be of form 'address:port'" - elseif not admin_ssl_port then return nil, "admin_listen_ssl must be of form 'address:port'" - elseif not proxy_port then return nil, "proxy_listen must be of form 'address:port'" - elseif not proxy_ssl_port then return nil, "proxy_listen_ssl must be of form 'address:port'" end - conf.admin_ip = admin_ip - conf.admin_ssl_ip = admin_ssl_ip - conf.proxy_ip = proxy_ip - conf.proxy_ssl_ip = proxy_ssl_ip - conf.admin_port = tonumber(admin_port) - conf.admin_ssl_port = tonumber(admin_ssl_port) - conf.proxy_port = tonumber(proxy_port) - conf.proxy_ssl_port = tonumber(proxy_ssl_port) + local err + -- this meta table will prevent the parsed table to be passed on in the + -- intermediate Kong config file in the prefix directory + local mt = { __tostring = function() return "" end } + + conf.proxy_listeners, err = parse_listeners(conf.proxy_listen) + if err then + return nil, "proxy_listen " .. err + end + + setmetatable(conf.proxy_listeners, mt) -- do not pass on, parse again + conf.proxy_ssl_enabled = false + + for _, listener in ipairs(conf.proxy_listeners) do + if listener.ssl == true then + conf.proxy_ssl_enabled = true + break + end + end + + conf.admin_listeners, err = parse_listeners(conf.admin_listen) + if err then + return nil, "admin_listen " .. err + end + + setmetatable(conf.admin_listeners, mt) -- do not pass on, parse again + conf.admin_ssl_enabled = false + + for _, listener in ipairs(conf.admin_listeners) do + if listener.ssl == true then + conf.admin_ssl_enabled = true + break + end + end end -- load absolute paths diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 8510077d679..354696b46be 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -8,17 +8,13 @@ admin_error_log = logs/error.log custom_plugins = NONE anonymous_reports = on -proxy_listen = 0.0.0.0:8000 -proxy_listen_ssl = 0.0.0.0:8443 -admin_listen = 127.0.0.1:8001 -admin_listen_ssl = 127.0.0.1:8444 +proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443 ssl +admin_listen = 127.0.0.1:8001, 127.0.0.1:8444 ssl nginx_user = nobody nobody nginx_worker_processes = auto nginx_optimizations = on nginx_daemon = on mem_cache_size = 128m -http2 = off -ssl = on ssl_cert = NONE ssl_cert_key = NONE client_ssl = off @@ -26,8 +22,6 @@ client_ssl_cert = NONE client_ssl_cert_key = NONE ssl_cipher_suite = modern ssl_ciphers = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 -admin_http2 = off -admin_ssl = on admin_ssl_cert = NONE admin_ssl_cert_key = NONE upstream_keepalive = 60 diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index 8a6abd644fa..8ef037f9fe2 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -53,6 +53,7 @@ init_worker_by_lua_block { proxy_next_upstream_tries 999; +> if #proxy_listeners > 0 then upstream kong_upstream { server 0.0.0.1; balancer_by_lua_block { @@ -63,7 +64,9 @@ upstream kong_upstream { server { server_name kong; - listen ${{PROXY_LISTEN}}${{PROXY_PROTOCOL}}; +> for i = 1, #proxy_listeners do + listen $(proxy_listeners[i].listener); +> end error_page 400 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; @@ -72,8 +75,7 @@ server { client_body_buffer_size ${{CLIENT_BODY_BUFFER_SIZE}}; -> if ssl then - listen ${{PROXY_LISTEN_SSL}} ssl${{HTTP2}}${{PROXY_PROTOCOL}}; +> if proxy_ssl_enabled then ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -151,10 +153,14 @@ server { } } } +> end +> if #admin_listeners > 0 then server { server_name kong_admin; - listen ${{ADMIN_LISTEN}}; +> for i = 1, #admin_listeners do + listen $(admin_listeners[i].listener); +> end access_log ${{ADMIN_ACCESS_LOG}}; error_log ${{ADMIN_ERROR_LOG}} ${{LOG_LEVEL}}; @@ -162,8 +168,7 @@ server { client_max_body_size 10m; client_body_buffer_size 10m; -> if admin_ssl then - listen ${{ADMIN_LISTEN_SSL}} ssl${{ADMIN_HTTP2}}; +> if admin_ssl_enabled then ssl_certificate ${{ADMIN_SSL_CERT}}; ssl_certificate_key ${{ADMIN_SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -191,4 +196,5 @@ server { return 200 'User-agent: *\nDisallow: /'; } } +> end ]] diff --git a/spec/01-unit/002-conf_loader_spec.lua b/spec/01-unit/002-conf_loader_spec.lua index bce139b72fc..63698b18c0b 100644 --- a/spec/01-unit/002-conf_loader_spec.lua +++ b/spec/01-unit/002-conf_loader_spec.lua @@ -7,10 +7,8 @@ describe("Configuration loader", function() assert.is_string(conf.lua_package_path) assert.is_nil(conf.nginx_user) assert.equal("auto", conf.nginx_worker_processes) - assert.equal("127.0.0.1:8001", conf.admin_listen) - assert.equal("0.0.0.0:8000", conf.proxy_listen) - assert.equal("0.0.0.0:8443", conf.proxy_listen_ssl) - assert.equal("127.0.0.1:8444", conf.admin_listen_ssl) + assert.same({"127.0.0.1:8001", "127.0.0.1:8444 ssl"}, conf.admin_listen) + assert.same({"0.0.0.0:8000", "0.0.0.0:8443 ssl"}, conf.proxy_listen) assert.is_nil(conf.ssl_cert) -- check placeholder value assert.is_nil(conf.ssl_cert_key) assert.is_nil(conf.admin_ssl_cert) @@ -24,9 +22,8 @@ describe("Configuration loader", function() -- overrides assert.is_nil(conf.nginx_user) assert.equal("1", conf.nginx_worker_processes) - assert.equal("127.0.0.1:9001", conf.admin_listen) - assert.equal("0.0.0.0:9000", conf.proxy_listen) - assert.equal("0.0.0.0:9443", conf.proxy_listen_ssl) + assert.same({"127.0.0.1:9001"}, conf.admin_listen) + assert.same({"0.0.0.0:9000", "0.0.0.0:9443 ssl"}, conf.proxy_listen) assert.is_nil(getmetatable(conf)) end) it("preserves default properties if not in given file", function() @@ -43,9 +40,8 @@ describe("Configuration loader", function() -- overrides assert.is_nil(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) - assert.equal("0.0.0.0:9443", conf.proxy_listen_ssl) + assert.same({"127.0.0.1:9001"}, conf.admin_listen) + assert.same({"0.0.0.0:9000", "0.0.0.0:9443 ssl"}, conf.proxy_listen) assert.is_nil(getmetatable(conf)) end) it("strips extraneous properties (not in defaults)", function() @@ -73,16 +69,74 @@ describe("Configuration loader", function() assert.True(conf.plugins["hello-world"]) assert.True(conf.plugins["another-one"]) end) - it("extracts ports and listen ips from proxy_listen/admin_listen", function() + it("extracts flags, ports and listen ips from proxy_listen/admin_listen", function() local conf = assert(conf_loader()) - assert.equal("127.0.0.1", conf.admin_ip) - assert.equal(8001, conf.admin_port) - assert.equal("127.0.0.1", conf.admin_ssl_ip) - assert.equal(8444, conf.admin_ssl_port) - assert.equal("0.0.0.0", conf.proxy_ip) - assert.equal(8000, conf.proxy_port) - assert.equal("0.0.0.0", conf.proxy_ssl_ip) - assert.equal(8443, conf.proxy_ssl_port) + assert.equal("127.0.0.1", conf.admin_listeners[1].ip) + assert.equal(8001, conf.admin_listeners[1].port) + assert.equal(false, conf.admin_listeners[1].ssl) + assert.equal(false, conf.admin_listeners[1].http2) + assert.equal("127.0.0.1:8001", conf.admin_listeners[1].listener) + + assert.equal("127.0.0.1", conf.admin_listeners[2].ip) + assert.equal(8444, conf.admin_listeners[2].port) + assert.equal(true, conf.admin_listeners[2].ssl) + assert.equal(false, conf.admin_listeners[2].http2) + assert.equal("127.0.0.1:8444 ssl", conf.admin_listeners[2].listener) + + assert.equal("0.0.0.0", conf.proxy_listeners[1].ip) + assert.equal(8000, conf.proxy_listeners[1].port) + assert.equal(false, conf.proxy_listeners[1].ssl) + assert.equal(false, conf.proxy_listeners[1].http2) + assert.equal("0.0.0.0:8000", conf.proxy_listeners[1].listener) + + assert.equal("0.0.0.0", conf.proxy_listeners[2].ip) + assert.equal(8443, conf.proxy_listeners[2].port) + assert.equal(true, conf.proxy_listeners[2].ssl) + assert.equal(false, conf.proxy_listeners[2].http2) + assert.equal("0.0.0.0:8443 ssl", conf.proxy_listeners[2].listener) + end) + it("extracts ssl flags properly when hostnames contain them", function() + local conf + conf = assert(conf_loader(nil, { + proxy_listen = "ssl.myname.com:8000", + admin_listen = "ssl.myname.com:8001", + })) + assert.equal("ssl.myname.com", conf.proxy_listeners[1].ip) + assert.equal(false, conf.proxy_listeners[1].ssl) + assert.equal("ssl.myname.com", conf.admin_listeners[1].ip) + assert.equal(false, conf.admin_listeners[1].ssl) + + conf = assert(conf_loader(nil, { + proxy_listen = "ssl_myname.com:8000 ssl", + admin_listen = "ssl_myname.com:8001 ssl", + })) + assert.equal("ssl_myname.com", conf.proxy_listeners[1].ip) + assert.equal(true, conf.proxy_listeners[1].ssl) + assert.equal("ssl_myname.com", conf.admin_listeners[1].ip) + assert.equal(true, conf.admin_listeners[1].ssl) + end) + it("extracts 'off' from proxy_listen/admin_listen", function() + local conf + conf = assert(conf_loader(nil, { + proxy_listen = "off", + admin_listen = "off", + })) + assert.same({}, conf.proxy_listeners) + assert.same({}, conf.admin_listeners) + -- off with multiple entries + conf = assert(conf_loader(nil, { + proxy_listen = "off, 0.0.0.0:9000", + admin_listen = "off, 127.0.0.1:9001", + })) + assert.same({}, conf.proxy_listeners) + assert.same({}, conf.admin_listeners) + -- not off with names containing 'off' + conf = assert(conf_loader(nil, { + proxy_listen = "offshore.com:9000", + admin_listen = "offshore.com:9001", + })) + assert.same("offshore.com", conf.proxy_listeners[1].ip) + assert.same("offshore.com", conf.admin_listeners[1].ip) end) it("attaches prefix paths", function() local conf = assert(conf_loader()) @@ -231,25 +285,13 @@ describe("Configuration loader", function() admin_listen = "127.0.0.1" }) assert.is_nil(conf) - assert.equal("admin_listen must be of form 'address:port'", err) - - conf, err = conf_loader(nil, { - admin_listen_ssl = "127.0.0.1" - }) - assert.is_nil(conf) - assert.equal("admin_listen_ssl must be of form 'address:port'", err) + assert.equal("admin_listen must be of form: [off] | : [ssl] [http2] [proxy_protocol], [... next entry ...]", err) conf, err = conf_loader(nil, { proxy_listen = "127.0.0.1" }) assert.is_nil(conf) - assert.equal("proxy_listen must be of form 'address:port'", err) - - conf, err = conf_loader(nil, { - proxy_listen_ssl = "127.0.0.1" - }) - assert.is_nil(conf) - assert.equal("proxy_listen_ssl must be of form 'address:port'", err) + assert.equal("proxy_listen must be of form: [off] | : [ssl] [http2] [proxy_protocol], [... next entry ...]", err) end) it("errors when dns_resolver is not a list in ipv4/6[:port] format", function() local conf, err = conf_loader(nil, { @@ -316,16 +358,23 @@ describe("Configuration loader", function() assert.equal("bad cassandra contact point 'addr1:9042': port must be specified in cassandra_port", err) assert.is_nil(conf) end) - it("does not check SSL cert and key if SSL is off", function() - local conf, err = conf_loader(nil, { - ssl = false, - ssl_cert = "/path/cert.pem" - }) - assert.is_nil(err) - assert.is_table(conf) - end) describe("SSL", function() describe("proxy", function() + it("does not check SSL cert and key if SSL is off", function() + local conf, err = conf_loader(nil, { + proxy_listen = "127.0.0.1:123", + ssl_cert = "/path/cert.pem" + }) + assert.is_nil(err) + assert.is_table(conf) + -- specific case with 'ssl' in the name + local conf, err = conf_loader(nil, { + proxy_listen = "ssl:23", + proxy_ssl_cert = "/path/cert.pem" + }) + assert.is_nil(err) + assert.is_table(conf) + end) it("requires both proxy SSL cert and key", function() local conf, err = conf_loader(nil, { ssl_cert = "/path/cert.pem" @@ -470,6 +519,21 @@ describe("Configuration loader", function() end) end) describe("admin", function() + it("does not check SSL cert and key if SSL is off", function() + local conf, err = conf_loader(nil, { + admin_listen = "127.0.0.1:123", + admin_ssl_cert = "/path/cert.pem" + }) + assert.is_nil(err) + assert.is_table(conf) + -- specific case with 'ssl' in the name + local conf, err = conf_loader(nil, { + admin_listen = "ssl:23", + admin_ssl_cert = "/path/cert.pem" + }) + assert.is_nil(err) + assert.is_table(conf) + end) it("requires both admin SSL cert and key", function() local conf, err = conf_loader(nil, { admin_ssl_cert = "/path/cert.pem" diff --git a/spec/01-unit/003-prefix_handler_spec.lua b/spec/01-unit/003-prefix_handler_spec.lua index f1b60ca6e01..627c0068dcc 100644 --- a/spec/01-unit/003-prefix_handler_spec.lua +++ b/spec/01-unit/003-prefix_handler_spec.lua @@ -9,10 +9,8 @@ describe("NGINX conf compiler", function() describe("gen_default_ssl_cert()", function() local conf = assert(conf_loader(helpers.test_conf_path, { prefix = "ssl_tmp", - ssl = true, ssl_cert = "spec/fixtures/kong_spec.crt", ssl_cert_key = "spec/fixtures/kong_spec.key", - admin_ssl = true, admin_ssl_cert = "spec/fixtures/kong_spec.crt", admin_ssl_cert_key = "spec/fixtures/kong_spec.key", })) @@ -77,17 +75,18 @@ describe("NGINX conf compiler", function() end) it("enables HTTP/2", function() local conf = assert(conf_loader(helpers.test_conf_path, { - http2 = true, - admin_http2 = true + proxy_listen = "0.0.0.0:9000, 0.0.0.0:9443 http2 ssl", + admin_listen = "127.0.0.1:9001, 127.0.0.1:9444 http2 ssl", })) local kong_nginx_conf = prefix_handler.compile_kong_conf(conf) assert.matches("listen 0.0.0.0:9000;", kong_nginx_conf, nil, true) assert.matches("listen 0.0.0.0:9443 ssl http2;", kong_nginx_conf, nil, true) assert.matches("listen 127.0.0.1:9001;", kong_nginx_conf, nil, true) - assert.matches("listen 127.0.0.1:8444 ssl http2;", kong_nginx_conf, nil, true) + assert.matches("listen 127.0.0.1:9444 ssl http2;", kong_nginx_conf, nil, true) conf = assert(conf_loader(helpers.test_conf_path, { - http2 = true, + proxy_listen = "0.0.0.0:9000, 0.0.0.0:9443 http2 ssl", + admin_listen = "127.0.0.1:9001, 127.0.0.1:8444 ssl", })) kong_nginx_conf = prefix_handler.compile_kong_conf(conf) assert.matches("listen 0.0.0.0:9000;", kong_nginx_conf, nil, true) @@ -96,7 +95,8 @@ describe("NGINX conf compiler", function() assert.matches("listen 127.0.0.1:8444 ssl;", kong_nginx_conf, nil, true) conf = assert(conf_loader(helpers.test_conf_path, { - admin_http2 = true + proxy_listen = "0.0.0.0:9000, 0.0.0.0:9443 ssl", + admin_listen = "127.0.0.1:9001, 127.0.0.1:8444 http2 ssl", })) kong_nginx_conf = prefix_handler.compile_kong_conf(conf) assert.matches("listen 0.0.0.0:9000;", kong_nginx_conf, nil, true) @@ -106,16 +106,17 @@ describe("NGINX conf compiler", function() end) it("enables proxy_protocol", function() local conf = assert(conf_loader(helpers.test_conf_path, { + proxy_listen = "0.0.0.0:9000 proxy_protocol", real_ip_header = "proxy_protocol", })) local kong_nginx_conf = prefix_handler.compile_kong_conf(conf) assert.matches("listen 0.0.0.0:9000 proxy_protocol;", kong_nginx_conf, nil, true) - assert.matches("listen 0.0.0.0:9443 ssl proxy_protocol;", kong_nginx_conf, nil, true) + assert.matches("real_ip_header%s+proxy_protocol;", kong_nginx_conf) end) it("disables SSL", function() local conf = assert(conf_loader(helpers.test_conf_path, { - ssl = false, - admin_ssl = false + proxy_listen = "127.0.0.1:8000", + admin_listen = "127.0.0.1:8001", })) local kong_nginx_conf = prefix_handler.compile_kong_conf(conf) assert.not_matches("listen %d+%.%d+%.%d+%.%d+:%d+ ssl;", kong_nginx_conf) @@ -279,12 +280,13 @@ describe("NGINX conf compiler", function() end) it("proxy_protocol", function() local conf = assert(conf_loader(nil, { - real_ip_header = "proxy_protocol" + proxy_listen = "0.0.0.0:8000 proxy_protocol, 0.0.0.0:8443 ssl", + 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 ssl proxy_protocol;", nginx_conf) + assert.matches("listen 0.0.0.0:8443 ssl;", nginx_conf) end) end) end) @@ -411,8 +413,8 @@ describe("NGINX conf compiler", function() it("does not create SSL dir if disabled", function() local conf = conf_loader(nil, { prefix = tmp_config.prefix, - ssl = false, - admin_ssl = false + proxy_listen = "127.0.0.1:8000", + admin_listen = "127.0.0.1:8001", }) assert(prefix_handler.prepare_prefix(conf)) @@ -421,10 +423,10 @@ describe("NGINX conf compiler", function() it("does not create SSL dir if using custom cert", function() local conf = conf_loader(nil, { prefix = tmp_config.prefix, - ssl = true, + proxy_listen = "127.0.0.1:8000 ssl", + admin_listen = "127.0.0.1:8001 ssl", ssl_cert = "spec/fixtures/kong_spec.crt", ssl_cert_key = "spec/fixtures/kong_spec.key", - admin_ssl = true, admin_ssl_cert = "spec/fixtures/kong_spec.crt", admin_ssl_cert_key = "spec/fixtures/kong_spec.key", }) @@ -435,8 +437,8 @@ describe("NGINX conf compiler", function() it("generates default SSL cert", function() local conf = conf_loader(nil, { prefix = tmp_config.prefix, - ssl = true, - admin_ssl = true + proxy_listen = "127.0.0.1:8000 ssl", + admin_listen = "127.0.0.1:8001 ssl", }) assert(prefix_handler.prepare_prefix(conf)) diff --git a/spec/02-integration/02-cmd/02-start_stop_spec.lua b/spec/02-integration/02-cmd/02-start_stop_spec.lua index 6ee00f7594b..f41db25bfa5 100644 --- a/spec/02-integration/02-cmd/02-start_stop_spec.lua +++ b/spec/02-integration/02-cmd/02-start_stop_spec.lua @@ -173,7 +173,7 @@ describe("kong start/stop", function() assert.matches("Kong is already running in " .. helpers.test_conf.prefix, stderr, nil, true) end) it("stops other services when could not start", function() - local thread = helpers.tcp_server(helpers.test_conf.proxy_port) + local thread = helpers.tcp_server(helpers.test_conf.proxy_listeners[1].port) finally(function() -- make tcp server receive and close helpers.proxy_client():send { diff --git a/spec/02-integration/04-admin_api/00-admin_api_spec.lua b/spec/02-integration/04-admin_api/00-admin_api_spec.lua new file mode 100644 index 00000000000..5ba4404a2ae --- /dev/null +++ b/spec/02-integration/04-admin_api/00-admin_api_spec.lua @@ -0,0 +1,76 @@ +local helpers = require "spec.helpers" +local utils = require "pl.utils" +local stringx = require "pl.stringx" +local http = require "resty.http" + + +local function count_server_blocks(filename) + local file = assert(utils.readfile(filename)) + local _, count = file:gsub("[%\n%s]+server%s{","") + return count +end + + +local function get_listeners(filename) + local file = assert(utils.readfile(filename)) + local result = {} + for block in file:gmatch("[%\n%s]+server%s+(%b{})") do + local server = {} + local server_name = stringx.strip(block:match("[%\n%s]server_name%s(.-);")) + result[server_name] = server + for listen in block:gmatch("[%\n%s]listen%s(.-);") do + listen = stringx.strip(listen) + table.insert(server, listen) + server[listen] = #server + end + end + return result +end + + +describe("Admin API listeners", function() + before_each(function() + helpers.run_migrations() + end) + + after_each(function() + helpers.stop_kong() + end) + + it("disabled", function() + assert(helpers.start_kong({ + proxy_listen = "0.0.0.0:9000", + admin_listen = "off", + })) + assert.equals(1, count_server_blocks(helpers.test_conf.nginx_kong_conf)) + assert.is_nil(get_listeners(helpers.test_conf.nginx_kong_conf).kong_admin) + end) + + it("multiple", function() + assert(helpers.start_kong({ + proxy_listen = "0.0.0.0:9000", + admin_listen = "127.0.0.1:9001, 127.0.0.1:9002", + })) + + assert.equals(2, count_server_blocks(helpers.test_conf.nginx_kong_conf)) + assert.same({ + ["127.0.0.1:9001"] = 1, + ["127.0.0.1:9002"] = 2, + [1] = "127.0.0.1:9001", + [2] = "127.0.0.1:9002", + }, get_listeners(helpers.test_conf.nginx_kong_conf).kong_admin) + + for i = 9001, 9002 do + local client = assert(http.new()) + assert(client:connect("127.0.0.1", i)) + + local res = assert(client:request { + method = "GET", + path = "/" + }) + res:read_body() + client:close() + assert.equals(200, res.status) + end + end) +end) diff --git a/spec/02-integration/04-admin_api/01-kong_routes_spec.lua b/spec/02-integration/04-admin_api/01-kong_routes_spec.lua index a7b62c3a2b3..37515e95154 100644 --- a/spec/02-integration/04-admin_api/01-kong_routes_spec.lua +++ b/spec/02-integration/04-admin_api/01-kong_routes_spec.lua @@ -91,7 +91,7 @@ describe("Admin API - Kong routes", function() local body = assert.response(res).has.status(200) local json = cjson.decode(body) assert.is_table(json.prng_seeds) - for k, v in pairs(json.prng_seeds) do + for k in pairs(json.prng_seeds) do assert.matches("pid: %d+", k) assert.matches("%d+", k) end diff --git a/spec/02-integration/05-proxy/00-proxy_spec.lua b/spec/02-integration/05-proxy/00-proxy_spec.lua new file mode 100644 index 00000000000..468195d9eb5 --- /dev/null +++ b/spec/02-integration/05-proxy/00-proxy_spec.lua @@ -0,0 +1,76 @@ +local helpers = require "spec.helpers" +local utils = require "pl.utils" +local stringx = require "pl.stringx" +local http = require "resty.http" + + +local function count_server_blocks(filename) + local file = assert(utils.readfile(filename)) + local _, count = file:gsub("[%\n%s]+server%s{","") + return count +end + + +local function get_listeners(filename) + local file = assert(utils.readfile(filename)) + local result = {} + for block in file:gmatch("[%\n%s]+server%s+(%b{})") do + local server = {} + local server_name = stringx.strip(block:match("[%\n%s]server_name%s(.-);")) + result[server_name] = server + for listen in block:gmatch("[%\n%s]listen%s(.-);") do + listen = stringx.strip(listen) + table.insert(server, listen) + server[listen] = #server + end + end + return result +end + + +describe("Proxy interface listeners", function() + before_each(function() + helpers.run_migrations() + end) + + after_each(function() + helpers.stop_kong() + end) + + it("disabled", function() + assert(helpers.start_kong({ + proxy_listen = "off", + admin_listen = "0.0.0.0:9001", + })) + assert.equals(1, count_server_blocks(helpers.test_conf.nginx_kong_conf)) + assert.is_nil(get_listeners(helpers.test_conf.nginx_kong_conf).kong) + end) + + it("multiple", function() + assert(helpers.start_kong({ + proxy_listen = "127.0.0.1:9001, 127.0.0.1:9002", + admin_listen = "0.0.0.0:9000", + })) + + assert.equals(2, count_server_blocks(helpers.test_conf.nginx_kong_conf)) + assert.same({ + ["127.0.0.1:9001"] = 1, + ["127.0.0.1:9002"] = 2, + [1] = "127.0.0.1:9001", + [2] = "127.0.0.1:9002", + }, get_listeners(helpers.test_conf.nginx_kong_conf).kong) + + for i = 9001, 9002 do + local client = assert(http.new()) + assert(client:connect("127.0.0.1", i)) + + local res = assert(client:request { + method = "GET", + path = "/" + }) + res:read_body() + client:close() + assert.equals(404, res.status) + 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 c331bf24e5b..65bc388985a 100644 --- a/spec/02-integration/05-proxy/02-upstream_headers_spec.lua +++ b/spec/02-integration/05-proxy/02-upstream_headers_spec.lua @@ -156,7 +156,7 @@ describe("Upstream header(s)", function() ["Host"] = "headers-inspect.com", } - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) it("should be replaced if present in request", function() @@ -165,7 +165,7 @@ describe("Upstream header(s)", function() ["X-Forwarded-Port"] = "80", } - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) end) @@ -180,7 +180,7 @@ describe("Upstream header(s)", function() 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"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) it("should be added if present in request while preserving the downstream host", function() @@ -198,7 +198,7 @@ describe("Upstream header(s)", function() 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"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) end) @@ -215,7 +215,7 @@ describe("Upstream header(s)", function() assert.equal(helpers.mock_upstream_host, headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) assert.equal("headers-inspect.com", headers["x-forwarded-host"]) - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) it("if present in request while discarding the downstream host", function() @@ -235,7 +235,7 @@ describe("Upstream header(s)", function() assert.equal("10.0.0.1, 127.0.0.1", headers["x-forwarded-for"]) assert.equal("http", headers["x-forwarded-proto"]) assert.equal("headers-inspect.com", headers["x-forwarded-host"]) - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) end) @@ -335,7 +335,7 @@ describe("Upstream header(s)", function() ["Host"] = "headers-inspect.com", } - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) it("should be forwarded if present in request", function() @@ -445,7 +445,7 @@ describe("Upstream header(s)", function() ["Host"] = "headers-inspect.com", } - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) it("should be replaced if present in request", function() @@ -454,7 +454,7 @@ describe("Upstream header(s)", function() ["X-Forwarded-Port"] = "80", } - assert.equal(helpers.test_conf.proxy_port, tonumber(headers["x-forwarded-port"])) + assert.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) end) end) @@ -565,7 +565,7 @@ describe("Upstream header(s)", function() 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.equal(helpers.get_proxy_port(false), 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() @@ -577,7 +577,7 @@ describe("Upstream header(s)", function() 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.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) end) end) @@ -586,6 +586,7 @@ describe("Upstream header(s)", function() describe("(using trusted proxy protocol configuration values)", function() setup(start_kong { + proxy_listen = "0.0.0.0:9000 proxy_protocol", real_ip_header = "proxy_protocol", real_ip_recursive = "on", trusted_ips = "127.0.0.1,172.16.0.1,192.168.0.1", @@ -598,13 +599,14 @@ describe("Upstream header(s)", function() 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\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:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -619,13 +621,14 @@ describe("Upstream header(s)", function() 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.equal(helpers.get_proxy_port(false), 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\r\n" .. "GET / HTTP/1.1\r\n" .. "Host: headers-inspect.com\r\n" .. "Connection: close\r\n" .. @@ -633,7 +636,7 @@ describe("Upstream header(s)", function() "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:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -655,7 +658,8 @@ describe("Upstream header(s)", function() 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\r\n" .. "GET / HTTP/1.1\r\n" .. "Host: headers-inspect.com\r\n" .. "Connection: close\r\n" .. @@ -664,7 +668,7 @@ describe("Upstream header(s)", function() "X-Forwarded-Port: 14\r\n" .. "\r\n" - assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -688,6 +692,7 @@ describe("Upstream header(s)", function() describe("(using non-trusted proxy protocol configuration values)", function() setup(start_kong { + proxy_listen = "0.0.0.0:9000 proxy_protocol", real_ip_header = "proxy_protocol", real_ip_recursive = "on", trusted_ips = "10.0.0.1,172.16.0.1,192.168.0.1", @@ -700,13 +705,14 @@ describe("Upstream header(s)", function() 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\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:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -721,13 +727,14 @@ describe("Upstream header(s)", function() 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.equal(helpers.get_proxy_port(false), 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\r\n" .. "GET / HTTP/1.1\r\n" .. "Host: headers-inspect.com\r\n" .. "Connection: close\r\n" .. @@ -735,7 +742,7 @@ describe("Upstream header(s)", function() "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:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -757,7 +764,8 @@ describe("Upstream header(s)", function() 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" .. + local request = "PROXY TCP4 192.168.0.1 " .. helpers.get_proxy_ip(false) .. + " 56324 " .. helpers.get_proxy_port(false) .. "\r\n" .. "GET / HTTP/1.1\r\n" .. "Host: headers-inspect.com\r\n" .. "Connection: close\r\n" .. @@ -766,7 +774,7 @@ describe("Upstream header(s)", function() "X-Forwarded-Port: 14\r\n" .. "\r\n" - assert(sock:connect(helpers.test_conf.proxy_ip, helpers.test_conf.proxy_port)) + assert(sock:connect(helpers.get_proxy_ip(false), helpers.get_proxy_port(false))) assert(sock:send(request)) local response, err = sock:receive "*a" @@ -781,7 +789,7 @@ describe("Upstream header(s)", function() 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.equal(helpers.get_proxy_port(false), tonumber(headers["x-forwarded-port"])) assert(sock:close()) end) end) diff --git a/spec/02-integration/05-proxy/05-ssl_spec.lua b/spec/02-integration/05-proxy/05-ssl_spec.lua index 2d3750aed30..b7fa592c467 100644 --- a/spec/02-integration/05-proxy/05-ssl_spec.lua +++ b/spec/02-integration/05-proxy/05-ssl_spec.lua @@ -6,7 +6,7 @@ local cjson = require "cjson" local function get_cert(server_name) local _, _, stdout = assert(helpers.execute( string.format("echo 'GET /' | openssl s_client -connect 0.0.0.0:%d -servername %s", - helpers.test_conf.proxy_ssl_port, server_name) + helpers.get_proxy_port(true), server_name) )) return stdout diff --git a/spec/02-integration/05-proxy/08-websockets_spec.lua b/spec/02-integration/05-proxy/08-websockets_spec.lua index ba339c86a34..321a21a8b4b 100644 --- a/spec/02-integration/05-proxy/08-websockets_spec.lua +++ b/spec/02-integration/05-proxy/08-websockets_spec.lua @@ -46,13 +46,13 @@ describe("Websockets", function() end) it("sends and gets text with Kong", function() - send_text_and_get_echo("ws://" .. helpers.test_conf.proxy_ip .. - ":" .. helpers.test_conf.proxy_port .. "/up-ws") + send_text_and_get_echo("ws://" .. helpers.get_proxy_ip(false) .. + ":" .. helpers.get_proxy_port(false) .. "/up-ws") end) it("sends and gets text with kong under HTTPS", function() - send_text_and_get_echo("wss://" .. helpers.test_conf.proxy_ssl_ip .. - ":" .. helpers.test_conf.proxy_ssl_port .. "/up-ws") + send_text_and_get_echo("wss://" .. helpers.get_proxy_ip(true) .. + ":" .. helpers.get_proxy_port(true) .. "/up-ws") end) end) @@ -76,13 +76,13 @@ describe("Websockets", function() end) it("plays ping-pong with Kong", function() - send_ping_and_get_pong("ws://" .. helpers.test_conf.proxy_ip .. - ":" .. helpers.test_conf.proxy_port .. "/up-ws") + send_ping_and_get_pong("ws://" .. helpers.get_proxy_ip(false) .. + ":" .. helpers.get_proxy_port(false) .. "/up-ws") end) it("plays ping-pong with kong under HTTPS", function() - send_ping_and_get_pong("wss://" .. helpers.test_conf.proxy_ssl_ip .. - ":" .. helpers.test_conf.proxy_ssl_port .. "/up-ws") + send_ping_and_get_pong("wss://" .. helpers.get_proxy_ip(true) .. + ":" .. helpers.get_proxy_port(true) .. "/up-ws") end) end) end) diff --git a/spec/02-integration/06-invalidations/02-core_entities_invalidations_spec.lua b/spec/02-integration/06-invalidations/02-core_entities_invalidations_spec.lua index 8efa8dc59e2..92232556ae4 100644 --- a/spec/02-integration/06-invalidations/02-core_entities_invalidations_spec.lua +++ b/spec/02-integration/06-invalidations/02-core_entities_invalidations_spec.lua @@ -32,10 +32,8 @@ describe("core entities are invalidated with db: #" .. kong_conf.database, funct log_level = "debug", prefix = "servroot1", database = kong_conf.database, - proxy_listen = "0.0.0.0:8000", - proxy_listen_ssl = "0.0.0.0:8443", + proxy_listen = "0.0.0.0:8000, 0.0.0.0:8443 ssl", admin_listen = "0.0.0.0:8001", - admin_ssl = false, db_update_frequency = POLL_INTERVAL, db_update_propagation = db_update_propagation, nginx_conf = "spec/fixtures/custom_nginx.template", @@ -45,10 +43,8 @@ describe("core entities are invalidated with db: #" .. kong_conf.database, funct log_level = "debug", prefix = "servroot2", database = kong_conf.database, - proxy_listen = "0.0.0.0:9000", - proxy_listen_ssl = "0.0.0.0:9443", + proxy_listen = "0.0.0.0:9000, 0.0.0.0:9443 ssl", admin_listen = "0.0.0.0:9001", - admin_ssl = false, db_update_frequency = POLL_INTERVAL, db_update_propagation = db_update_propagation, }) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 0f3e4ae80cd..1d7cf3d5b77 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -66,6 +66,7 @@ http { proxy_next_upstream_tries 999; +> if #proxy_listeners > 0 then upstream kong_upstream { server 0.0.0.1; balancer_by_lua_block { @@ -76,7 +77,9 @@ http { server { server_name kong; - listen ${{PROXY_LISTEN}}${{PROXY_PROTOCOL}}; +> for i = 1, #proxy_listeners do + listen $(proxy_listeners[i].listener); +> end error_page 400 404 408 411 412 413 414 417 /kong_error_handler; error_page 500 502 503 504 /kong_error_handler; @@ -84,8 +87,7 @@ http { client_body_buffer_size ${{CLIENT_BODY_BUFFER_SIZE}}; -> if ssl then - listen ${{PROXY_LISTEN_SSL}} ssl${{HTTP2}}${{PROXY_PROTOCOL}}; +> if proxy_ssl_enabled then ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -154,18 +156,21 @@ http { } } } +> end +> if #admin_listeners > 0 then server { server_name kong_admin; - listen ${{ADMIN_LISTEN}}; +> for i = 1, #admin_listeners do + listen $(admin_listeners[i].listener); +> end access_log logs/admin_access.log; client_max_body_size 10m; client_body_buffer_size 10m; -> if admin_ssl then - listen ${{ADMIN_LISTEN_SSL}} ssl${{ADMIN_HTTP2}}; +> if admin_ssl_enabled then ssl_certificate ${{ADMIN_SSL_CERT}}; ssl_certificate_key ${{ADMIN_SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; @@ -188,6 +193,7 @@ http { return 200 'User-agent: *\nDisallow: /'; } } +> end server { server_name mock_aws_lambda; diff --git a/spec/helpers.lua b/spec/helpers.lua index 9d147c0d252..c56528b6af9 100644 --- a/spec/helpers.lua +++ b/spec/helpers.lua @@ -280,16 +280,46 @@ local function http_client(host, port, timeout) }, resty_http_proxy_mt) end +--- Returns the proxy port. +-- @param ssl (boolean) if `true` returns the ssl port +local function get_proxy_port(ssl) + if ssl == nil then ssl = false end + for _, entry in ipairs(conf.proxy_listeners) do + if entry.ssl == ssl then + return entry.port + end + end + error("No proxy port found for ssl=" .. tostring(ssl), 2) +end + +--- Returns the proxy ip. +-- @param ssl (boolean) if `true` returns the ssl ip address +local function get_proxy_ip(ssl) + if ssl == nil then ssl = false end + for _, entry in ipairs(conf.proxy_listeners) do + if entry.ssl == ssl then + return entry.ip + end + end + error("No proxy ip found for ssl=" .. tostring(ssl), 2) +end + --- returns a pre-configured `http_client` for the Kong proxy port. -- @name proxy_client local function proxy_client(timeout) - return http_client(conf.proxy_ip, conf.proxy_port, timeout) + local proxy_ip = get_proxy_ip(false) + local proxy_port = get_proxy_port(false) + assert(proxy_ip, "No http-proxy found in the configuration") + return http_client(proxy_ip, proxy_port, timeout) end --- returns a pre-configured `http_client` for the Kong SSL proxy port. -- @name proxy_ssl_client local function proxy_ssl_client(timeout) - local client = http_client(conf.proxy_ip, conf.proxy_ssl_port, timeout) + local proxy_ip = get_proxy_ip(true) + local proxy_port = get_proxy_port(true) + assert(proxy_ip, "No https-proxy found in the configuration") + local client = http_client(proxy_ip, proxy_port, timeout) assert(client:ssl_handshake()) return client end @@ -297,7 +327,31 @@ end --- returns a pre-configured `http_client` for the Kong admin port. -- @name admin_client local function admin_client(timeout) - return http_client(conf.admin_ip, conf.admin_port, timeout) + local admin_ip, admin_port + for _, entry in ipairs(conf.admin_listeners) do + if entry.ssl == false then + admin_ip = entry.ip + admin_port = entry.port + end + end + assert(admin_ip, "No http-admin found in the configuration") + return http_client(admin_ip, admin_port, timeout) +end + +--- returns a pre-configured `http_client` for the Kong admin SSL port. +-- @name admin_ssl_client +local function admin_ssl_client(timeout) + local admin_ip, admin_port + for _, entry in ipairs(conf.proxy_listeners) do + if entry.ssl == true then + admin_ip = entry.ip + admin_port = entry.port + end + end + assert(admin_ip, "No https-admin found in the configuration") + local client = http_client(admin_ip, admin_port, timeout) + assert(client:ssl_handshake()) + return client end --- @@ -982,9 +1036,12 @@ return { tcp_server = tcp_server, udp_server = udp_server, http_server = http_server, + get_proxy_ip = get_proxy_ip, + get_proxy_port = get_proxy_port, proxy_client = proxy_client, admin_client = admin_client, proxy_ssl_client = proxy_ssl_client, + admin_ssl_client = admin_ssl_client, prepare_prefix = prepare_prefix, clean_prefix = clean_prefix, wait_for_invalidation = wait_for_invalidation, diff --git a/spec/kong_tests.conf b/spec/kong_tests.conf index e00231c5f5d..14b80cba729 100644 --- a/spec/kong_tests.conf +++ b/spec/kong_tests.conf @@ -1,7 +1,6 @@ # 1st digit is 9 for our test instances admin_listen = 127.0.0.1:9001 -proxy_listen = 0.0.0.0:9000 -proxy_listen_ssl = 0.0.0.0:9443 +proxy_listen = 0.0.0.0:9000, 0.0.0.0:9443 ssl ssl_cert = spec/fixtures/kong_spec.crt ssl_cert_key = spec/fixtures/kong_spec.key