diff --git a/README.md b/README.md index e131e521..aab81817 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ resty.kong.tls.disable\_session\_reuse **context:** *ssl_certificate_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Prevents the TLS session for the current connection from being reused by disabling session ticket and session ID for the current TLS connection. @@ -162,9 +162,9 @@ resty.kong.tls.get\_full\_client\_certificate\_chain ---------------------------------------------------- **syntax:** *pem_chain, err = resty.kong.tls.get\_full\_client\_certificate\_chain()* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua**, *preread_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Returns the PEM encoded downstream client certificate chain with the client certificate at the top and intermediate certificates (if any) at the bottom. @@ -192,9 +192,9 @@ resty.kong.tls.set\_upstream\_cert\_and\_key -------------------------------------------- **syntax:** *ok, err = resty.kong.tls.set\_upstream\_cert\_and\_key(chain, key)* -**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua**, *preread_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Overrides and enables sending client certificate while connecting to the upstream in the current request. @@ -218,9 +218,9 @@ resty.kong.tls.set\_upstream\_ssl\_trusted\_store ------------------------------------------------- **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_trusted\_store(store)* -**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua**, *preread_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Set upstream ssl verification trusted store of current request. Global setting set by `proxy_ssl_trusted_certificate` will be overwritten for the current request. @@ -280,9 +280,9 @@ resty.kong.tls.set\_upstream\_ssl\_verify ----------------------------------------- **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_verify(verify)* -**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua**, *preread_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Set upstream ssl verification enablement of current request to the given boolean argument `verify`. Global setting set by `proxy_ssl_verify` will be overwritten. @@ -299,9 +299,9 @@ resty.kong.tls.set\_upstream\_ssl\_verify\_depth ------------------------------------------------ **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_verify\_depth(depth)* -**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua**, *preread_by_lua** -**subsystems:** *http* +**subsystems:** *http* *stream* Set upstream ssl verification depth of current request to the given non-negative integer argument `depth`. Global setting set by `proxy_ssl_verify_depth` will be overwritten. diff --git a/config b/config index b5cadc8a..aa2d484e 100644 --- a/config +++ b/config @@ -8,6 +8,7 @@ ngx_module_srcs=" \ $ngx_addon_dir/src/ngx_http_lua_kong_tag.c \ $ngx_addon_dir/src/ngx_http_lua_kong_module.c \ $ngx_addon_dir/src/ngx_http_lua_kong_log.c \ + $ngx_addon_dir/src/ssl/ngx_lua_kong_ssl.c \ " ngx_module_incs="$ngx_addon_dir/src" diff --git a/lualib/resty/kong/tls.lua b/lualib/resty/kong/tls.lua index 60e9360c..c56b0085 100644 --- a/lualib/resty/kong/tls.lua +++ b/lualib/resty/kong/tls.lua @@ -18,10 +18,27 @@ local _M = {} local ffi = require("ffi") local base = require("resty.core.base") + +local get_phase = ngx.get_phase +local type = type +local error = error +local tostring = tostring +local C = ffi.C +local ffi_string = ffi.string +local get_string_buf = base.get_string_buf +local size_ptr = base.get_size_ptr() +local orig_get_request = base.get_request +local subsystem = ngx.config.subsystem base.allows_subsystem('http', 'stream') +local kong_lua_kong_ffi_get_full_client_certificate_chain +local kong_lua_kong_ffi_disable_session_reuse +local kong_lua_kong_ffi_set_upstream_client_cert_and_key +local kong_lua_kong_ffi_set_upstream_ssl_trusted_store +local kong_lua_kong_ffi_set_upstream_ssl_verify +local kong_lua_kong_ffi_set_upstream_ssl_verify_depth -if ngx.config.subsystem == "http" then +if subsystem == "http" then ffi.cdef([[ int ngx_http_lua_kong_ffi_get_full_client_certificate_chain( ngx_http_request_t *r, char *buf, size_t *buf_len); @@ -36,23 +53,38 @@ if ngx.config.subsystem == "http" then int depth); ]]) -else + kong_lua_kong_ffi_get_full_client_certificate_chain = C.ngx_http_lua_kong_ffi_get_full_client_certificate_chain + kong_lua_kong_ffi_disable_session_reuse = C.ngx_http_lua_kong_ffi_disable_session_reuse + kong_lua_kong_ffi_set_upstream_client_cert_and_key = C.ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key + kong_lua_kong_ffi_set_upstream_ssl_trusted_store = C.ngx_http_lua_kong_ffi_set_upstream_ssl_trusted_store + kong_lua_kong_ffi_set_upstream_ssl_verify = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify + kong_lua_kong_ffi_set_upstream_ssl_verify_depth = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify_depth + +elseif subsystem == 'stream' then ffi.cdef([[ - int - ngx_stream_lua_kong_ffi_proxy_ssl_disable(ngx_stream_lua_request_t *r); + int ngx_stream_lua_kong_ffi_proxy_ssl_disable(ngx_stream_lua_request_t *r); + int ngx_stream_lua_kong_ffi_get_full_client_certificate_chain(ngx_stream_lua_request_t *r, + char *buf, size_t *buf_len); + const char *ngx_stream_lua_kong_ffi_disable_session_reuse(ngx_stream_lua_request_t *r); + int ngx_stream_lua_kong_ffi_set_upstream_client_cert_and_key(ngx_stream_lua_request_t *r, + void *_chain, void *_key); + int ngx_stream_lua_kong_ffi_set_upstream_ssl_trusted_store(ngx_stream_lua_request_t *r, + void *_store); + int ngx_stream_lua_kong_ffi_set_upstream_ssl_verify(ngx_stream_lua_request_t *r, + int verify); + int ngx_stream_lua_kong_ffi_set_upstream_ssl_verify_depth(ngx_stream_lua_request_t *r, + int depth); ]]) -end - -local get_phase = ngx.get_phase -local type = type -local error = error -local tostring = tostring -local C = ffi.C -local ffi_string = ffi.string -local get_string_buf = base.get_string_buf -local size_ptr = base.get_size_ptr() -local orig_get_request = base.get_request + kong_lua_kong_ffi_get_full_client_certificate_chain = C.ngx_stream_lua_kong_ffi_get_full_client_certificate_chain + kong_lua_kong_ffi_disable_session_reuse = C.ngx_stream_lua_kong_ffi_disable_session_reuse + kong_lua_kong_ffi_set_upstream_client_cert_and_key = C.ngx_stream_lua_kong_ffi_set_upstream_client_cert_and_key + kong_lua_kong_ffi_set_upstream_ssl_trusted_store = C.ngx_stream_lua_kong_ffi_set_upstream_ssl_trusted_store + kong_lua_kong_ffi_set_upstream_ssl_verify = C.ngx_stream_lua_kong_ffi_set_upstream_ssl_verify + kong_lua_kong_ffi_set_upstream_ssl_verify_depth = C.ngx_stream_lua_kong_ffi_set_upstream_ssl_verify_depth +else + error("unknown subsystem: " .. subsystem) +end local DEFAULT_CERT_CHAIN_SIZE = 10240 @@ -73,187 +105,188 @@ local function get_request() return r end -if ngx.config.subsystem == "http" then - function _M.disable_session_reuse() - if get_phase() ~= 'ssl_cert' then - error("API disabled in the current context") +function _M.disable_session_reuse() + if get_phase() ~= 'ssl_cert' then + error("API disabled in the current context") + end + + local r = get_request() + + local errmsg = kong_lua_kong_ffi_disable_session_reuse(r) + if errmsg == nil then + return true + end + + return nil, ffi_string(errmsg) +end + + +do + local ALLOWED_PHASES = { + ['rewrite'] = true, + ['balancer'] = true, + ['access'] = true, + ['content'] = true, + ['log'] = true, + ['preread'] = true, + } + + function _M.get_full_client_certificate_chain() + if not ALLOWED_PHASES[get_phase()] then + error("API disabled in the current context", 2) end local r = get_request() - local errmsg = C.ngx_http_lua_kong_ffi_disable_session_reuse(r) - if errmsg == nil then - return true - end + size_ptr[0] = DEFAULT_CERT_CHAIN_SIZE - return nil, ffi_string(errmsg) - end +::again:: + local buf = get_string_buf(size_ptr[0]) - do - local ALLOWED_PHASES = { - ['rewrite'] = true, - ['balancer'] = true, - ['access'] = true, - ['content'] = true, - ['log'] = true, - } + local ret = kong_lua_kong_ffi_get_full_client_certificate_chain( + r, buf, size_ptr) + if ret == NGX_OK then + return ffi_string(buf, size_ptr[0]) + end - function _M.get_full_client_certificate_chain() - if not ALLOWED_PHASES[get_phase()] then - error("API disabled in the current context", 2) - end + if ret == NGX_ERROR then + return nil, "error while obtaining client certificate chain" + end - local r = get_request() + if ret == NGX_ABORT then + return nil, + "connection is not TLS or TLS support for Nginx not enabled" + end - size_ptr[0] = DEFAULT_CERT_CHAIN_SIZE + if ret == NGX_DECLINED then + return nil + end - ::again:: + if ret == NGX_AGAIN then + goto again + end - local buf = get_string_buf(size_ptr[0]) + error("unknown return code: " .. tostring(ret)) + end +end - local ret = C.ngx_http_lua_kong_ffi_get_full_client_certificate_chain( - r, buf, size_ptr) - if ret == NGX_OK then - return ffi_string(buf, size_ptr[0]) - end - if ret == NGX_ERROR then - return nil, "error while obtaining client certificate chain" - end +do + local ALLOWED_PHASES = { + ['rewrite'] = true, + ['balancer'] = true, + ['access'] = true, + ['preread'] = true, + } - if ret == NGX_ABORT then - return nil, - "connection is not TLS or TLS support for Nginx not enabled" - end + function _M.set_upstream_cert_and_key(chain, key) + if not ALLOWED_PHASES[get_phase()] then + error("API disabled in the current context", 2) + end - if ret == NGX_DECLINED then - return nil - end + if not chain or not key then + error("chain and key must not be nil", 2) + end - if ret == NGX_AGAIN then - goto again - end + local r = get_request() - error("unknown return code: " .. tostring(ret)) + local ret = kong_lua_kong_ffi_set_upstream_client_cert_and_key( + r, chain, key) + if ret == NGX_OK then + return true + end + + if ret == NGX_ERROR then + return nil, "error while setting upstream client cert and key" end - end + error("unknown return code: " .. tostring(ret)) + end do - local ALLOWED_PHASES = { - ['rewrite'] = true, - ['balancer'] = true, - ['access'] = true, - } + local store_lib = require("resty.openssl.x509.store") - function _M.set_upstream_cert_and_key(chain, key) + function _M.set_upstream_ssl_trusted_store(store) if not ALLOWED_PHASES[get_phase()] then error("API disabled in the current context", 2) end - if not chain or not key then - error("chain and key must not be nil", 2) + if not store_lib.istype(store) then + error("store expects a resty.openssl.x509.store" .. + " object but found " .. type(store), 2) end local r = get_request() - local ret = C.ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key( - r, chain, key) + local ret = kong_lua_kong_ffi_set_upstream_ssl_trusted_store( + r, store.ctx) if ret == NGX_OK then return true end if ret == NGX_ERROR then - return nil, "error while setting upstream client cert and key" + return nil, "error while setting upstream trusted store" end error("unknown return code: " .. tostring(ret)) end + end - do - local store_lib = require("resty.openssl.x509.store") - - function _M.set_upstream_ssl_trusted_store(store) - if not ALLOWED_PHASES[get_phase()] then - error("API disabled in the current context", 2) - end - - if not store_lib.istype(store) then - error("store expects a resty.openssl.x509.store" .. - " object but found " .. type(store), 2) - end - - local r = get_request() - - local ret = C.ngx_http_lua_kong_ffi_set_upstream_ssl_trusted_store( - r, store.ctx) - if ret == NGX_OK then - return true - end - - if ret == NGX_ERROR then - return nil, "error while setting upstream trusted store" - end - - error("unknown return code: " .. tostring(ret)) - end + function _M.set_upstream_ssl_verify(verify) + if not ALLOWED_PHASES[get_phase()] then + error("API disabled in the current context", 2) end - function _M.set_upstream_ssl_verify(verify) - if not ALLOWED_PHASES[get_phase()] then - error("API disabled in the current context", 2) - end - - if type(verify) ~= 'boolean' then - error("verify expects a boolean but found " .. type(verify), 2) - end - - local r = get_request() + if type(verify) ~= 'boolean' then + error("verify expects a boolean but found " .. type(verify), 2) + end - local ret = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify( - r, verify) - if ret == NGX_OK then - return true - end + local r = get_request() - if ret == NGX_ERROR then - return nil, "error while setting upstream ssl verify mode" - end + local ret = kong_lua_kong_ffi_set_upstream_ssl_verify( + r, verify) + if ret == NGX_OK then + return true + end - error("unknown return code: " .. tostring(ret)) + if ret == NGX_ERROR then + return nil, "error while setting upstream ssl verify mode" end - function _M.set_upstream_ssl_verify_depth(depth) - if not ALLOWED_PHASES[get_phase()] then - error("API disabled in the current context", 2) - end + error("unknown return code: " .. tostring(ret)) + end - if type(depth) ~= 'number' then - error("depth expects a number but found " .. type(depth), 2) - end + function _M.set_upstream_ssl_verify_depth(depth) + if not ALLOWED_PHASES[get_phase()] then + error("API disabled in the current context", 2) + end - if depth < 0 then - error("depth expects a non-negative integer but found " .. tostring(depth), 2) - end + if type(depth) ~= 'number' then + error("depth expects a number but found " .. type(depth), 2) + end - local r = get_request() + if depth < 0 then + error("depth expects a non-negative integer but found " .. tostring(depth), 2) + end - local ret = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify_depth( - r, depth) - if ret == NGX_OK then - return true - end + local r = get_request() - if ret == NGX_ERROR then - return nil, "error while setting upstream ssl verify depth" - end + local ret = kong_lua_kong_ffi_set_upstream_ssl_verify_depth( + r, depth) + if ret == NGX_OK then + return true + end - error("unknown return code: " .. tostring(ret)) + if ret == NGX_ERROR then + return nil, "error while setting upstream ssl verify depth" end + + error("unknown return code: " .. tostring(ret)) end +end -else -- stream +if ngx.config.subsystem == "stream" then do local ALLOWED_PHASES = { ['preread'] = true, diff --git a/src/ngx_http_lua_kong_common.h b/src/ngx_http_lua_kong_common.h index f4ebb638..96f2974b 100644 --- a/src/ngx_http_lua_kong_common.h +++ b/src/ngx_http_lua_kong_common.h @@ -22,17 +22,11 @@ #include #include #include - +#include "ssl/ngx_lua_kong_ssl.h" typedef struct { - STACK_OF(X509) *upstream_client_certificate_chain; - EVP_PKEY *upstream_client_private_key; - X509_STORE *upstream_trusted_store; - ngx_uint_t upstream_ssl_verify_depth; - ngx_str_t grpc_authority; - unsigned upstream_ssl_verify:1; - unsigned upstream_ssl_verify_set:1; - unsigned upstream_ssl_verify_depth_set:1; + ngx_lua_kong_ssl_ctx_t ssl_ctx; + ngx_str_t grpc_authority; } ngx_http_lua_kong_ctx_t; diff --git a/src/ngx_http_lua_kong_module.c b/src/ngx_http_lua_kong_module.c index bcb4f693..085bc498 100644 --- a/src/ngx_http_lua_kong_module.c +++ b/src/ngx_http_lua_kong_module.c @@ -16,7 +16,6 @@ #include "ngx_http_lua_kong_directive.h" -#include "ngx_http_lua_kong_ssl.h" static ngx_int_t ngx_http_lua_kong_init(ngx_conf_t *cf); @@ -77,7 +76,7 @@ ngx_module_t ngx_http_lua_kong_module = { static ngx_int_t ngx_http_lua_kong_init(ngx_conf_t *cf) { - return ngx_http_lua_kong_ssl_init(cf); + return ngx_lua_kong_ssl_init(cf); } @@ -86,9 +85,7 @@ ngx_http_lua_kong_cleanup(void *data) { ngx_http_lua_kong_ctx_t *ctx = data; - ngx_http_lua_kong_cleanup_cert_and_key(ctx); - - ngx_http_lua_kong_cleanup_trusted_store(ctx); + ngx_lua_kong_ssl_cleanup(&ctx->ssl_ctx); } diff --git a/src/ngx_http_lua_kong_ssl.c b/src/ngx_http_lua_kong_ssl.c index d97369d7..daf263ab 100644 --- a/src/ngx_http_lua_kong_ssl.c +++ b/src/ngx_http_lua_kong_ssl.c @@ -18,63 +18,6 @@ #include "ngx_http_lua_kong_common.h" -#if (NGX_SSL) -static int ngx_http_lua_kong_ssl_old_sess_new_cb_index = -1; -static int ngx_http_lua_kong_ssl_no_session_cache_flag_index = -1; -#endif - -ngx_int_t -ngx_http_lua_kong_ssl_init(ngx_conf_t *cf) -{ -#if (NGX_SSL) - if (ngx_http_lua_kong_ssl_old_sess_new_cb_index == -1) { - ngx_http_lua_kong_ssl_old_sess_new_cb_index = - SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); - - if (ngx_http_lua_kong_ssl_old_sess_new_cb_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, cf->log, 0, - "kong: SSL_CTX_get_ex_new_index() for " - "ssl ctx failed"); - return NGX_ERROR; - } - } - - if (ngx_http_lua_kong_ssl_no_session_cache_flag_index == -1) { - ngx_http_lua_kong_ssl_no_session_cache_flag_index = - SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - - if (ngx_http_lua_kong_ssl_no_session_cache_flag_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, cf->log, 0, - "kong: SSL_get_ex_new_index() for ssl failed"); - return NGX_ERROR; - } - } -#endif - - return NGX_OK; -} - -#if (NGX_SSL) -static int -ngx_http_lua_kong_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) -{ - ngx_uint_t flag; - - flag = (ngx_uint_t) SSL_get_ex_data(ssl_conn, - ngx_http_lua_kong_ssl_no_session_cache_flag_index); - - if (flag) { - return 0; - } - - return ((int (*)(SSL *ssl, SSL_SESSION *sess)) - SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn), - ngx_http_lua_kong_ssl_old_sess_new_cb_index))(ssl_conn, - sess); -} -#endif - - /* * disables session reuse for the current TLS connection, must be called * in ssl_certby_lua* phase @@ -84,53 +27,7 @@ const char * ngx_http_lua_kong_ffi_disable_session_reuse(ngx_http_request_t *r) { #if (NGX_SSL) - ngx_uint_t flag; - ngx_connection_t *c = r->connection; - ngx_ssl_conn_t *sc; - SSL_CTX *ctx; - - if (c->ssl == NULL) { - return "server does not have TLS enabled"; - } - - sc = c->ssl->connection; - - /* the following disables session ticket for the current connection */ - SSL_set_options(sc, SSL_OP_NO_TICKET); - - /* the following disables session cache for the current connection - * note that we are using the pointer storage to store a flag value to - * avoid having to do memory allocations. since the pointer is never - * dereferenced this is completely safe to do */ - flag = 1; - - if (SSL_set_ex_data(sc, - ngx_http_lua_kong_ssl_no_session_cache_flag_index, - (void *) flag) == 0) - { - return "unable to disable session cache for current connection"; - } - - ctx = c->ssl->session_ctx; - - /* hook session_new_cb if not already done so */ - if (SSL_CTX_sess_get_new_cb(ctx) != - ngx_http_lua_kong_new_session) - { - /* save old callback */ - if (SSL_CTX_set_ex_data(ctx, - ngx_http_lua_kong_ssl_old_sess_new_cb_index, - SSL_CTX_sess_get_new_cb(ctx)) == 0) - { - return "unable to install new session hook"; - } - - /* install hook */ - SSL_CTX_sess_set_new_cb(ctx, ngx_http_lua_kong_new_session); - } - - return NULL; - + return ngx_lua_kong_ssl_disable_session_reuse(r->connection); #else return "TLS support is not enabled in Nginx build"; #endif @@ -142,84 +39,7 @@ ngx_http_lua_kong_ffi_get_full_client_certificate_chain(ngx_http_request_t *r, char *buf, size_t *buf_len) { #if (NGX_SSL) - ngx_connection_t *c = r->connection; - ngx_ssl_conn_t *sc; - STACK_OF(X509) *chain; - X509 *cert; - int i, n; - size_t len; - BIO *bio; - int ret; - - if (c->ssl == NULL) { - return NGX_ABORT; - } - - sc = c->ssl->connection; - - cert = SSL_get_peer_certificate(c->ssl->connection); - if (cert == NULL) { - /* client did not present a certificate or server did not request it */ - return NGX_DECLINED; - } - - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); - - X509_free(cert); - ret = NGX_ERROR; - goto done; - } - - if (PEM_write_bio_X509(bio, cert) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed"); - - X509_free(cert); - ret = NGX_ERROR; - goto done; - } - - X509_free(cert); - - chain = SSL_get_peer_cert_chain(sc); - if (chain == NULL) { - ret = NGX_DECLINED; - goto done; - } - - n = sk_X509_num(chain); - for (i = 0; i < n; i++) { - cert = sk_X509_value(chain, i); - - if (PEM_write_bio_X509(bio, cert) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "PEM_write_bio_X509() failed"); - - ret = NGX_ERROR; - goto done; - } - } - - len = BIO_pending(bio); - if (len > *buf_len) { - *buf_len = len; - - ret = NGX_AGAIN; - goto done; - } - - BIO_read(bio, buf, len); - *buf_len = len; - - ret = NGX_OK; - -done: - - BIO_free(bio); - - return ret; - + return ngx_lua_kong_ssl_get_full_client_certificate_chain(r->connection, buf, buf_len); #else return NGX_ABORT; #endif @@ -239,17 +59,7 @@ ngx_http_lua_kong_ffi_get_full_client_certificate_chain(ngx_http_request_t *r, void ngx_http_lua_kong_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c) { - ngx_ssl_conn_t *sc = c->ssl->connection; ngx_http_lua_kong_ctx_t *ctx; - STACK_OF(X509) *chain; - EVP_PKEY *pkey; - X509 *x509; - X509_STORE *store; -#ifdef OPENSSL_IS_BORINGSSL - size_t i; -#else - int i; -#endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_kong_module); @@ -260,106 +70,7 @@ ngx_http_lua_kong_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c) return; } - if (ctx->upstream_client_certificate_chain != NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "overriding upstream SSL client cert and key"); - - chain = ctx->upstream_client_certificate_chain; - pkey = ctx->upstream_client_private_key; - - if (sk_X509_num(chain) < 1) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "invalid client certificate chain provided while " - "handshaking with upstream"); - goto failed; - } - - x509 = sk_X509_value(chain, 0); - if (x509 == NULL) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "sk_X509_value() failed"); - goto failed; - } - - if (SSL_use_certificate(sc, x509) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "SSL_use_certificate() failed"); - goto failed; - } - - /* read rest of the chain */ - - for (i = 1; i < sk_X509_num(chain); i++) { - x509 = sk_X509_value(chain, i); - if (x509 == NULL) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "sk_X509_value() failed"); - goto failed; - } - - if (SSL_add1_chain_cert(sc, x509) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "SSL_add1_chain_cert() failed"); - goto failed; - } - } - - if (SSL_use_PrivateKey(sc, pkey) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "SSL_use_PrivateKey() failed"); - goto failed; - } - } - - if (ctx->upstream_trusted_store != NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "overriding upstream SSL trusted store"); - store = ctx->upstream_trusted_store; - - if (SSL_set1_verify_cert_store(sc, store) == 0) { - ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, - "SSL_set1_verify_cert_store() failed"); - goto failed; - } - } - - if (ctx->upstream_ssl_verify_depth_set) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "overriding upstream SSL verify depth"); - SSL_set_verify_depth(sc, ctx->upstream_ssl_verify_depth); - } - - - return; - -failed: - - ERR_clear_error(); -} - - -static X509 * -ngx_http_lua_kong_x509_copy(X509 *in) -{ - return X509_up_ref(in) == 0 ? NULL : in; -} - - -void -ngx_http_lua_kong_cleanup_cert_and_key(ngx_http_lua_kong_ctx_t *ctx) -{ - if (ctx->upstream_client_certificate_chain != NULL) { - sk_X509_pop_free(ctx->upstream_client_certificate_chain, X509_free); - EVP_PKEY_free(ctx->upstream_client_private_key); - } -} - - -void -ngx_http_lua_kong_cleanup_trusted_store(ngx_http_lua_kong_ctx_t *ctx) -{ - if (ctx->upstream_trusted_store != NULL) { - X509_STORE_free(ctx->upstream_trusted_store); - } + return ngx_lua_kong_ssl_set_upstream_ssl(&ctx->ssl_ctx, c); } @@ -367,48 +78,15 @@ int ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key(ngx_http_request_t *r, void *_chain, void *_key) { - STACK_OF(X509) *chain = _chain; - EVP_PKEY *key = _key; - STACK_OF(X509) *new_chain; ngx_http_lua_kong_ctx_t *ctx; - if (chain == NULL || key == NULL) { - return NGX_ERROR; - } - ctx = ngx_http_lua_kong_get_module_ctx(r); if (ctx == NULL) { return NGX_ERROR; - - } else if (ctx->upstream_client_certificate_chain != NULL) { - ngx_http_lua_kong_cleanup_cert_and_key(ctx); - - ctx->upstream_client_certificate_chain = NULL; - ctx->upstream_client_private_key = NULL; - } - - if (EVP_PKEY_up_ref(key) == 0) { - goto failed; } - new_chain = sk_X509_deep_copy(chain, ngx_http_lua_kong_x509_copy, - X509_free); - if (new_chain == NULL) { - EVP_PKEY_free(key); - goto failed; - } - - ctx->upstream_client_certificate_chain = new_chain; - ctx->upstream_client_private_key = key; - - return NGX_OK; - -failed: - - ERR_clear_error(); - - return NGX_ERROR; + return ngx_lua_kong_ssl_set_upstream_client_cert_and_key(&ctx->ssl_ctx, _chain, _key); } @@ -416,36 +94,15 @@ int ngx_http_lua_kong_ffi_set_upstream_ssl_trusted_store(ngx_http_request_t *r, void *_store) { - X509_STORE *store = _store; ngx_http_lua_kong_ctx_t *ctx; - if (store == NULL) { - return NGX_ERROR; - } - ctx = ngx_http_lua_kong_get_module_ctx(r); + if (ctx == NULL) { return NGX_ERROR; - - } else if (ctx->upstream_trusted_store != NULL) { - ngx_http_lua_kong_cleanup_trusted_store(ctx); - - ctx->upstream_trusted_store = NULL; - } - - if (X509_STORE_up_ref(store) == 0) { - goto failed; } - ctx->upstream_trusted_store = store; - - return NGX_OK; - -failed: - - ERR_clear_error(); - - return NGX_ERROR; + return ngx_lua_kong_ssl_set_upstream_ssl_trusted_store(&ctx->ssl_ctx,_store); } @@ -460,10 +117,7 @@ ngx_http_lua_kong_ffi_set_upstream_ssl_verify(ngx_http_request_t *r, return NGX_ERROR; } - ctx->upstream_ssl_verify_set = 1; - ctx->upstream_ssl_verify = verify; - - return NGX_OK; + return ngx_lua_kong_ssl_set_upstream_ssl_verify(&ctx->ssl_ctx, verify); } @@ -478,10 +132,7 @@ ngx_http_lua_kong_ffi_set_upstream_ssl_verify_depth(ngx_http_request_t *r, return NGX_ERROR; } - ctx->upstream_ssl_verify_depth_set = 1; - ctx->upstream_ssl_verify_depth = depth; - - return NGX_OK; + return ngx_lua_kong_ssl_set_upstream_ssl_verify_depth(&ctx->ssl_ctx, depth); } @@ -493,15 +144,11 @@ ngx_http_lua_kong_get_upstream_ssl_verify(ngx_http_request_t *r, ctx = ngx_http_get_module_ctx(r, ngx_http_lua_kong_module); - /* - * if upstream_ssl_verify is not set, - * use the default Nginx proxy_ssl_verify value - */ - if (ctx == NULL || !ctx->upstream_ssl_verify_set) { + if (ctx == NULL) { return proxy_ssl_verify; } - return ctx->upstream_ssl_verify; + return ngx_lua_kong_ssl_get_upstream_ssl_verify(&ctx->ssl_ctx, proxy_ssl_verify); } diff --git a/src/ssl/ngx_lua_kong_ssl.c b/src/ssl/ngx_lua_kong_ssl.c new file mode 100644 index 00000000..932727f8 --- /dev/null +++ b/src/ssl/ngx_lua_kong_ssl.c @@ -0,0 +1,458 @@ +/** + * Copyright 2019-2022 Kong Inc. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "ngx_lua_kong_ssl.h" + +#if (NGX_SSL) +static int ngx_lua_kong_ssl_old_sess_new_cb_index = -1; +static int ngx_lua_kong_ssl_no_session_cache_flag_index = -1; +#endif + +ngx_int_t +ngx_lua_kong_ssl_init(ngx_conf_t *cf) +{ +#if (NGX_SSL) + if (ngx_lua_kong_ssl_old_sess_new_cb_index == -1) { + ngx_lua_kong_ssl_old_sess_new_cb_index = + SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_lua_kong_ssl_old_sess_new_cb_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, cf->log, 0, + "kong: SSL_CTX_get_ex_new_index() for " + "ssl ctx failed"); + return NGX_ERROR; + } + } + + if (ngx_lua_kong_ssl_no_session_cache_flag_index == -1) { + ngx_lua_kong_ssl_no_session_cache_flag_index = + SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_lua_kong_ssl_no_session_cache_flag_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, cf->log, 0, + "kong: SSL_get_ex_new_index() for ssl failed"); + return NGX_ERROR; + } + } +#endif + + return NGX_OK; +} + + +#if (NGX_SSL) + + +static int +ngx_lua_kong_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) +{ + ngx_uint_t flag; + + flag = (ngx_uint_t) SSL_get_ex_data(ssl_conn, + ngx_lua_kong_ssl_no_session_cache_flag_index); + + if (flag) { + return 0; + } + + return ((int (*)(SSL *ssl, SSL_SESSION *sess)) + SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn), + ngx_lua_kong_ssl_old_sess_new_cb_index))(ssl_conn, + sess); +} + + +const char * +ngx_lua_kong_ssl_disable_session_reuse(ngx_connection_t *c) +{ + ngx_uint_t flag; + ngx_ssl_conn_t *sc; + SSL_CTX *ctx; + + if (c->ssl == NULL) { + return "server does not have TLS enabled"; + } + + sc = c->ssl->connection; + + /* the following disables session ticket for the current connection */ + SSL_set_options(sc, SSL_OP_NO_TICKET); + + /* the following disables session cache for the current connection + * note that we are using the pointer storage to store a flag value to + * avoid having to do memory allocations. since the pointer is never + * dereferenced this is completely safe to do */ + flag = 1; + + if (SSL_set_ex_data(sc, + ngx_lua_kong_ssl_no_session_cache_flag_index, + (void *) flag) == 0) + { + return "unable to disable session cache for current connection"; + } + + ctx = c->ssl->session_ctx; + + /* hook session_new_cb if not already done so */ + if (SSL_CTX_sess_get_new_cb(ctx) != + ngx_lua_kong_ssl_new_session) + { + /* save old callback */ + if (SSL_CTX_set_ex_data(ctx, + ngx_lua_kong_ssl_old_sess_new_cb_index, + SSL_CTX_sess_get_new_cb(ctx)) == 0) + { + return "unable to install new session hook"; + } + + /* install hook */ + SSL_CTX_sess_set_new_cb(ctx, ngx_lua_kong_ssl_new_session); + } + + return NULL; +} + + +int +ngx_lua_kong_ssl_get_full_client_certificate_chain(ngx_connection_t *c, + char *buf, size_t *buf_len) +{ + ngx_ssl_conn_t *sc; + STACK_OF(X509) *chain; + X509 *cert; + int i, n; + size_t len; + BIO *bio; + int ret; + + if (c->ssl == NULL) { + return NGX_ABORT; + } + + sc = c->ssl->connection; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + /* client did not present a certificate or server did not request it */ + return NGX_DECLINED; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); + + X509_free(cert); + ret = NGX_ERROR; + goto done; + } + + if (PEM_write_bio_X509(bio, cert) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed"); + + X509_free(cert); + ret = NGX_ERROR; + goto done; + } + + X509_free(cert); + + chain = SSL_get_peer_cert_chain(sc); + if (chain == NULL) { + ret = NGX_DECLINED; + goto done; + } + + n = sk_X509_num(chain); + for (i = 0; i < n; i++) { + cert = sk_X509_value(chain, i); + + if (PEM_write_bio_X509(bio, cert) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "PEM_write_bio_X509() failed"); + + ret = NGX_ERROR; + goto done; + } + } + + len = BIO_pending(bio); + if (len > *buf_len) { + *buf_len = len; + + ret = NGX_AGAIN; + goto done; + } + + BIO_read(bio, buf, len); + *buf_len = len; + + ret = NGX_OK; + +done: + + BIO_free(bio); + + return ret; +} + + +void +ngx_lua_kong_ssl_set_upstream_ssl(ngx_lua_kong_ssl_ctx_t *ctx, ngx_connection_t *c) +{ + ngx_ssl_conn_t *sc = c->ssl->connection; + STACK_OF(X509) *chain; + EVP_PKEY *pkey; + X509 *x509; + X509_STORE *store; +#ifdef OPENSSL_IS_BORINGSSL + size_t i; +#else + int i; +#endif + + + if (ctx->upstream_client_certificate_chain != NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "overriding upstream SSL client cert and key"); + + chain = ctx->upstream_client_certificate_chain; + pkey = ctx->upstream_client_private_key; + + if (sk_X509_num(chain) < 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "invalid client certificate chain provided while " + "handshaking with upstream"); + goto failed; + } + + x509 = sk_X509_value(chain, 0); + if (x509 == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "sk_X509_value() failed"); + goto failed; + } + + if (SSL_use_certificate(sc, x509) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "SSL_use_certificate() failed"); + goto failed; + } + + /* read rest of the chain */ + + for (i = 1; i < sk_X509_num(chain); i++) { + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "sk_X509_value() failed"); + goto failed; + } + + if (SSL_add1_chain_cert(sc, x509) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "SSL_add1_chain_cert() failed"); + goto failed; + } + } + + if (SSL_use_PrivateKey(sc, pkey) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "SSL_use_PrivateKey() failed"); + goto failed; + } + } + + if (ctx->upstream_trusted_store != NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "overriding upstream SSL trusted store"); + store = ctx->upstream_trusted_store; + + if (SSL_set1_verify_cert_store(sc, store) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "SSL_set1_verify_cert_store() failed"); + goto failed; + } + } + + if (ctx->upstream_ssl_verify_depth_set) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "overriding upstream SSL verify depth"); + SSL_set_verify_depth(sc, ctx->upstream_ssl_verify_depth); + } + + + return; + +failed: + + ERR_clear_error(); +} + + +static X509 * +ngx_lua_kong_ssl_x509_copy(X509 *in) +{ + return X509_up_ref(in) == 0 ? NULL : in; +} + + +void +ngx_lua_kong_ssl_cleanup_cert_and_key(ngx_lua_kong_ssl_ctx_t *ctx) +{ + if (ctx->upstream_client_certificate_chain != NULL) { + sk_X509_pop_free(ctx->upstream_client_certificate_chain, X509_free); + EVP_PKEY_free(ctx->upstream_client_private_key); + } +} + + +void +ngx_lua_kong_ssl_cleanup_trusted_store(ngx_lua_kong_ssl_ctx_t *ctx) +{ + if (ctx->upstream_trusted_store != NULL) { + X509_STORE_free(ctx->upstream_trusted_store); + } +} + + +void +ngx_lua_kong_ssl_cleanup(ngx_lua_kong_ssl_ctx_t *ctx) +{ + if (ctx == NULL) { + return; + } + + ngx_lua_kong_ssl_cleanup_cert_and_key(ctx); + + ngx_lua_kong_ssl_cleanup_trusted_store(ctx); +} + + +int +ngx_lua_kong_ssl_set_upstream_client_cert_and_key(ngx_lua_kong_ssl_ctx_t *ctx, + void *_chain, void *_key) +{ + STACK_OF(X509) *chain = _chain; + EVP_PKEY *key = _key; + STACK_OF(X509) *new_chain; + + if (chain == NULL || key == NULL) { + return NGX_ERROR; + } + + if (ctx == NULL) { + return NGX_ERROR; + + } else if (ctx->upstream_client_certificate_chain != NULL) { + ngx_lua_kong_ssl_cleanup_cert_and_key(ctx); + + ctx->upstream_client_certificate_chain = NULL; + ctx->upstream_client_private_key = NULL; + } + + if (EVP_PKEY_up_ref(key) == 0) { + goto failed; + } + + new_chain = sk_X509_deep_copy(chain, ngx_lua_kong_ssl_x509_copy, + X509_free); + if (new_chain == NULL) { + EVP_PKEY_free(key); + goto failed; + } + + ctx->upstream_client_certificate_chain = new_chain; + ctx->upstream_client_private_key = key; + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; +} + + +int +ngx_lua_kong_ssl_set_upstream_ssl_trusted_store(ngx_lua_kong_ssl_ctx_t *ctx, + void *_store) +{ + X509_STORE *store = _store; + + if (store == NULL) { + return NGX_ERROR; + } + + if (ctx->upstream_trusted_store != NULL) { + ngx_lua_kong_ssl_cleanup_trusted_store(ctx); + + ctx->upstream_trusted_store = NULL; + } + + if (X509_STORE_up_ref(store) == 0) { + goto failed; + } + + ctx->upstream_trusted_store = store; + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; +} + + +int +ngx_lua_kong_ssl_set_upstream_ssl_verify(ngx_lua_kong_ssl_ctx_t *ctx, + int verify) +{ + ctx->upstream_ssl_verify_set = 1; + ctx->upstream_ssl_verify = verify; + + return NGX_OK; +} + + +int +ngx_lua_kong_ssl_set_upstream_ssl_verify_depth(ngx_lua_kong_ssl_ctx_t *ctx, + int depth) +{ + ctx->upstream_ssl_verify_depth_set = 1; + ctx->upstream_ssl_verify_depth = depth; + + return NGX_OK; +} + + +ngx_flag_t +ngx_lua_kong_ssl_get_upstream_ssl_verify(ngx_lua_kong_ssl_ctx_t *ctx, + ngx_flag_t proxy_ssl_verify) +{ + /* + * if upstream_ssl_verify is not set, + * use the default Nginx proxy_ssl_verify value + */ + if (!ctx->upstream_ssl_verify_set) { + return proxy_ssl_verify; + } + + return ctx->upstream_ssl_verify; +} +#endif \ No newline at end of file diff --git a/src/ssl/ngx_lua_kong_ssl.h b/src/ssl/ngx_lua_kong_ssl.h new file mode 100644 index 00000000..11c7a112 --- /dev/null +++ b/src/ssl/ngx_lua_kong_ssl.h @@ -0,0 +1,50 @@ +/** + * Copyright 2019-2022 Kong Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NGX_LUA_KONG_SSL_H_INCLUDED_ +#define _NGX_LUA_KONG_SSL_H_INCLUDED_ + + +#include +#include + + +typedef struct { + STACK_OF(X509) *upstream_client_certificate_chain; + EVP_PKEY *upstream_client_private_key; + X509_STORE *upstream_trusted_store; + ngx_uint_t upstream_ssl_verify_depth; + unsigned upstream_ssl_verify:1; + unsigned upstream_ssl_verify_set:1; + unsigned upstream_ssl_verify_depth_set:1; +} ngx_lua_kong_ssl_ctx_t; + +ngx_int_t ngx_lua_kong_ssl_init(ngx_conf_t *cf); +const char *ngx_lua_kong_ssl_disable_session_reuse(ngx_connection_t *c); +int ngx_lua_kong_ssl_get_full_client_certificate_chain(ngx_connection_t *c, + char *buf, size_t *buf_len); + +void ngx_lua_kong_ssl_set_upstream_ssl(ngx_lua_kong_ssl_ctx_t *ctx, ngx_connection_t *c); +void ngx_lua_kong_ssl_cleanup(ngx_lua_kong_ssl_ctx_t *ctx); +int ngx_lua_kong_ssl_set_upstream_client_cert_and_key(ngx_lua_kong_ssl_ctx_t *ctx, + void *_chain, void *_key); +int ngx_lua_kong_ssl_set_upstream_ssl_trusted_store(ngx_lua_kong_ssl_ctx_t *ctx, + void *_store); +int ngx_lua_kong_ssl_set_upstream_ssl_verify(ngx_lua_kong_ssl_ctx_t *ctx, + int verify); +int ngx_lua_kong_ssl_set_upstream_ssl_verify_depth(ngx_lua_kong_ssl_ctx_t *ctx, + int depth); +ngx_flag_t ngx_lua_kong_ssl_get_upstream_ssl_verify(ngx_lua_kong_ssl_ctx_t *ctx, + ngx_flag_t proxy_ssl_verify); + +#endif /* _NGX_LUA_KONG_SSL_H_INCLUDED_ */ diff --git a/stream/src/ngx_stream_lua_kong_module.c b/stream/src/ngx_stream_lua_kong_module.c index 3ebf63b6..70b2e5ce 100644 --- a/stream/src/ngx_stream_lua_kong_module.c +++ b/stream/src/ngx_stream_lua_kong_module.c @@ -15,19 +15,15 @@ */ -#include -#include -#include -#include #include "ngx_stream_lua_kong_module.h" static void* ngx_stream_lua_kong_create_srv_conf(ngx_conf_t* cf); - +static ngx_int_t ngx_stream_lua_kong_init(ngx_conf_t *cf); static ngx_stream_module_t ngx_stream_lua_kong_module_ctx = { NULL, /* preconfiguration */ - NULL, /* postconfiguration */ + ngx_stream_lua_kong_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ @@ -66,6 +62,51 @@ ngx_module_t ngx_stream_lua_kong_module = { }; +static ngx_int_t +ngx_stream_lua_kong_init(ngx_conf_t *cf) +{ + return ngx_lua_kong_ssl_init(cf); +} + + + +static void +ngx_stream_lua_kong_cleanup(void *data) +{ + ngx_stream_lua_kong_ctx_t *ctx = data; + + ngx_lua_kong_ssl_cleanup(&ctx->ssl_ctx); +} + + +ngx_stream_lua_kong_ctx_t * +ngx_stream_lua_kong_get_module_ctx(ngx_stream_lua_request_t *r) +{ + ngx_stream_lua_kong_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_kong_module); + + if (ctx == NULL) { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_stream_lua_kong_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->data = ctx; + cln->handler = ngx_stream_lua_kong_cleanup; + + ngx_stream_lua_set_ctx(r, ctx, ngx_stream_lua_kong_module); + } + + return ctx; +} + static void * ngx_stream_lua_kong_create_srv_conf(ngx_conf_t* cf) { @@ -92,22 +133,75 @@ ngx_stream_lua_kong_ffi_get_static_tag(ngx_stream_lua_request_t *r) } +int +ngx_stream_lua_kong_ffi_get_full_client_certificate_chain(ngx_stream_lua_request_t *r, + char *buf, size_t *buf_len) +{ +#if (NGX_SSL) + return ngx_lua_kong_ssl_get_full_client_certificate_chain(r->connection, buf, buf_len); +#else + return NGX_ABORT; +#endif +} + + +/* + * disables session reuse for the current TLS connection, must be called + * in ssl_certby_lua* phase + */ + +const char * +ngx_stream_lua_kong_ffi_disable_session_reuse(ngx_stream_lua_request_t *r) +{ +#if (NGX_SSL) + return ngx_lua_kong_ssl_disable_session_reuse(r->connection); +#else + return "TLS support is not enabled in Nginx build" +#endif +} + + #if (NGX_STREAM_SSL) +int +ngx_stream_lua_kong_ffi_set_upstream_ssl_verify(ngx_stream_lua_request_t *r, + int verify) +{ + ngx_stream_lua_kong_ctx_t *ctx; + + ctx = ngx_stream_lua_kong_get_module_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + + return ngx_lua_kong_ssl_set_upstream_ssl_verify(&ctx->ssl_ctx, verify); +} + + +int +ngx_stream_lua_kong_ffi_set_upstream_ssl_verify_depth(ngx_stream_lua_request_t *r, + int depth) +{ + ngx_stream_lua_kong_ctx_t *ctx; + + ctx = ngx_stream_lua_kong_get_module_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + + return ngx_lua_kong_ssl_set_upstream_ssl_verify_depth(&ctx->ssl_ctx, depth); +} + int ngx_stream_lua_kong_ffi_proxy_ssl_disable(ngx_stream_lua_request_t *r) { ngx_stream_lua_kong_ctx_t *ctx; - ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_kong_module); - if (ctx == NULL) { - ctx = ngx_pcalloc(r->pool, sizeof(ngx_stream_lua_kong_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } + ctx = ngx_stream_lua_kong_get_module_ctx(r); - ngx_stream_lua_set_ctx(r, ctx, ngx_stream_lua_kong_module); + if (ctx == NULL) { + return NGX_ERROR; } ctx->proxy_ssl_disable = 1; @@ -116,7 +210,7 @@ ngx_stream_lua_kong_ffi_proxy_ssl_disable(ngx_stream_lua_request_t *r) } -ngx_uint_t +ngx_flag_t ngx_stream_lua_kong_get_proxy_ssl_disable(ngx_stream_session_t *s) { ngx_stream_lua_kong_ctx_t *ctx; @@ -127,6 +221,84 @@ ngx_stream_lua_kong_get_proxy_ssl_disable(ngx_stream_session_t *s) } +/* + * called by ngx_stream_upstream_ssl_init_connection right after + * ngx_ssl_create_connection to override any parameters in the + * ngx_ssl_conn_t before handshake occurs + * + * c->ssl is guaranteed to be present and valid + */ + +void +ngx_stream_lua_kong_set_upstream_ssl(ngx_stream_session_t *s, ngx_connection_t *c) +{ + ngx_stream_lua_kong_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_kong_module); + + if (ctx == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "skip overriding upstream SSL configuration, " + "module ctx not set"); + return; + } + + return ngx_lua_kong_ssl_set_upstream_ssl(&ctx->ssl_ctx, c); +} + + +int +ngx_stream_lua_kong_ffi_set_upstream_client_cert_and_key(ngx_stream_lua_request_t *r, + void *_chain, void *_key) +{ + ngx_stream_lua_kong_ctx_t *ctx; + + ctx = ngx_stream_lua_kong_get_module_ctx(r); + + if (ctx == NULL) { + return NGX_ERROR; + } + + return ngx_lua_kong_ssl_set_upstream_client_cert_and_key(&ctx->ssl_ctx, _chain, _key); +} + + +int +ngx_stream_lua_kong_ffi_set_upstream_ssl_trusted_store(ngx_stream_lua_request_t *r, + void *_store) +{ + X509_STORE *store = _store; + ngx_stream_lua_kong_ctx_t *ctx; + + if (store == NULL) { + return NGX_ERROR; + } + + ctx = ngx_stream_lua_kong_get_module_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + + return ngx_lua_kong_ssl_set_upstream_ssl_trusted_store(&ctx->ssl_ctx,_store); +} + + +ngx_flag_t +ngx_stream_lua_kong_get_upstream_ssl_verify(ngx_stream_session_t *s, + ngx_flag_t proxy_ssl_verify) +{ + ngx_stream_lua_kong_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_kong_module); + if (ctx == NULL) { + return proxy_ssl_verify; + } + + return ngx_lua_kong_ssl_get_upstream_ssl_verify(&ctx->ssl_ctx, proxy_ssl_verify); +} +#endif + + // macOS with M1 fixes, see: https://github.com/LuaJIT/LuaJIT/issues/205 int @@ -155,6 +327,3 @@ ngx_stream_lua_ffi_shdict_incr_m1(ngx_shdict_incr_t *s) } // macOS with M1 fixes end - - -#endif diff --git a/stream/src/ngx_stream_lua_kong_module.h b/stream/src/ngx_stream_lua_kong_module.h index a6b97e53..c1630222 100644 --- a/stream/src/ngx_stream_lua_kong_module.h +++ b/stream/src/ngx_stream_lua_kong_module.h @@ -5,10 +5,12 @@ #include #include #include - +#include +#include "../../src/ssl/ngx_lua_kong_ssl.h" typedef struct { - ngx_uint_t proxy_ssl_disable; /* unsigned proxy_ssl_disable:1; */ + ngx_lua_kong_ssl_ctx_t ssl_ctx; + ngx_flag_t proxy_ssl_disable; /* unsigned proxy_ssl_disable:1; */ } ngx_stream_lua_kong_ctx_t; @@ -19,7 +21,7 @@ typedef struct { #if (NGX_STREAM_SSL) -ngx_uint_t +ngx_flag_t ngx_stream_lua_kong_get_proxy_ssl_disable(ngx_stream_session_t *s); @@ -91,6 +93,13 @@ int ngx_stream_lua_ffi_shdict_incr_m1(ngx_shdict_incr_t *s); // macOS with M1 fixes end +void +ngx_stream_lua_kong_set_upstream_ssl(ngx_stream_session_t *s, + ngx_connection_t *c); + +ngx_flag_t +ngx_stream_lua_kong_get_upstream_ssl_verify(ngx_stream_session_t *s, + ngx_flag_t proxy_ssl_verify); #endif diff --git a/t/stream/001-upstream-tls.t b/t/stream/001-upstream-tls.t index 9688eb85..93475293 100644 --- a/t/stream/001-upstream-tls.t +++ b/t/stream/001-upstream-tls.t @@ -1,14 +1,16 @@ # vim:set ft= ts=4 sw=4 et fdm=marker: use Test::Nginx::Socket::Lua::Stream; +use Test::Nginx::Socket::Lua; #worker_connections(1014); #master_process_enabled(1); -log_level('warn'); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); repeat_each(2); -plan tests => repeat_each() * (blocks() * 3); +plan tests => repeat_each() * (blocks() * 3 + 1); #no_diff(); #no_long_string(); @@ -59,3 +61,741 @@ __DATA__ --- stream_response_like: ^.+400 The plain HTTP request was sent to HTTPS port.+$ --- no_error_log [error] + + + +=== TEST 4: not sending client certificate, upstream returns 400 +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + # to suppress a valgrind false positive in the nginx core: + proxy_ssl_session_reuse off; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_client_certificate ../../cert/ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } + +--- stream_config + proxy_ssl_session_reuse off; + +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + +--- stream_request eval +"GET / HTTP/1.0\r\nHost: mockbin.com\r\n\r\n" + +--- stream_response_like +^.+No required SSL certificate was sent.+ + +--- error_log +client sent no required SSL certificate + +--- no_error_log +[error] +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 5: sending client certificate using resty.kong.tls.set_upstream_cert_and_key in preread phase +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_client_certificate ../../cert/ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/cert/client_example.com.crt")) + local cert_data = f:read("*a") + f:close() + + local chain = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/cert/client_example.com.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + local ok, err = tls.set_upstream_cert_and_key(chain, key) + if not ok then + ngx.say("set_upstream_cert_and_key failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +verify:1, error:0, depth:0, subject:"/C=US/ST=California/O=Kong Testing/CN=foo@example.com", issuer:"/C=US/ST=California/O=Kong Testing/CN=Kong Testing Intermidiate CA" +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 6: sending client certificate using resty.kong.tls.set_upstream_cert_and_key in balancer phase +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_client_certificate ../../cert/ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_config + upstream foo { + server unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + balancer_by_lua_block { + collectgarbage() -- to make leak check mode pass + + local tls = require("resty.kong.tls") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/cert/client_example.com.crt")) + local cert_data = f:read("*a") + f:close() + + local chain = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/cert/client_example.com.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + local ok, err = tls.set_upstream_cert_and_key(chain, key) + if not ok then + ngx.say("set_upstream_cert_and_key failed: ", err) + end + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + + proxy_pass foo; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +verify:1, error:0, depth:0, subject:"/C=US/ST=California/O=Kong Testing/CN=foo@example.com", issuer:"/C=US/ST=California/O=Kong Testing/CN=Kong Testing Intermidiate CA" + +--- skip_nginx +3: < 1.21.4 + + +=== TEST 7: repeatedly calling resty.kong.tls.set_upstream_cert_and_key does not leaks memory +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_client_certificate ../../cert/ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/cert/client_example.com.crt")) + local cert_data = f:read("*a") + f:close() + + local chain = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/cert/client_example.com.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + for i = 1, 1000 do + local ok, err = tls.set_upstream_cert_and_key(chain, key) + if not ok then + ngx.say("set_upstream_cert_and_key failed: ", err) + break + end + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +verify:1, error:0, depth:0, subject:"/C=US/ST=California/O=Kong Testing/CN=foo@example.com", issuer:"/C=US/ST=California/O=Kong Testing/CN=Kong Testing Intermidiate CA" + +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 8: setting proxy_ssl_verify with invalid verify chain, verify failed +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/client_example.com.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +receive stream response error: connection reset by peer + +--- error_log +upstream SSL certificate verify error: (2:unable to get issuer certificate) + +--- skip_nginx +4: < 1.21.4 + + + +=== TEST 9: setting proxy_ssl_verify with invalid verify chain, turn off with tls.set_upstream_ssl_verify +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(false) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- no_error_log +skip overriding upstream SSL configuration + +--- skip_nginx +4: < 1.21.4 + + +=== TEST 10: proxy_ssl_verify not set, turn on with tls.set_upstream_ssl_verify +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(true) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +receive stream response error: connection reset by peer + +--- error_log +upstream SSL certificate verify error: (20:unable to get local issuer certificate) + +--- skip_nginx +4: < 1.21.4 + + + +=== TEST 11: proxy_ssl_verify not set, turn on with tls.set_upstream_ssl_verify in balancer phase +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_client_certificate ../../cert/ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_config + upstream foo { + server unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + balancer_by_lua_block { + collectgarbage() -- to make leak check mode pass + + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(true) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + } + } +--- stream_server_config + proxy_ssl_name example.com; + proxy_ssl on; + + proxy_pass foo; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +receive stream response error: connection reset by peer + +--- error_log +upstream SSL certificate verify error: (20:unable to get local issuer certificate) + +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 12: setting insufficient verify depth with tls.set_upstream_ssl_verify_depth, verify failed +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_verify_depth 100; + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + -- verify depth 0 means only self signed cert is allowed + local ok, err = tls.set_upstream_ssl_verify_depth(0) + if not ok then + ngx.say("set_upstream_ssl_verify_depth failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +receive stream response error: connection reset by peer + +--- error_log +upstream SSL certificate verify error: (22:certificate chain too long) + +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 13: setting deeper verify depth with tls.set_upstream_ssl_verify_depth, verify pass +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_trusted_certificate ../../cert/ca.crt; + proxy_ssl_verify on; + proxy_ssl_verify_depth 0; + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify_depth(1) + if not ok then + ngx.say("set_upstream_ssl_verify_depth failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +X509_check_host(): match + +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 14: setting incorrect trusted store with tls.set_upstream_ssl_trusted_store, verify failed +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(true) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + + local store = require("resty.openssl.x509.store") + local x509 = require("resty.openssl.x509") + local s = assert(store.new()) + for _, f in ipairs({ "client_example.com", "intermediate" }) do + local f = assert(io.open("t/cert/" .. f .. ".crt")) + local cert_data = f:read("*a") + f:close() + assert(s:add(x509.new(cert_data))) + end + local ok, err = tls.set_upstream_ssl_trusted_store(s) + if not ok then + ngx.say("set_upstream_ssl_trusted_store failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +receive stream response error: connection reset by peer + +--- error_log +upstream SSL certificate verify error: (2:unable to get issuer certificate) + +--- skip_nginx +3: < 1.21.4 + + + +=== TEST 15: setting trusted store with tls.set_upstream_ssl_trusted_store, verify passed +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(true) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + + local store = require("resty.openssl.x509.store") + local x509 = require("resty.openssl.x509") + local s = assert(store.new()) + for _, f in ipairs({ "intermediate", "ca" }) do + local f = assert(io.open("t/cert/" .. f .. ".crt")) + local cert_data = f:read("*a") + f:close() + assert(s:add(x509.new(cert_data))) + end + local ok, err = tls.set_upstream_ssl_trusted_store(s) + if not ok then + ngx.say("set_upstream_ssl_trusted_store failed: ", err) + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +X509_check_host(): match + +--- skip_nginx +2: < 1.21.4 + + + +=== TEST 16: setting trusted store multiple times tls.set_upstream_ssl_trusted_store, no leak detected +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name example.com; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + + server_tokens off; + + location /foo { + default_type 'text/plain'; + more_clear_headers Date; + echo 'it works!'; + } + } +--- stream_server_config + proxy_ssl_name example.com; + proxy_ssl on; + + preread_by_lua_block { + local tls = require("resty.kong.tls") + + local ok, err = tls.set_upstream_ssl_verify(true) + if not ok then + ngx.say("set_upstream_ssl_verify failed: ", err) + end + + local store = require("resty.openssl.x509.store") + local x509 = require("resty.openssl.x509") + local s = assert(store.new()) + for _, f in ipairs({ "intermediate", "ca" }) do + local f = assert(io.open("t/cert/" .. f .. ".crt")) + local cert_data = f:read("*a") + f:close() + assert(s:add(x509.new(cert_data))) + end + for i=0,3 do + local ok, err = tls.set_upstream_ssl_trusted_store(s) + if not ok then + ngx.say("set_upstream_ssl_trusted_store failed: ", err) + return + end + end + } + + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_session_reuse off; + +--- stream_request eval +"GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n" + +--- stream_response_like +it works! + +--- error_log +X509_check_host(): match + +--- skip_nginx +2: < 1.21.4 diff --git a/t/stream/003-tls.t b/t/stream/003-tls.t new file mode 100644 index 00000000..57dce60f --- /dev/null +++ b/t/stream/003-tls.t @@ -0,0 +1,402 @@ +# vim:set ft= ts=4 sw=4 et: + +use Test::Nginx::Socket::Lua::Stream; +use Test::Nginx::Socket::Lua; +use Cwd qw(cwd); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 7 - 1); + +my $pwd = cwd(); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ +=== TEST 1: session reuse by session tickets without disable_session_reuse +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_session_cache off; + ssl_session_tickets on; + + content_by_lua_block { + ngx.say("it works") + } + } + +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + do + local session + for i = 1, 2 do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + session, err = sock:sslhandshake(session, "example.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + ngx.say("received: ", line) + local ok, err = sock:close() + if not ok then + ngx.say("failed to close: ", err) + return + end + end + end -- do + } + +--- response_body +received: it works +received: it works +--- error_log +SSL reused session +--- no_error_log +[error] +[alert] +[warn] +[crit] +--- skip_nginx +7: < 1.21.4 + + + +=== TEST 2: session reuse by session cache without disable_session_reuse +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_session_cache shared:SSL:128k; + ssl_session_tickets off; + + content_by_lua_block { + ngx.say("it works") + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + do + local session + for i = 1, 2 do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + session, err = sock:sslhandshake(session, "example.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + ngx.say("received: ", line) + local ok, err = sock:close() + if not ok then + ngx.say("failed to close: ", err) + return + end + end + end -- do + } + +--- response_body +received: it works +received: it works +--- error_log +SSL reused session +--- no_error_log +[error] +[alert] +[warn] +[crit] +--- skip_nginx +7: < 1.21.4 + + + +=== TEST 3: disable_session_reuse can suppress usage of session tickets +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + assert(require("resty.kong.tls").disable_session_reuse()) + } + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_session_cache off; + ssl_session_tickets on; + + content_by_lua_block { + ngx.say("it works") + } + } +--- stream_server_config + + content_by_lua_block { + local sock = ngx.socket.tcp() + do + local session + for i = 1, 2 do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + session, err = sock:sslhandshake(session, "example.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + ngx.say("received: ", line) + local ok, err = sock:close() + if not ok then + ngx.say("failed to close: ", err) + return + end + end + end -- do + } + +--- response_body +received: it works +received: it works +--- error_log +--- no_error_log +SSL reused session +[error] +[alert] +[warn] +[crit] +--- skip_nginx +7: < 1.21.4 + + + +=== TEST 4: disable_session_reuse can suppress usage of session cache +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + assert(require("resty.kong.tls").disable_session_reuse()) + } + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + ssl_session_cache shared:SSL:128k; + ssl_session_tickets off; + + content_by_lua_block { + ngx.say("it works") + } + } +--- stream_server_config + content_by_lua_block { + local sock = ngx.socket.tcp() + do + local session + for i = 1, 2 do + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + session, err = sock:sslhandshake(session, "example.com") + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + local line, err = sock:receive() + if not line then + ngx.say("failed to receive response status line: ", err) + return + end + ngx.say("received: ", line) + local ok, err = sock:close() + if not ok then + ngx.say("failed to close: ", err) + return + end + end + end -- do + } + +--- response_body +received: it works +received: it works +--- error_log +--- no_error_log +SSL reused session +[error] +[alert] +[warn] +[crit] +--- skip_nginx +7: < 1.21.4 + + + +=== TEST 5: get_full_client_certificate_chain behaves normally after requesting the client certificate +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_certificate_by_lua_block { + print("ssl cert by lua is running!") + assert(require("ngx.ssl").verify_client()) + } + ssl_certificate ../../cert/example.com.crt; + ssl_certificate_key ../../cert/example.com.key; + content_by_lua_block { + ngx.say("DN: ", ngx.var.ssl_client_s_dn) + ngx.say("Verify: ", ngx.var.ssl_client_verify) + local chain = require("resty.kong.tls").get_full_client_certificate_chain() + if chain then + chain = chain:sub(0, -2) + end + ngx.say("Chain: ", chain) + } + } + +--- stream_server_config + proxy_ssl_certificate ../../cert/client_example.com.crt; + proxy_ssl_certificate_key ../../cert/client_example.com.key; + proxy_ssl_session_reuse off; + proxy_ssl_name example.com; + proxy_ssl on; + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + +--- response_body +--- response_body +DN: CN=foo@example.com,O=Kong Testing,ST=California,C=US +Verify: FAILED:unable to get local issuer certificate +Chain: -----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICIAEwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG +A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIyMDAz +MTFaFw0yOTA0MjgyMDAzMTFaMFMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp +Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxGDAWBgNVBAMMD2Zvb0BleGFt +cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJldMxsZHDxA +RpbSXdIFZiTf8D0dYgsPnsmx5tVjA/zrVBSVBPO9KunaXNm4Z6JWmUwenzFGbzWP +NLfbLn4khuoczzqSru5XfbyH1HrD0cd5lkf44Dw1/otfIFDBleiR/OWEiAxwS4zi +xIajNyvLr3gC5dv+F+JuWpW1yVQxybIDQWoI25xpd3+ZkXO+OLkToo+YpuwIDlUj +6Rkm5kbqoxDpaDihA2bsAqjNG7G+SHthaNyACsQsU/t6BHSWzHumScN0CxJ+TeVH +fTZklelItZ6YP0B0RQjzvSGA423UgALzqJglGPe8UDjm3BMlg2xhTfnfy1J6Vmbt +5jx6FOXUARsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF +oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp +ZmljYXRlMB0GA1UdDgQWBBRTzNOmhGRXaZamxVfnlKXarIOEmDAfBgNVHSMEGDAW +gBQLDgQOl/htYk8k8DvGb9IKO40RETAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw +FAYIKwYBBQUHAwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNv +bYEPYmFyQGV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBziDuVjU0I1CwO +b1Cx2TJpzi3l5FD/ozrMZT6F3EpkJFGZWgXrsXHz/0qKTrsbB2m3/fcyd0lwQ5Lh +fz8X1HPrwXa3BqZskNu1vOUNiqAYWvQ5gtbpweJ96LzMSYVGLK78NigYTtK+Rgq3 +As5CVfLXDBburrQNGyRTsilCQDNBvIpib0eqg/HJCNDFMPrBzTMPpUutyatfpFH2 +UwTiVBfA14YYDxZaetYWeksy28XH6Uj0ylyz67VHND+gBMmQNLXQHJTIDh8JuIf2 +ec6o4HrtyyuRE3urNQmcPMAokacm4NKw2+og6Rg1VS/pckaSPOlSEmNnKFiXStv+ +AVd77NGriUWDFCmnrFNOPOIS019W0oOk6YMwTUDSa86Ii6skCtBLHmp/cingkTWg +7KEbdT1uVVPgseC2AFpQ1BWJOjjtyW3GWuxERIhuab9/ckTz6BuIiuK7mfsvPBrn +BqjZyt9WAx8uaWMS/ZrmIj3fUXefaPtl27jMSsiU5oi2vzFu0xiXJb6Jr7RQxD3O +XRnycL/chWnp7eVV1TQS+XzZ3ZZQIjckDWX4E+zGo4o9pD1YC0eytbIlSuqYVr/t +dZmD2gqju3Io9EXPDlRDP2VIX9q1euF9caz1vpLCfV+F8wVPtZe5p6JbNugdgjix +nDZ2sD2xGXy6/fNG75oHveYo6MREFw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG +A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw +NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV +MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50 +ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj +oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv +mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb +zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP +qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp +zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7 +cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp +ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U +FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S +CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx +1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO +XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE +FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8 +apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P +EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV +5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh +SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6 +pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x +snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP +PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD ++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj +GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4 +qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ +uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA== +-----END CERTIFICATE----- +--- error_log +[lua] ssl_certificate_by_lua:2: ssl cert by lua is running! +--- no_error_log +[error] +[alert] +[warn] +[crit] +--- skip_nginx +7: < 1.21.4 + + + +=== TEST 6: calling get_full_client_certificate_chain in plain text request, +error is returned +--- stream_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + +--- stream_server_config + content_by_lua_block { + local res, err = require("resty.kong.tls").get_full_client_certificate_chain() + ngx.say(res, ', ', err) + } + +--- response_body +nil, connection is not TLS or TLS support for Nginx not enabled +--- error_log +--- no_error_log +[error] +[alert] +[warn] +[crit] +--- skip_nginx +6: < 1.21.4