From b6fb624bb394afd16f93a4aafaf2f53b07ff0ef6 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 18 Oct 2026 00:05:44 +0800 Subject: [PATCH 1/3] feature: ssl_verify_by_lua* directives working after receiving client certificates, allowing us to use Lua to dynamically control their verification. --- config | 2 + src/ngx_stream_lua_common.h | 7 + src/ngx_stream_lua_control.c | 4 +- src/ngx_stream_lua_coroutine.c | 3 + src/ngx_stream_lua_ctx.c | 6 +- src/ngx_stream_lua_module.c | 83 ++- src/ngx_stream_lua_phase.c | 4 + src/ngx_stream_lua_ssl.h | 1 + src/ngx_stream_lua_ssl_verifyby.c | 710 ++++++++++++++++++++++ src/ngx_stream_lua_ssl_verifyby.h | 29 + src/ngx_stream_lua_uthread.c | 1 + src/ngx_stream_lua_util.h | 2 + src/ngx_stream_lua_variable.c | 3 +- t/168-ssl-verify.t | 970 ++++++++++++++++++++++++++++++ 14 files changed, 1820 insertions(+), 5 deletions(-) create mode 100644 src/ngx_stream_lua_ssl_verifyby.c create mode 100644 src/ngx_stream_lua_ssl_verifyby.h create mode 100644 t/168-ssl-verify.t diff --git a/config b/config index 56c7b554..b9385ef3 100644 --- a/config +++ b/config @@ -278,6 +278,7 @@ STREAM_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_stream_lua_semaphore.c \ $ngx_addon_dir/src/ngx_stream_lua_ssl_client_helloby.c \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.c \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_verifyby.c \ $ngx_addon_dir/src/ngx_stream_lua_proxy_ssl_certby.c \ $ngx_addon_dir/src/ngx_stream_lua_proxy_ssl_verifyby.c \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.c \ @@ -324,6 +325,7 @@ STREAM_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_stream_lua_semaphore.h \ $ngx_addon_dir/src/ngx_stream_lua_ssl_client_helloby.h \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.h \ + $ngx_addon_dir/src/ngx_stream_lua_ssl_verifyby.h \ $ngx_addon_dir/src/ngx_stream_lua_proxy_ssl_certby.h \ $ngx_addon_dir/src/ngx_stream_lua_proxy_ssl_verifyby.h \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.h \ diff --git a/src/ngx_stream_lua_common.h b/src/ngx_stream_lua_common.h index 1126f7dd..86d2ba5f 100644 --- a/src/ngx_stream_lua_common.h +++ b/src/ngx_stream_lua_common.h @@ -154,6 +154,7 @@ #define NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO 0x0080 #define NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY 0x0100 #define NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT 0x0200 +#define NGX_STREAM_LUA_CONTEXT_SSL_VERIFY 0x0400 #define NGX_STREAM_LUA_FFI_NO_REQ_CTX -100 @@ -288,6 +289,12 @@ struct ngx_stream_lua_srv_conf_s { ngx_stream_lua_srv_conf_handler_pt ssl_client_hello_handler; ngx_str_t ssl_client_hello_src; u_char *ssl_client_hello_src_key; + + ngx_stream_lua_srv_conf_handler_pt ssl_verify_handler; + ngx_str_t ssl_verify_src; + u_char *ssl_verify_src_key; + + ngx_flag_t skip_openssl_default_verify; } srv; #ifdef HAVE_PROXY_SSL_PATCH diff --git a/src/ngx_stream_lua_control.c b/src/ngx_stream_lua_control.c index 6d9089ca..cde57e92 100644 --- a/src/ngx_stream_lua_control.c +++ b/src/ngx_stream_lua_control.c @@ -116,6 +116,7 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, | NGX_STREAM_LUA_CONTEXT_BALANCER | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY #ifdef HAVE_LUA_PROXY_SSL | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY @@ -131,7 +132,8 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY #endif - | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO )) + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY)) { #if (NGX_STREAM_SSL) diff --git a/src/ngx_stream_lua_coroutine.c b/src/ngx_stream_lua_coroutine.c index a8533ca8..92abd197 100644 --- a/src/ngx_stream_lua_coroutine.c +++ b/src/ngx_stream_lua_coroutine.c @@ -205,6 +205,7 @@ ngx_stream_lua_coroutine_resume(lua_State *L) | NGX_STREAM_LUA_CONTEXT_TIMER | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY #ifdef HAVE_LUA_PROXY_SSL | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY @@ -270,6 +271,7 @@ ngx_stream_lua_coroutine_yield(lua_State *L) | NGX_STREAM_LUA_CONTEXT_TIMER | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY #ifdef HAVE_LUA_PROXY_SSL | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY @@ -434,6 +436,7 @@ ngx_stream_lua_coroutine_status(lua_State *L) | NGX_STREAM_LUA_CONTEXT_TIMER | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY #ifdef HAVE_LUA_PROXY_SSL | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY diff --git a/src/ngx_stream_lua_ctx.c b/src/ngx_stream_lua_ctx.c index 54c7b306..389a28eb 100644 --- a/src/ngx_stream_lua_ctx.c +++ b/src/ngx_stream_lua_ctx.c @@ -102,7 +102,8 @@ ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r, int *in_ssl_phase, } *in_ssl_phase = ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO); + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY); *ssl_ctx_ref = LUA_NOREF; #if (NGX_STREAM_SSL) @@ -138,7 +139,8 @@ ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref) ssl_ctx = NULL; if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO)) + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY)) { ssl_ctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection); if (ssl_ctx == NULL) { diff --git a/src/ngx_stream_lua_module.c b/src/ngx_stream_lua_module.c index 772e8c55..c0b73bd0 100644 --- a/src/ngx_stream_lua_module.c +++ b/src/ngx_stream_lua_module.c @@ -30,6 +30,7 @@ #include "ngx_stream_lua_semaphore.h" #include "ngx_stream_lua_ssl_client_helloby.h" #include "ngx_stream_lua_ssl_certby.h" +#include "ngx_stream_lua_ssl_verifyby.h" #ifdef HAVE_LUA_PROXY_SSL #include "ngx_stream_lua_proxy_ssl_certby.h" @@ -433,6 +434,27 @@ static ngx_command_t ngx_stream_lua_cmds[] = { 0, (void *) ngx_stream_lua_ssl_cert_handler_file }, + { ngx_string("ssl_verify_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_ssl_verify_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_verify_handler_inline }, + + { ngx_string("ssl_verify_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_ssl_verify_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_ssl_verify_handler_file }, + + { ngx_string("lua_skip_openssl_default_verify"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_lua_srv_conf_t, srv.skip_openssl_default_verify), + NULL }, + #if HAVE_LUA_PROXY_SSL /* same context as proxy_pass directive */ { ngx_string("proxy_ssl_certificate_by_lua_block"), @@ -876,6 +898,10 @@ ngx_stream_lua_create_srv_conf(ngx_conf_t *cf) * lscf->srv.ssl_client_hello_src = { 0, NULL }; * lscf->srv.ssl_client_hello_src_key = NULL; * + * lscf->srv.ssl_verify_handler = NULL; + * lscf->srv.ssl_verify_src = { 0, NULL }; + * lscf->srv.ssl_verify_src_key = NULL; + * * lscf->ups.proxy_ssl_cert_handler = NULL; * lscf->ups.proxy_ssl_cert_src = { 0, NULL }; * lscf->ups.proxy_ssl_cert_src_key = NULL; @@ -921,11 +947,17 @@ ngx_stream_lua_create_srv_conf(ngx_conf_t *cf) conf->log_socket_errors = NGX_CONF_UNSET; -#ifdef HAVE_LUA_PROXY_SSL +#if (NGX_STREAM_SSL) + conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_certificates = NGX_CONF_UNSET_PTR; conf->ssl_certificate_keys = NGX_CONF_UNSET_PTR; + conf->srv.skip_openssl_default_verify = NGX_CONF_UNSET; + +#ifdef HAVE_LUA_PROXY_SSL conf->ups.upstream_skip_openssl_default_verify = NGX_CONF_UNSET; +#endif + #endif return conf; @@ -1030,8 +1062,57 @@ ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) #endif } + + } + + if (conf->srv.ssl_verify_src.len == 0) { + conf->srv.ssl_verify_src = prev->srv.ssl_verify_src; + conf->srv.ssl_verify_src_key = prev->srv.ssl_verify_src_key; + conf->srv.ssl_verify_handler = prev->srv.ssl_verify_handler; + } + + if (conf->srv.ssl_verify_src.len) { + if (sscf == NULL || sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl configured for the server"); + + return NGX_CONF_ERROR; + } + + if (sscf->verify == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "ssl_verify_client can't be off " + "when using ssl_verify_by_lua*"); + + return NGX_CONF_ERROR; + } + +#ifdef LIBRESSL_VERSION_NUMBER + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by ssl_verify_by_lua*"); + return NGX_CONF_ERROR; + +#else + +#if defined(SSL_ERROR_WANT_RETRY_VERIFY) && \ + OPENSSL_VERSION_NUMBER >= 0x40100000L + + SSL_CTX_set_cert_verify_callback(sscf->ssl.ctx, + ngx_stream_lua_ssl_verify_handler, + NULL); + +#else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support ssl_verify_by_lua*"); + return NGX_CONF_ERROR; + +#endif +#endif } + ngx_conf_merge_value(conf->srv.skip_openssl_default_verify, + prev->srv.skip_openssl_default_verify, 0); #endif /* NGX_STREAM_SSL */ diff --git a/src/ngx_stream_lua_phase.c b/src/ngx_stream_lua_phase.c index 35bbfe52..2dd0de47 100644 --- a/src/ngx_stream_lua_phase.c +++ b/src/ngx_stream_lua_phase.c @@ -58,6 +58,10 @@ ngx_stream_lua_ngx_get_phase(lua_State *L) lua_pushliteral(L, "ssl_cert"); break; + case NGX_STREAM_LUA_CONTEXT_SSL_VERIFY: + lua_pushliteral(L, "ssl_verify"); + break; + case NGX_STREAM_LUA_CONTEXT_PREREAD: lua_pushliteral(L, "preread"); break; diff --git a/src/ngx_stream_lua_ssl.h b/src/ngx_stream_lua_ssl.h index aeab886d..e11c6755 100644 --- a/src/ngx_stream_lua_ssl.h +++ b/src/ngx_stream_lua_ssl.h @@ -52,6 +52,7 @@ typedef struct { unsigned entered_client_hello_handler:1; unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; + unsigned entered_ssl_verify_handler:1; #ifdef HAVE_LUA_PROXY_SSL unsigned entered_proxy_ssl_cert_handler:1; unsigned entered_proxy_ssl_verify_handler:1; diff --git a/src/ngx_stream_lua_ssl_verifyby.c b/src/ngx_stream_lua_ssl_verifyby.c new file mode 100644 index 00000000..6fd99025 --- /dev/null +++ b/src/ngx_stream_lua_ssl_verifyby.c @@ -0,0 +1,710 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_STREAM_SSL) + + +#include "ngx_stream_lua_cache.h" +#include "ngx_stream_lua_initworkerby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_ssl_module.h" +#include "ngx_stream_lua_contentby.h" +#include "ngx_stream_lua_ssl_verifyby.h" +#include "ngx_stream_lua_directive.h" +#include "ngx_stream_lua_ssl.h" + + +static void ngx_stream_lua_ssl_verify_done(void *data); +static void ngx_stream_lua_ssl_verify_aborted(void *data); +static u_char *ngx_stream_lua_log_ssl_verify_error(ngx_log_t *log, + u_char *buf, size_t len); +static ngx_int_t ngx_stream_lua_ssl_verify_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +ngx_int_t +ngx_stream_lua_ssl_verify_handler_file(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, + lscf->srv.ssl_verify_src.data, + lscf->srv.ssl_verify_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_verify_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_ssl_verify_handler_inline(ngx_stream_lua_request_t *r, + ngx_stream_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.ssl_verify_src.data, + lscf->srv.ssl_verify_src.len, + lscf->srv.ssl_verify_src_key, + "=ssl_verify_by_lua"); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_ssl_verify_by_chunk(L, r); +} + + +char * +ngx_stream_lua_ssl_verify_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_ssl_verify_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if !defined(SSL_ERROR_WANT_RETRY_VERIFY) || \ + OPENSSL_VERSION_NUMBER < 0x40100000L + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 4.1.0 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + u_char *p; + u_char *name; + ngx_str_t *value; + ngx_stream_lua_srv_conf_t *lscf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.ssl_verify_handler) { + return "is duplicate"; + } + + if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lscf->srv.ssl_verify_handler = + (ngx_stream_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_stream_lua_ssl_verify_handler_file) { + /* Lua code in an external file */ + + name = ngx_stream_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_verify_src.data = name; + lscf->srv.ssl_verify_src.len = ngx_strlen(name); + + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_verify_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + /* inlined Lua code */ + + lscf->srv.ssl_verify_src = value[1]; + + p = ngx_palloc(cf->pool, + sizeof("ssl_verify_by_lua") + + NGX_STREAM_LUA_INLINE_KEY_LEN); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_verify_src_key = p; + + p = ngx_copy(p, "ssl_verify_by_lua", + sizeof("ssl_verify_by_lua") - 1); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + + return NGX_CONF_OK; + +#endif +} + + +int +ngx_stream_lua_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_stream_lua_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_ssl_ctx_t *cctx; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_session_t *s, *fs; + ngx_ssl_conn_t *ssl_conn; + + ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "ssl verify: connection reusable: %ud", c->reusable); + + cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl verify handler, cert-verify-ctx=%p", cctx); + + if (cctx && cctx->entered_ssl_verify_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "ssl_verify_by_lua: " + "cert verify callback exit code: %d", + cctx->exit_code); + + dd("lua ssl verify done, finally"); + return cctx->exit_code; + } + + return SSL_set_retry_verify(ssl_conn); + } + + dd("first time"); + + ngx_reusable_connection(c, 0); + + s = c->data; + + fc = ngx_stream_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_stream_lua_log_ssl_verify_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + fs = ngx_stream_lua_create_fake_session(fc); + if (fs == NULL) { + goto failed; + } + + fs->main_conf = s->main_conf; + fs->srv_conf = s->srv_conf; + + r = ngx_stream_lua_create_fake_request(fs); + if (r == NULL) { + goto failed; + } + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module); + +#if defined(nginx_version) && nginx_version >= 1027001 + ngx_set_connection_log(fc, cscf->error_log); + +#else +#error "stream ssl_verify_by_lua only supports nginx >= 1.27.1" +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->exit_code = 1; /* successful by default */ + cctx->connection = c; + cctx->request = r; + cctx->entered_ssl_verify_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_stream_lua_ssl_ctx_index, + cctx) == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (lscf->srv.skip_openssl_default_verify == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "ssl_verify_by_lua: openssl default verify"); + + rc = X509_verify_cert(x509_store); + if (rc == 0) { + goto failed; + } + } + + /* TODO honor lua_code_cache off */ + L = ngx_stream_lua_get_lua_vm(r, NULL); + + c->log->action = "loading ssl verify by lua"; + + rc = lscf->srv.ssl_verify_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream ssl_verify_by_lua: handler return value: %i, " + "cert verify callback exit code: %d", rc, + cctx->exit_code); + + c->log->action = "SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(fc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_stream_lua_ssl_verify_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_stream_lua_ssl_verify_aborted; + + return SSL_set_retry_verify(ssl_conn); + +#if 1 +failed: + + if (r && r->pool) { + ngx_stream_lua_free_fake_request(r); + } + + if (fc) { + ngx_stream_lua_close_fake_connection(fc); + } + + return 0; /* verify failure or error */ +#endif +} + + +static void +ngx_stream_lua_ssl_verify_done(void *data) +{ + ngx_connection_t *c; + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl verify done"); + + if (cctx->aborted) { + return; + } + + ngx_stream_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_stream_lua_ssl_verify_aborted(void *data) +{ + ngx_stream_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl verify aborted"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cctx->connection->log, 0, + "stream ssl_verify_by_lua: cert verify callback aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_stream_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_stream_lua_log_ssl_verify_error(ngx_log_t *log, u_char *buf, + size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_verify_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c && c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c && c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + + return buf; +} + + +static ngx_int_t +ngx_stream_lua_ssl_verify_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(r->session); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_stream_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "stream failed to create new" + " coroutine to handle request"); + + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_stream_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_STREAM_LUA_CONTEXT_SSL_VERIFY; + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_stream_lua_finalize_request(r, rc); + return rc; +} + + +/* + * openssl's doc of SSL_CTX_set_cert_verify_callback: + * In any case a viable verification result value must + * be reflected in the error member of x509_store_ctx, + * which can be done using X509_STORE_CTX_set_error. + */ +int +ngx_stream_lua_ffi_ssl_set_verify_result(ngx_stream_lua_request_t *r, + int verify_result, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + *err = "LibreSSL does not support by ssl_verify_by_lua*"; + return NGX_ERROR; +#else + +#if defined(SSL_ERROR_WANT_RETRY_VERIFY) && \ + OPENSSL_VERSION_NUMBER >= 0x40100000L + ngx_ssl_conn_t *ssl_conn; + ngx_stream_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + cctx = ngx_stream_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + x509_store = cctx->x509_store; + + X509_STORE_CTX_set_error(x509_store, verify_result); + + return NGX_OK; + +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; + +#endif + +#endif /* LIBRESSL_VERSION_NUMBER */ +} + + +int +ngx_stream_lua_ffi_ssl_get_verify_result(ngx_stream_lua_request_t *r, + char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + *err = "LibreSSL does not support by ssl_verify_by_lua*"; + return NGX_ERROR; +#else + +#if defined(SSL_ERROR_WANT_RETRY_VERIFY) && \ + OPENSSL_VERSION_NUMBER >= 0x40100000L + ngx_ssl_conn_t *ssl_conn; + ngx_stream_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + cctx = ngx_stream_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + x509_store = cctx->x509_store; + + return X509_STORE_CTX_get_error(x509_store); + +#else + *err = "OpenSSL too old to support this function"; + return NGX_ERROR; + +#endif + +#endif /* LIBRESSL_VERSION_NUMBER */ +} + + +void +ngx_stream_lua_ffi_ssl_free_verify_cert(void *cdata) +{ + X509 *cert = cdata; + + X509_free(cert); +} + + +void * +ngx_stream_lua_ffi_ssl_get_verify_cert(ngx_stream_lua_request_t *r, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + *err = "LibreSSL does not support by ssl_verify_by_lua*"; + return NULL; +#else + +#if defined(SSL_ERROR_WANT_RETRY_VERIFY) && \ + OPENSSL_VERSION_NUMBER >= 0x40100000L + ngx_ssl_conn_t *ssl_conn; + ngx_stream_lua_ssl_ctx_t *cctx; + X509_STORE_CTX *x509_store; + X509 *x509; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NULL; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NULL; + } + + cctx = ngx_stream_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NULL; + } + + x509_store = cctx->x509_store; + + x509 = X509_STORE_CTX_get0_cert(x509_store); + + if (!X509_up_ref(x509)) { + *err = "get verify cert failed"; + return NULL; + } + + return x509; + +#else + *err = "OpenSSL too old to support this function"; + return NULL; + +#endif + +#endif /* LIBRESSL_VERSION_NUMBER */ +} + + +#endif /* NGX_STREAM_SSL */ diff --git a/src/ngx_stream_lua_ssl_verifyby.h b/src/ngx_stream_lua_ssl_verifyby.h new file mode 100644 index 00000000..d3b7abdf --- /dev/null +++ b/src/ngx_stream_lua_ssl_verifyby.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_STREAM_LUA_SSL_VERIFYBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_SSL_VERIFYBY_H_INCLUDED_ + +#include "ngx_stream_lua_common.h" + +#if (NGX_STREAM_SSL) + +ngx_int_t ngx_stream_lua_ssl_verify_handler_inline( + ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_stream_lua_ssl_verify_handler_file( + ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_stream_lua_ssl_verify_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_stream_lua_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_stream_lua_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg); + +#endif /* NGX_STREAM_SSL */ +#endif /* _NGX_STREAM_LUA_SSL_VERIFYBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_uthread.c b/src/ngx_stream_lua_uthread.c index 1f92c8f7..dad5f84a 100644 --- a/src/ngx_stream_lua_uthread.c +++ b/src/ngx_stream_lua_uthread.c @@ -279,6 +279,7 @@ ngx_stream_lua_uthread_kill(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY #ifdef HAVE_LUA_PROXY_SSL | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY diff --git a/src/ngx_stream_lua_util.h b/src/ngx_stream_lua_util.h index e395ecc1..964fa5a3 100644 --- a/src/ngx_stream_lua_util.h +++ b/src/ngx_stream_lua_util.h @@ -77,6 +77,7 @@ extern char ngx_stream_lua_headers_metatable_key; | NGX_STREAM_LUA_CONTEXT_TIMER \ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO \ | NGX_STREAM_LUA_CONTEXT_SSL_CERT \ + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY \ | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT \ | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY) @@ -91,6 +92,7 @@ extern char ngx_stream_lua_headers_metatable_key; : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ "ssl_client_hello_by_lua*" \ : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_SSL_VERIFY ? "ssl_verify_by_lua*" \ : (c) == NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT ? \ "proxy_ssl_certificate_by_lua*" \ : (c) == NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY ? \ diff --git a/src/ngx_stream_lua_variable.c b/src/ngx_stream_lua_variable.c index fe195c3b..b97d1157 100644 --- a/src/ngx_stream_lua_variable.c +++ b/src/ngx_stream_lua_variable.c @@ -53,7 +53,8 @@ ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data, | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PROXY_SSL_VERIFY #endif - | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO)) + | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_STREAM_LUA_CONTEXT_SSL_VERIFY)) { cctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection); session = cctx->connection->data; diff --git a/t/168-ssl-verify.t b/t/168-ssl-verify.t new file mode 100644 index 00000000..431cd449 --- /dev/null +++ b/t/168-ssl-verify.t @@ -0,0 +1,970 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua::Stream; +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL (\d+)\.(\d+)\.(\d+)/) { + my ($major, $minor, $patch) = ($1, $2, $3); + + if ($major < 4 || ($major == 4 && $minor < 1)) { + plan(skip_all => "too old OpenSSL, need >= 4.1.0 was " . + "$major.$minor.$patch"); + } else { + plan tests => repeat_each() * (blocks() * 5 + 19); + } +} elsif ($openssl_version =~ m/running with BoringSSL/) { + plan(skip_all => "does not support BoringSSL"); +} else { + die "unknown SSL"; +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: ssl_verify_by_lua in stream {} block so that all servers should be ssl configured +--- stream_config + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log +no ssl configured for the server +--- no_error_log +[error] +[alert] +--- must_die + + + +=== TEST 2: ssl_verify_by_lua in server {} block but ssl_verify_client off +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client off; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log +ssl_verify_client can't be off when using ssl_verify_by_lua* +--- no_error_log +[error] +[alert] +--- must_die + + + +=== TEST 3: simple logging +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "ssl verify by lua is running!") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 4: sleep +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + return 'it works!\n'; + + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl verify by lua: ", ngx.now() - begin) + } + + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log eval +qr/elapsed in ssl verify by lua: 0.(?:09|1\d)\d+,/, +--- no_error_log +[error] +[alert] + + + +=== TEST 5: timer +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +my timer run! +ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1 +--- no_error_log +[error] +[alert] + + + +=== TEST 6: ngx.exit(0) - no yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 7: ngx.exit(ngx.ERROR) - no yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log eval +[ +'lua exit with code -1', +'ssl_verify_by_lua: handler return value: -1, cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(0) - yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 9: ngx.exit(ngx.ERROR) - yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + server_name test.com; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log eval +[ +'lua exit with code -1', +'ssl_verify_by_lua: cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 10: lua exception - no yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log eval +[ +'runtime error: ssl_verify_by_lua:2: bad bad bad', +'ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: lua exception - yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- error_log eval +[ +'runtime error: ssl_verify_by_lua:3: bad bad bad', +'ssl_verify_by_lua: cert verify callback exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 12: get phase +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + print("get_phase: ", ngx.get_phase()) + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +get_phase: ssl_verify +--- no_error_log +[error] +[alert] + + + +=== TEST 13: simple logging (by_lua_file) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_file html/a.lua; + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- user_files +>>> a.lua +print("ssl verify by lua is running!") + +--- error_log +a.lua:1: ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 14: coroutine API +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 +--- no_error_log +[error] +[alert] + + + +=== TEST 15: simple user thread wait with yielding +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + local function f() + ngx.sleep(0.01) + print("uthread: hello in thread") + return "done" + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + print("uthread: thread created: ", coroutine.status(t)) + + local ok, res = ngx.thread.wait(t) + if not ok then + print("uthread: failed to wait thread: ", res) + return + end + + print("uthread: ", res) + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: thread created: running +uthread: hello in thread +uthread: done + + + +=== TEST 16: uthread (kill) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + local function f() + ngx.log(ngx.INFO, "uthread: hello from f()") + ngx.sleep(1) + end + + local t, err = ngx.thread.spawn(f) + if not t then + ngx.log(ngx.ERR, "failed to spawn thread: ", err) + return ngx.exit(ngx.ERROR) + end + + local ok, res = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.ERR, "failed to kill thread: ", res) + return + end + + ngx.log(ngx.INFO, "uthread: killed") + + local ok, err = ngx.thread.kill(t) + if not ok then + ngx.log(ngx.INFO, "uthread: failed to kill: ", err) + end + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- no_error_log +[error] +[alert] +[emerg] +--- grep_error_log eval: qr/uthread: [^.,]+/ +--- grep_error_log_out +uthread: hello from f() +uthread: killed +uthread: failed to kill: already waited or killed + + + +=== TEST 17: ngx.exit(ngx.OK) - no yield +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.exit(ngx.OK) + ngx.log(ngx.ERR, "should never reached here...") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log eval +[ +'ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1', +qr/\[debug\] .*? SSL_do_handshake: 1/, +'lua exit with code 0', +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 18: ssl_verify_by_lua* without yield API (simple logic) +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + print("ssl verify: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("ssl verify: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("ssl verify: concatenated string: ", str) + + -- Table operations + local t = {a = 1, b = 2, c = 3} + local count = 0 + for k, v in pairs(t) do + count = count + v + end + print("ssl verify: table sum: ", count) + + print("ssl verify: simple test done") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- grep_error_log eval: qr/(ssl verify: simple test start|ssl verify: calculated sum: 55|ssl verify: concatenated string: hello world|ssl verify: table sum: 6|ssl verify: simple test done)/ +--- grep_error_log_out +ssl verify: simple test start +ssl verify: calculated sum: 55 +ssl verify: concatenated string: hello world +ssl verify: table sum: 6 +ssl verify: simple test done + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 19: lua_skip_openssl_default_verify default off +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "ssl verify by lua is running!") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +ssl_verify_by_lua: openssl default verify +--- no_error_log +[error] +[alert] + + + +=== TEST 20: lua_skip_openssl_default_verify on +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + lua_skip_openssl_default_verify on; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "ssl verify by lua is running!") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + lua_skip_openssl_default_verify on; +--- stream_response +it works! +--- error_log +ssl verify by lua is running! +--- no_error_log +ssl_verify_by_lua: openssl default verify +[error] +[alert] + + + +=== TEST 21: cosocket +--- stream_config + server { + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + + return "it works!\n"; + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + while true do + local line, err = sock:receive() + if not line then + -- ngx.log(ngx.ERR, "failed to receive response status line: ", err) + break + end + ngx.log(ngx.INFO, "received: ", line) + end + + local ok, err = sock:close() + ngx.log(ngx.INFO, "close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +connected: 1 +received: it works! +close: 1 nil +--- no_error_log +[error] +[alert] From 0857a51bdc655f7dd222624eaeb65694a256595d Mon Sep 17 00:00:00 2001 From: willmafh Date: Wed, 21 Oct 2026 23:36:17 +0800 Subject: [PATCH 2/3] update README.md for ssl_verify_by_lua* directives --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cf62587c..a185be35 100644 --- a/README.md +++ b/README.md @@ -151,11 +151,14 @@ behavior. * [ssl_client_hello_by_lua_file](https://github.com/openresty/lua-nginx-module#ssl_client_hello_by_lua_file) * [ssl_certificate_by_lua_block](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_block) * [ssl_certificate_by_lua_file](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_file) +* [ssl_verify_by_lua_block](https://github.com/openresty/lua-nginx-module#ssl_verify_by_lua_block) +* [ssl_verify_by_lua_file](https://github.com/openresty/lua-nginx-module#ssl_verify_by_lua_file) * [proxy_ssl_certificate_by_lua_block](https://github.com/openresty/lua-nginx-module#proxy_ssl_certificate_by_lua_block) * [proxy_ssl_certificate_by_lua_file](https://github.com/openresty/lua-nginx-module#proxy_ssl_certificate_by_lua_file) * [proxy_ssl_verify_by_lua_block](https://github.com/openresty/lua-nginx-module#proxy_ssl_verify_by_lua_block) * [proxy_ssl_verify_by_lua_file](https://github.com/openresty/lua-nginx-module#proxy_ssl_verify_by_lua_file) * [lua_shared_dict](https://github.com/openresty/lua-nginx-module#lua_shared_dict) +* [lua_skip_openssl_default_verify](https://github.com/openresty/lua-nginx-module#lua_skip_openssl_default_verify) * [lua_socket_connect_timeout](https://github.com/openresty/lua-nginx-module#lua_socket_connect_timeout) * [lua_socket_buffer_size](https://github.com/openresty/lua-nginx-module#lua_socket_buffer_size) * [lua_socket_pool_size](https://github.com/openresty/lua-nginx-module#lua_socket_pool_size) From bba91694aa5dce3faa573d3135defbaea13424f4 Mon Sep 17 00:00:00 2001 From: willmafh Date: Fri, 23 Oct 2026 22:32:33 +0800 Subject: [PATCH 3/3] more test cases about ssl_verify_client works with ssl_verify_by_lua* --- t/168-ssl-verify.t | 113 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/t/168-ssl-verify.t b/t/168-ssl-verify.t index 431cd449..c2190f49 100644 --- a/t/168-ssl-verify.t +++ b/t/168-ssl-verify.t @@ -14,7 +14,7 @@ if ($openssl_version =~ m/built with OpenSSL (\d+)\.(\d+)\.(\d+)/) { plan(skip_all => "too old OpenSSL, need >= 4.1.0 was " . "$major.$minor.$patch"); } else { - plan tests => repeat_each() * (blocks() * 5 + 19); + plan tests => repeat_each() * (blocks() * 5 + 20); } } elsif ($openssl_version =~ m/running with BoringSSL/) { plan(skip_all => "does not support BoringSSL"); @@ -103,7 +103,7 @@ ssl_verify_client can't be off when using ssl_verify_by_lua* -=== TEST 3: simple logging +=== TEST 3: simple logging and ssl_verify_client on --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -134,12 +134,85 @@ it works! --- error_log ssl verify by lua is running! --- no_error_log +client sent no required SSL certificate [error] [alert] -=== TEST 4: sleep +=== TEST 4: simple logging and ssl_verify_client optional +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client optional; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "ssl verify by lua is running!") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 5: simple logging and ssl_verify_client optional_no_ca +--- stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client optional_no_ca; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "ssl verify by lua is running!") + } + + return 'it works!\n'; + } +--- stream_server_config + proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl on; + proxy_ssl_name example.com; + proxy_ssl_certificate ../../cert/mtls_client.crt; + proxy_ssl_certificate_key ../../cert/mtls_client.key; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; +--- stream_response +it works! +--- error_log +ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 6: sleep --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -179,7 +252,7 @@ qr/elapsed in ssl verify by lua: 0.(?:09|1\d)\d+,/, -=== TEST 5: timer +=== TEST 7: timer --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -223,7 +296,7 @@ ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1 -=== TEST 6: ngx.exit(0) - no yield +=== TEST 8: ngx.exit(0) - no yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -262,7 +335,7 @@ should never reached here -=== TEST 7: ngx.exit(ngx.ERROR) - no yield +=== TEST 9: ngx.exit(ngx.ERROR) - no yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -302,7 +375,7 @@ should never reached here -=== TEST 8: ngx.exit(0) - yield +=== TEST 10: ngx.exit(0) - yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -343,7 +416,7 @@ should never reached here -=== TEST 9: ngx.exit(ngx.ERROR) - yield +=== TEST 11: ngx.exit(ngx.ERROR) - yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -385,7 +458,7 @@ should never reached here -=== TEST 10: lua exception - no yield +=== TEST 12: lua exception - no yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -425,7 +498,7 @@ should never reached here -=== TEST 11: lua exception - yield +=== TEST 13: lua exception - yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -466,7 +539,7 @@ should never reached here -=== TEST 12: get phase +=== TEST 14: get phase --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -502,7 +575,7 @@ get_phase: ssl_verify -=== TEST 13: simple logging (by_lua_file) +=== TEST 15: simple logging (by_lua_file) --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -540,7 +613,7 @@ a.lua:1: ssl verify by lua is running! -=== TEST 14: coroutine API +=== TEST 16: coroutine API --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -597,7 +670,7 @@ co yield: 2 -=== TEST 15: simple user thread wait with yielding +=== TEST 17: simple user thread wait with yielding --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -656,7 +729,7 @@ uthread: done -=== TEST 16: uthread (kill) +=== TEST 18: uthread (kill) --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -718,7 +791,7 @@ uthread: failed to kill: already waited or killed -=== TEST 17: ngx.exit(ngx.OK) - no yield +=== TEST 19: ngx.exit(ngx.OK) - no yield --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -760,7 +833,7 @@ should never reached here -=== TEST 18: ssl_verify_by_lua* without yield API (simple logic) +=== TEST 20: ssl_verify_by_lua* without yield API (simple logic) --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -826,7 +899,7 @@ ssl verify: simple test done -=== TEST 19: lua_skip_openssl_default_verify default off +=== TEST 21: lua_skip_openssl_default_verify default off --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -862,7 +935,7 @@ ssl_verify_by_lua: openssl default verify -=== TEST 20: lua_skip_openssl_default_verify on +=== TEST 22: lua_skip_openssl_default_verify on --- stream_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; @@ -903,7 +976,7 @@ ssl_verify_by_lua: openssl default verify -=== TEST 21: cosocket +=== TEST 23: cosocket --- stream_config server { listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;