316 lines
11 KiB
Lua
316 lines
11 KiB
Lua
--- Client information module.
|
|
--
|
|
-- A set of functions to retrieve information about the client connecting to
|
|
-- Kong in the context of a given request.
|
|
--
|
|
-- See also:
|
|
-- [nginx.org/en/docs/http/ngx_http_realip_module.html](http://nginx.org/en/docs/http/ngx_http_realip_module.html)
|
|
-- @module kong.client
|
|
|
|
|
|
local phase_checker = require "kong.pdk.private.phases"
|
|
local is_valid_uuid = require("kong.tools.uuid").is_valid_uuid
|
|
local check_https = require("kong.tools.http").check_https
|
|
|
|
|
|
local ngx = ngx
|
|
local tonumber = tonumber
|
|
local check_phase = phase_checker.check
|
|
local check_not_phase = phase_checker.check_not
|
|
|
|
|
|
local PHASES = phase_checker.phases
|
|
local AUTH_AND_LATER = phase_checker.new(PHASES.access,
|
|
PHASES.header_filter,
|
|
PHASES.response,
|
|
PHASES.body_filter,
|
|
PHASES.log)
|
|
local TABLE_OR_NIL = { ["table"] = true, ["nil"] = true }
|
|
|
|
local stream_subsystem = ngx.config.subsystem == "stream"
|
|
|
|
|
|
local function new(self)
|
|
local _CLIENT = {}
|
|
|
|
|
|
---
|
|
-- Returns the remote address of the client making the request. This module
|
|
-- **always** returns the address of the client directly connecting to Kong.
|
|
-- That is, in cases when a load balancer is in front of Kong, this function
|
|
-- returns the load balancer's address, and **not** that of the
|
|
-- downstream client.
|
|
--
|
|
-- @function kong.client.get_ip
|
|
-- @phases certificate, rewrite, access, header_filter, response, body_filter, log
|
|
-- @treturn string The remote IP address of the client making the request.
|
|
-- @usage
|
|
-- -- Given a client with IP 127.0.0.1 making connection through
|
|
-- -- a load balancer with IP 10.0.0.1 to Kong answering the request for
|
|
-- -- https://example.com:1234/v1/movies
|
|
-- kong.client.get_ip() -- "10.0.0.1"
|
|
function _CLIENT.get_ip()
|
|
check_not_phase(PHASES.init_worker)
|
|
|
|
-- when proxying TLS request in second layer or doing TLS passthrough
|
|
-- realip_remote_addr is always the previous layer of nginx thus always unix:
|
|
if stream_subsystem and
|
|
(ngx.var.kong_tls_passthrough_block == "1" or ngx.var.ssl_protocol) then
|
|
return ngx.var.remote_addr
|
|
end
|
|
|
|
return ngx.var.realip_remote_addr or ngx.var.remote_addr
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the remote address of the client making the request. Unlike
|
|
-- `kong.client.get_ip`, this function will consider forwarded addresses in
|
|
-- cases when a load balancer is in front of Kong. Whether this function
|
|
-- returns a forwarded address or not depends on several Kong configuration
|
|
-- parameters:
|
|
--
|
|
-- * [trusted\_ips](https://docs.konghq.com/gateway/latest/reference/configuration/#trusted_ips)
|
|
-- * [real\_ip\_header](https://docs.konghq.com/gateway/latest/reference/configuration/#real_ip_header)
|
|
-- * [real\_ip\_recursive](https://docs.konghq.com/gateway/latest/reference/configuration/#real_ip_recursive)
|
|
--
|
|
-- @function kong.client.get_forwarded_ip
|
|
-- @phases certificate, rewrite, access, header_filter, response, body_filter, log
|
|
-- @treturn string The remote IP address of the client making the request,
|
|
-- considering forwarded addresses.
|
|
--
|
|
-- @usage
|
|
-- -- Given a client with IP 127.0.0.1 making connection through
|
|
-- -- a load balancer with IP 10.0.0.1 to Kong answering the request for
|
|
-- -- https://username:password@example.com:1234/v1/movies
|
|
--
|
|
-- kong.client.get_forwarded_ip() -- "127.0.0.1"
|
|
--
|
|
-- -- Note: This example assumes that 10.0.0.1 is one of the trusted IPs, and that
|
|
-- -- the load balancer adds the right headers matching with the configuration
|
|
-- -- of `real_ip_header`, e.g. `proxy_protocol`.
|
|
function _CLIENT.get_forwarded_ip()
|
|
check_not_phase(PHASES.init_worker)
|
|
|
|
return ngx.var.remote_addr
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the remote port of the client making the request. This
|
|
-- **always** returns the port of the client directly connecting to Kong. That
|
|
-- is, in cases when a load balancer is in front of Kong, this function
|
|
-- returns the load balancer's port, and **not** that of the downstream client.
|
|
-- @function kong.client.get_port
|
|
-- @phases certificate, rewrite, access, header_filter, response, body_filter, log
|
|
-- @treturn number The remote client port.
|
|
-- @usage
|
|
-- -- [client]:40000 <-> 80:[balancer]:30000 <-> 80:[kong]:20000 <-> 80:[service]
|
|
-- kong.client.get_port() -- 30000
|
|
function _CLIENT.get_port()
|
|
check_not_phase(PHASES.init_worker)
|
|
|
|
-- when proxying TLS request in second layer or doing TLS passthrough
|
|
-- realip_remote_addr is always the previous layer of nginx thus always unix:
|
|
if stream_subsystem and
|
|
(ngx.var.kong_tls_passthrough_block == "1" or ngx.var.ssl_protocol) then
|
|
return tonumber(ngx.var.remote_port)
|
|
end
|
|
|
|
return tonumber(ngx.var.realip_remote_port or ngx.var.remote_port)
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the remote port of the client making the request. Unlike
|
|
-- `kong.client.get_port`, this function will consider forwarded ports in cases
|
|
-- when a load balancer is in front of Kong. Whether this function returns a
|
|
-- forwarded port or not depends on several Kong configuration parameters:
|
|
--
|
|
-- * [trusted\_ips](https://docs.konghq.com/gateway/latest/reference/configuration/#trusted_ips)
|
|
-- * [real\_ip\_header](https://docs.konghq.com/gateway/latest/reference/configuration/#real_ip_header)
|
|
-- * [real\_ip\_recursive](https://docs.konghq.com/gateway/latest/reference/configuration/#real_ip_recursive)
|
|
-- @function kong.client.get_forwarded_port
|
|
-- @phases certificate, rewrite, access, header_filter, response, body_filter, log
|
|
-- @treturn number The remote client port, considering forwarded ports.
|
|
-- @usage
|
|
-- -- [client]:40000 <-> 80:[balancer]:30000 <-> 80:[kong]:20000 <-> 80:[service]
|
|
-- kong.client.get_forwarded_port() -- 40000
|
|
--
|
|
-- -- Note: This example assumes that [balancer] is one of the trusted IPs, and that
|
|
-- -- the load balancer adds the right headers matching with the configuration
|
|
-- -- of `real_ip_header`, e.g. `proxy_protocol`.
|
|
function _CLIENT.get_forwarded_port()
|
|
check_not_phase(PHASES.init_worker)
|
|
|
|
return tonumber(ngx.var.remote_port)
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the credentials of the currently authenticated consumer.
|
|
-- If not set yet, it returns `nil`.
|
|
-- @function kong.client.get_credential
|
|
-- @phases access, header_filter, response, body_filter, log
|
|
-- @treturn string The authenticated credential.
|
|
-- @usage
|
|
-- local credential = kong.client.get_credential()
|
|
-- if credential then
|
|
-- consumer_id = credential.consumer_id
|
|
-- else
|
|
-- -- request not authenticated yet
|
|
-- end
|
|
function _CLIENT.get_credential()
|
|
check_phase(AUTH_AND_LATER)
|
|
|
|
return ngx.ctx.authenticated_credential
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the consumer from the datastore.
|
|
-- Looks up the consumer by ID, and can optionally do a second search by name.
|
|
-- @function kong.client.load_consumer
|
|
-- @phases access, header_filter, response, body_filter, log
|
|
-- @tparam string consumer_id The consumer ID to look up.
|
|
-- @tparam[opt] boolean search_by_username If truthy,
|
|
-- and if the consumer is not found by ID,
|
|
-- then a second search by username will be performed.
|
|
-- @treturn table|nil Consumer entity or `nil`.
|
|
-- @treturn nil|err `nil` if successful, or an error message if it fails.
|
|
-- @usage
|
|
-- local consumer_id = "john_doe"
|
|
-- local consumer = kong.client.load_consumer(consumer_id, true)
|
|
function _CLIENT.load_consumer(consumer_id, search_by_username)
|
|
check_phase(AUTH_AND_LATER)
|
|
|
|
if not consumer_id or type(consumer_id) ~= "string" then
|
|
error("consumer_id must be a string", 2)
|
|
end
|
|
|
|
if not is_valid_uuid(consumer_id) and not search_by_username then
|
|
error("cannot load a consumer with an id that is not a uuid", 2)
|
|
end
|
|
|
|
if is_valid_uuid(consumer_id) then
|
|
local result, err = kong.db.consumers:select({ id = consumer_id })
|
|
|
|
if result then
|
|
return result
|
|
end
|
|
|
|
if err then
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
-- no error and if search_by_username, look up by username
|
|
if search_by_username then
|
|
return kong.db.consumers:select_by_username(consumer_id)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the `consumer` entity of the currently authenticated consumer.
|
|
-- If not set yet, it returns `nil`.
|
|
-- @function kong.client.get_consumer
|
|
-- @phases access, header_filter, response, body_filter, log
|
|
-- @treturn table The authenticated consumer entity.
|
|
-- @usage
|
|
-- local consumer = kong.client.get_consumer()
|
|
-- if consumer then
|
|
-- consumer_id = consumer.id
|
|
-- else
|
|
-- -- request not authenticated yet, or a credential
|
|
-- -- without a consumer (external auth)
|
|
-- end
|
|
function _CLIENT.get_consumer()
|
|
check_phase(AUTH_AND_LATER)
|
|
|
|
return ngx.ctx.authenticated_consumer
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets the authenticated consumer and/or credential for the current request.
|
|
-- While both `consumer` and `credential` can be `nil`,
|
|
-- at least one of them must exist. Otherwise, this function will throw an
|
|
-- error.
|
|
-- @function kong.client.authenticate
|
|
-- @phases access
|
|
-- @tparam table|nil consumer The consumer to set. If no
|
|
-- value is provided, then any existing value will be cleared.
|
|
-- @tparam table|nil credential The credential to set. If
|
|
-- no value is provided, then any existing value will be cleared.
|
|
-- @usage
|
|
-- -- assuming `credential` and `consumer` have been set by some authentication code
|
|
-- kong.client.authenticate(consumer, credentials)
|
|
function _CLIENT.authenticate(consumer, credential)
|
|
check_phase(PHASES.access)
|
|
|
|
if not TABLE_OR_NIL[type(consumer)] then
|
|
error("consumer must be a table or nil", 2)
|
|
elseif not TABLE_OR_NIL[type(credential)] then
|
|
error("credential must be a table or nil", 2)
|
|
elseif credential == nil and consumer == nil then
|
|
error("either credential or consumer must be provided", 2)
|
|
end
|
|
|
|
local ctx = ngx.ctx
|
|
ctx.authenticated_consumer = consumer
|
|
ctx.authenticated_credential = credential
|
|
end
|
|
|
|
|
|
---
|
|
-- Returns the protocol matched by the current route (`"http"`, `"https"`, `"tcp"` or
|
|
-- `"tls"`), or `nil`, if no route has been matched, which can happen when dealing with
|
|
-- erroneous requests.
|
|
-- @function kong.client.get_protocol
|
|
-- @phases access, header_filter, response, body_filter, log
|
|
-- @tparam[opt] boolean allow_terminated If set, the `X-Forwarded-Proto` header is checked when checking for HTTPS.
|
|
-- @treturn string|nil Can be one of `"http"`, `"https"`, `"tcp"`, `"tls"` or `nil`.
|
|
-- @treturn nil|err `nil` if successful, or an error message if it fails.
|
|
-- @usage
|
|
-- kong.client.get_protocol() -- "http"
|
|
function _CLIENT.get_protocol(allow_terminated)
|
|
check_phase(AUTH_AND_LATER)
|
|
|
|
local route = ngx.ctx.route
|
|
if not route then
|
|
return nil, "No active route found"
|
|
end
|
|
|
|
local protocols = route.protocols
|
|
if #protocols == 1 then
|
|
return protocols[1]
|
|
end
|
|
|
|
if ngx.config.subsystem == "http" then
|
|
local is_trusted = self.ip.is_trusted(self.client.get_ip())
|
|
local is_https, err = check_https(is_trusted, allow_terminated)
|
|
if err then
|
|
return nil, err
|
|
end
|
|
|
|
return is_https and "https" or "http"
|
|
end
|
|
-- else subsystem is stream
|
|
|
|
local balancer_data = ngx.ctx.balancer_data
|
|
local is_tls = balancer_data and balancer_data.scheme == "tls"
|
|
|
|
return is_tls and "tls" or "tcp"
|
|
end
|
|
|
|
|
|
return _CLIENT
|
|
end
|
|
|
|
|
|
return {
|
|
new = new,
|
|
}
|