349 lines
12 KiB
Lua
349 lines
12 KiB
Lua
---
|
|
-- The service module contains a set of functions to manipulate the connection
|
|
-- aspect of the request to the Service, such as connecting to a given host, IP
|
|
-- address/port, or choosing a given Upstream entity for load-balancing and
|
|
-- healthchecking.
|
|
--
|
|
-- @module kong.service
|
|
|
|
|
|
local balancer = require "kong.runloop.balancer"
|
|
local phase_checker = require "kong.pdk.private.phases"
|
|
|
|
|
|
local type = type
|
|
local error = error
|
|
local floor = math.floor
|
|
|
|
|
|
local ngx = ngx
|
|
local check_phase = phase_checker.check
|
|
|
|
|
|
local PHASES = phase_checker.phases
|
|
local access_and_rewrite_and_balancer_preread =
|
|
phase_checker.new(PHASES.rewrite, PHASES.access, PHASES.balancer, PHASES.preread)
|
|
|
|
local function new()
|
|
local service = {}
|
|
|
|
|
|
---
|
|
-- Sets the desired Upstream entity to handle the load-balancing step for
|
|
-- this request. Using this method is equivalent to creating a Service with a
|
|
-- `host` property equal to that of an Upstream entity (in which case, the
|
|
-- request would be proxied to one of the Targets associated with that
|
|
-- Upstream).
|
|
--
|
|
-- The `host` argument should receive a string equal to the name of one of the
|
|
-- Upstream entities currently configured.
|
|
--
|
|
-- @function kong.service.set_upstream
|
|
-- @phases access
|
|
-- @tparam string host
|
|
-- @treturn boolean|nil `true` on success, or `nil` if no upstream entities
|
|
-- where found
|
|
-- @treturn string|nil An error message describing the error if there was
|
|
-- one.
|
|
--
|
|
-- @usage
|
|
-- local ok, err = kong.service.set_upstream("service.prod")
|
|
-- if not ok then
|
|
-- kong.log.err(err)
|
|
-- return
|
|
-- end
|
|
function service.set_upstream(host)
|
|
check_phase(PHASES.access)
|
|
|
|
if type(host) ~= "string" then
|
|
error("host must be a string", 2)
|
|
end
|
|
|
|
local upstream = balancer.get_upstream_by_name(host)
|
|
if not upstream then
|
|
return nil, "could not find an Upstream named '" .. host .. "'"
|
|
end
|
|
|
|
ngx.ctx.balancer_data.host = host
|
|
return true
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets the host and port on which to connect to for proxying the request.
|
|
-- Using this method is equivalent to ask Kong to not run the load-balancing
|
|
-- phase for this request, and consider it manually overridden.
|
|
-- Load-balancing components such as retries and health-checks will also be
|
|
-- ignored for this request. Use `kong.service.set_retries` to overwrite
|
|
-- retries count.
|
|
--
|
|
-- The `host` argument expects the hostname or IP address of the upstream
|
|
-- server, and the `port` expects a port number.
|
|
--
|
|
-- @function kong.service.set_target
|
|
-- @phases access
|
|
-- @tparam string host
|
|
-- @tparam number port
|
|
-- @usage
|
|
-- kong.service.set_target("service.local", 443)
|
|
-- kong.service.set_target("192.168.130.1", 80)
|
|
function service.set_target(host, port)
|
|
check_phase(access_and_rewrite_and_balancer_preread)
|
|
|
|
if type(host) ~= "string" then
|
|
error("host must be a string", 2)
|
|
end
|
|
if type(port) ~= "number" or floor(port) ~= port then
|
|
error("port must be an integer", 2)
|
|
end
|
|
if port < 0 or port > 65535 then
|
|
error("port must be an integer between 0 and 65535: given " .. port, 2)
|
|
end
|
|
|
|
ngx.var.upstream_host = host
|
|
|
|
local ctx = ngx.ctx
|
|
ctx.balancer_data.host = host
|
|
ctx.balancer_data.port = port
|
|
end
|
|
|
|
|
|
-- Sets the retry callback function when the target set by `service.set_target`
|
|
-- failed to connect. The callback function will be called with no argument and
|
|
-- must return `host`, `port` and `err` if any.
|
|
--
|
|
--
|
|
-- @function kong.service.set_target_retry_callback
|
|
-- @phases access
|
|
-- @tparam function retry_callback
|
|
-- @usage
|
|
-- kong.service.set_target_retry_callback(function() return "service.local", 443 end)
|
|
function service.set_target_retry_callback(retry_callback)
|
|
check_phase(PHASES.access)
|
|
|
|
if type(retry_callback) ~= "function" then
|
|
error("retry_callback must be a function", 2)
|
|
end
|
|
|
|
ngx.ctx.balancer_data.retry_callback = retry_callback
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets the retries count for the current request. This will override the
|
|
-- default retries count set in the Upstream entity.
|
|
--
|
|
-- The `retries` argument expects an integer between 0 and 32767.
|
|
--
|
|
-- @function kong.service.set_retries
|
|
-- @phases access
|
|
-- @tparam number retries
|
|
-- @usage
|
|
-- kong.service.set_retries(233)
|
|
function service.set_retries(retries)
|
|
check_phase(PHASES.access)
|
|
|
|
if type(retries) ~= "number" or floor(retries) ~= retries then
|
|
error("retries must be an integer", 2)
|
|
end
|
|
if retries < 0 or retries > 32767 then
|
|
error("retries must be an integer between 0 and 32767: given " .. retries, 2)
|
|
end
|
|
|
|
local ctx = ngx.ctx
|
|
ctx.balancer_data.retries = retries
|
|
end
|
|
|
|
---
|
|
-- Sets the timeouts for the current request. This will override the
|
|
-- default timeouts set in the Upstream entity.
|
|
--
|
|
-- The `connect_timeout`, `write_timeout`, and `read_timeout` arguments expect
|
|
-- an integer between 1 and 2147483646.
|
|
--
|
|
-- @function kong.service.set_timeouts
|
|
-- @phases access
|
|
-- @tparam number connect_timeout
|
|
-- @tparam number write_timeout
|
|
-- @tparam number read_timeout
|
|
-- @usage
|
|
-- kong.service.set_timeouts(233, 233, 233)
|
|
function service.set_timeouts(connect_timeout, write_timeout, read_timeout)
|
|
check_phase(PHASES.access)
|
|
|
|
if type(connect_timeout) ~= "number" or floor(connect_timeout) ~= connect_timeout then
|
|
error("connect_timeout must be an integer", 2)
|
|
end
|
|
if connect_timeout < 1 or connect_timeout > 2147483646 then
|
|
error("connect_timeout must be an integer between 1 and 2147483646: given " .. connect_timeout, 2)
|
|
end
|
|
|
|
if type(write_timeout) ~= "number" or floor(write_timeout) ~= write_timeout then
|
|
error("write_timeout must be an integer", 2)
|
|
end
|
|
if write_timeout < 1 or write_timeout > 2147483646 then
|
|
error("write_timeout must be an integer between 1 and 2147483646: given " .. write_timeout, 2)
|
|
end
|
|
|
|
if type(read_timeout) ~= "number" or floor(read_timeout) ~= read_timeout then
|
|
error("read_timeout must be an integer", 2)
|
|
end
|
|
if read_timeout < 1 or read_timeout > 2147483646 then
|
|
error("read_timeout must be an integer between 1 and 2147483646: given " .. read_timeout, 2)
|
|
end
|
|
|
|
local ctx = ngx.ctx
|
|
ctx.balancer_data.connect_timeout = connect_timeout
|
|
ctx.balancer_data.write_timeout = write_timeout
|
|
ctx.balancer_data.read_timeout = read_timeout
|
|
end
|
|
|
|
local tls = require("resty.kong.tls")
|
|
|
|
local set_upstream_cert_and_key = tls.set_upstream_cert_and_key
|
|
local set_upstream_ssl_verify = tls.set_upstream_ssl_verify
|
|
local set_upstream_ssl_verify_depth = tls.set_upstream_ssl_verify_depth
|
|
local set_upstream_ssl_trusted_store = tls.set_upstream_ssl_trusted_store
|
|
|
|
---
|
|
-- Sets the client certificate used while handshaking with the Service.
|
|
--
|
|
-- The `chain` argument is the client certificate and intermediate chain (if any)
|
|
-- returned by functions such as [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert).
|
|
--
|
|
-- The `key` argument is the private key corresponding to the client certificate
|
|
-- returned by functions such as [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key).
|
|
--
|
|
-- @function kong.service.set_tls_cert_key
|
|
-- @phases `rewrite`, `access`, `balancer`, `preread`
|
|
-- @tparam cdata chain The client certificate chain
|
|
-- @tparam cdata key The client certificate private key
|
|
-- @treturn boolean|nil `true` if the operation succeeded, `nil` if an error occurred
|
|
-- @treturn string|nil An error message describing the error if there was one
|
|
-- @usage
|
|
-- local chain = assert(ssl.parse_pem_cert(cert_data))
|
|
-- local key = assert(ssl.parse_pem_priv_key(key_data))
|
|
--
|
|
-- local ok, err = kong.service.set_tls_cert_key(chain, key)
|
|
-- if not ok then
|
|
-- -- do something with error
|
|
-- end
|
|
service.set_tls_cert_key = function(chain, key)
|
|
check_phase(access_and_rewrite_and_balancer_preread)
|
|
|
|
if type(chain) ~= "cdata" then
|
|
error("chain must be a parsed cdata object", 2)
|
|
end
|
|
|
|
if type(key) ~= "cdata" then
|
|
error("key must be a parsed cdata object", 2)
|
|
end
|
|
|
|
local res, err = set_upstream_cert_and_key(chain, key)
|
|
return res, err
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets whether TLS verification is enabled while handshaking with the Service.
|
|
--
|
|
-- The `on` argument is a boolean flag, where `true` means upstream verification
|
|
-- is enabled and `false` disables it.
|
|
--
|
|
-- This call affects only the current request. If the trusted certificate store is
|
|
-- not set already (via [proxy_ssl_trusted_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_trusted_certificate)
|
|
-- or [kong.service.set_upstream_ssl_trusted_store](#kongserviceset_upstream_ssl_trusted_store)),
|
|
-- then TLS verification will always fail with "unable to get local issuer certificate" error.
|
|
--
|
|
-- @function kong.service.set_tls_verify
|
|
-- @phases `rewrite`, `access`, `balancer`, `preread`
|
|
-- @tparam boolean on Whether to enable TLS certificate verification for the current request
|
|
-- @treturn boolean|nil `true` if the operation succeeded, `nil` if an error occurred
|
|
-- @treturn string|nil An error message describing the error if there was one
|
|
-- @usage
|
|
-- local ok, err = kong.service.set_tls_verify(true)
|
|
-- if not ok then
|
|
-- -- do something with error
|
|
-- end
|
|
service.set_tls_verify = function(on)
|
|
check_phase(access_and_rewrite_and_balancer_preread)
|
|
|
|
if type(on) ~= "boolean" then
|
|
error("argument must be a boolean", 2)
|
|
end
|
|
|
|
return set_upstream_ssl_verify(on)
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets the maximum depth of verification when validating upstream server's TLS certificate.
|
|
--
|
|
-- This call affects only the current request. For the depth to be actually used the verification
|
|
-- has to be enabled with either the [proxy_ssl_verify](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_verify)
|
|
-- directive or using the [kong.service.set_tls_verify](#kongserviceset_tls_verify) function.
|
|
--
|
|
-- @function kong.service.set_tls_verify_depth
|
|
-- @phases `rewrite`, `access`, `balancer`, `preread`
|
|
-- @tparam number depth Depth to use when validating. Must be non-negative
|
|
-- @treturn boolean|nil `true` if the operation succeeded, `nil` if an error occurred
|
|
-- @treturn string|nil An error message describing the error if there was one
|
|
-- @usage
|
|
-- local ok, err = kong.service.set_tls_verify_depth(3)
|
|
-- if not ok then
|
|
-- -- do something with error
|
|
-- end
|
|
service.set_tls_verify_depth = function(depth)
|
|
check_phase(access_and_rewrite_and_balancer_preread)
|
|
|
|
if type(depth) ~= "number" then
|
|
error("argument must be a number", 2)
|
|
end
|
|
|
|
return set_upstream_ssl_verify_depth(depth)
|
|
end
|
|
|
|
|
|
---
|
|
-- Sets the CA trust store to use when validating upstream server's TLS certificate.
|
|
--
|
|
-- This call affects only the current request. For the store to be actually used the verification
|
|
-- has to be enabled with either the [proxy_ssl_verify](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_verify)
|
|
-- directive or using the [kong.service.set_tls_verify](#kongserviceset_tls_verify) function.
|
|
--
|
|
-- The resty.openssl.x509.store object can be created by following
|
|
-- [examples](https://github.com/Kong/lua-kong-nginx-module#restykongtlsset_upstream_ssl_trusted_store) from the Kong/lua-kong-nginx-module repo.
|
|
--
|
|
-- @function kong.service.set_tls_verify_store
|
|
-- @phases `rewrite`, `access`, `balancer`, `preread`
|
|
-- @tparam table store resty.openssl.x509.store object to use
|
|
-- @treturn boolean|nil `true` if the operation succeeded, `nil` if an error occurred
|
|
-- @treturn string|nil An error message describing the error if there was one
|
|
-- @usage
|
|
-- local store = require("resty.openssl.x509.store")
|
|
-- local st = assert(store.new())
|
|
-- -- st:add(...certificate)
|
|
--
|
|
-- local ok, err = kong.service.set_tls_verify_store(st)
|
|
-- if not ok then
|
|
-- -- do something with error
|
|
-- end
|
|
service.set_tls_verify_store = function(store)
|
|
check_phase(access_and_rewrite_and_balancer_preread)
|
|
|
|
if type(store) ~= "table" then
|
|
error("argument must be a resty.openssl.x509.store object", 2)
|
|
end
|
|
|
|
return set_upstream_ssl_trusted_store(store)
|
|
end
|
|
|
|
|
|
return service
|
|
end
|
|
|
|
|
|
return {
|
|
new = new,
|
|
}
|