Files
2026-07-13 12:32:21 +08:00

176 lines
5.9 KiB
Lua

---
-- Client TLS connection module.
--
-- A set of functions for interacting with TLS connections from the client.
--
-- @module kong.client.tls
local phase_checker = require "kong.pdk.private.phases"
local kong_tls = require "resty.kong.tls"
local ngx_ssl = require "ngx.ssl"
local check_phase = phase_checker.check
local error = error
local type = type
local ngx = ngx
local PHASES = phase_checker.phases
local REWRITE_AND_LATER = phase_checker.new(PHASES.rewrite,
PHASES.access,
PHASES.response,
PHASES.balancer,
PHASES.log)
local REWRITE_BEFORE_LOG = phase_checker.new(PHASES.rewrite,
PHASES.access,
PHASES.response,
PHASES.balancer)
local function new()
local _TLS = {}
---
-- Requests the client to present its client-side certificate to initiate mutual
-- TLS authentication between server and client.
--
-- This function *requests*, but does not *require* the client to start
-- the mTLS process. The TLS handshake can still complete even if the client
-- doesn't present a client certificate. However, in that case, it becomes a
-- TLS connection instead of an mTLS connection, as there is no mutual
-- authentication.
--
-- To find out whether the client honored the request, use
-- `get_full_client_certificate_chain` in later phases.
--
-- The `ca_certs` argument is the optional CA certificate chain opaque pointer,
-- which can be created by the [parse_pem_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert)
-- or [resty.opensslx509.chain](https://github.com/fffonion/lua-resty-openssl#restyopensslx509chain)
-- The Distinguished Name (DN) list hints of the CA certificates will be sent to clients.
-- If omitted, will not send any DN list to clients.
--
-- @function kong.client.tls.request_client_certificate
-- @phases certificate
-- @tparam[opt] cdata ca_certs The CA certificate chain opaque pointer
-- @treturn true|nil Returns `true` if successful, or `nil` if it fails.
-- @treturn nil|err Returns `nil` if successful, or an error message if it fails.
--
-- @usage
-- local x509_lib = require "resty.openssl.x509"
-- local chain_lib = require "resty.openssl.x509.chain"
-- local res, err
-- local chain = chain_lib.new()
-- -- err check
-- local x509, err = x509_lib.new(pem_cert, "PEM")
-- -- err check
-- res, err = chain:add(x509)
-- -- err check
-- -- `chain.ctx` is the raw data of the chain, i.e. `STACK_OF(X509) *`
-- res, err = kong.client.tls.request_client_certificate(chain.ctx)
-- if not res then
-- -- do something with err
-- end
function _TLS.request_client_certificate(ca_certs)
check_phase(PHASES.certificate)
-- We don't care about the verification result during TLS handshake,
-- thus set `depth` to a minimum default value here in order to save CPU cycles
return ngx_ssl.verify_client(ca_certs, 0)
end
---
-- Prevents the TLS session for the current connection from being reused
-- by disabling the session ticket and session ID for the current TLS connection.
--
-- @function kong.client.tls.disable_session_reuse
-- @phases certificate
-- @treturn true|nil Returns `true` if successful, `nil` if it fails.
-- @treturn nil|err Returns `nil` if successful, or an error message if it fails.
--
-- @usage
-- local res, err = kong.client.tls.disable_session_reuse()
-- if not res then
-- -- do something with err
-- end
function _TLS.disable_session_reuse()
check_phase(PHASES.certificate)
return kong_tls.disable_session_reuse()
end
---
-- Returns the PEM encoded downstream client certificate chain with the
-- client certificate at the top and intermediate certificates
-- (if any) at the bottom.
--
-- @function kong.client.tls.get_full_client_certificate_chain
-- @phases rewrite, access, balancer, header_filter, body_filter, log
-- @treturn string|nil Returns a PEM-encoded client certificate if the mTLS
-- handshake was completed, or `nil` if an error occurred or the client did
-- not present its certificate.
-- @treturn nil|err Returns `nil` if successful, or an error message if it fails.
--
-- @usage
-- local cert, err = kong.client.tls.get_full_client_certificate_chain()
-- if err then
-- -- do something with err
-- end
--
-- if not cert then
-- -- client did not complete mTLS
-- end
--
-- -- do something with cert
function _TLS.get_full_client_certificate_chain()
check_phase(REWRITE_AND_LATER)
return kong_tls.get_full_client_certificate_chain()
end
---
-- Overrides the client's verification result generated by the log serializer.
--
-- By default, the `request.tls.client_verify` field inside the log
-- generated by Kong's log serializer is the same as the
-- [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify)
-- Nginx variable.
--
-- Only `"SUCCESS"`, `"NONE"`, or `"FAILED:<reason>"` are accepted values.
--
-- This function does not return anything on success, and throws a Lua error
-- in case of a failure.
--
-- @function kong.client.tls.set_client_verify
-- @phases rewrite, access, balancer
--
-- @usage
-- kong.client.tls.set_client_verify("FAILED:unknown CA")
function _TLS.set_client_verify(v)
check_phase(REWRITE_BEFORE_LOG)
assert(type(v) == "string")
if v ~= "SUCCESS" and v ~= "NONE" and v:sub(1, 7) ~= "FAILED:" then
error("unknown client verify value: " .. tostring(v) ..
" accepted values are: \"SUCCESS\", \"NONE\"" ..
" or \"FAILED:<reason>\"", 2)
end
ngx.ctx.CLIENT_VERIFY_OVERRIDE = v
end
return _TLS
end
return {
new = new,
}