From 89923f2f54612c2a0ea0f1a1d1e14424f5110700 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 20 Jul 2025 12:49:15 +0800 Subject: [PATCH 01/17] feature: proxy_ssl_verify_by_lua* directives working after receiving server certificates, allowing us to control upstream ssl handshake dynamically with Lua --- README.markdown | 80 ++ config | 2 + src/ngx_http_lua_common.h | 52 +- src/ngx_http_lua_control.c | 4 +- src/ngx_http_lua_ctx.c | 6 +- src/ngx_http_lua_module.c | 36 + src/ngx_http_lua_proxy_ssl_verifyby.c | 738 ++++++++++++++++++ src/ngx_http_lua_proxy_ssl_verifyby.h | 39 + src/ngx_http_lua_ssl.h | 6 +- src/ngx_http_lua_util.h | 3 + t/169-proxy-ssl-verify.t | 1002 +++++++++++++++++++++++++ 11 files changed, 1944 insertions(+), 24 deletions(-) create mode 100644 src/ngx_http_lua_proxy_ssl_verifyby.c create mode 100644 src/ngx_http_lua_proxy_ssl_verifyby.h create mode 100644 t/169-proxy-ssl-verify.t diff --git a/README.markdown b/README.markdown index 1565487d93..6e4b4015c9 100644 --- a/README.markdown +++ b/README.markdown @@ -1170,6 +1170,8 @@ Directives * [ssl_session_fetch_by_lua_file](#ssl_session_fetch_by_lua_file) * [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block) * [ssl_session_store_by_lua_file](#ssl_session_store_by_lua_file) +* [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block) +* [proxy_ssl_verify_by_lua_file](#proxy_ssl_verify_by_lua_file) * [lua_shared_dict](#lua_shared_dict) * [lua_socket_connect_timeout](#lua_socket_connect_timeout) * [lua_socket_send_timeout](#lua_socket_send_timeout) @@ -3156,6 +3158,84 @@ Note that: this directive is only allowed to used in **http context** from the ` [Back to TOC](#directives) +proxy_ssl_verify_by_lua_block +----------------------------- + +**syntax:** *proxy_ssl_verify_by_lua_block { lua-script }* + +**context:** *location* + +**phase:** *right-after-server-certificate-message-was-processed* + +This directive runs user Lua code when Nginx is about to post-process the SSL server certificate message for the upstream SSL (https) connections. + +It is particularly useful to parse upstream server certificate and do some custom operations in pure lua. + +The [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) Lua modules provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme) +library are particularly useful in this context. + +Below is a trivial example using the +[ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) module +at the same time: + +```nginx + + server { + listen 443 ssl; + server_name test.com; + ssl_certificate /path/to/cert.crt; + ssl_certificate_key /path/to/key.key; + + location /t { + proxy_ssl_certificate /path/to/cert.crt; + proxy_ssl_certificate_key /path/to/key.key; + proxy_pass https://upstream; + + proxy_ssl_verify_by_lua_block { + local proxy_ssl_vfy = require "ngx.ssl.proxysslverify" + local cert = proxy_ssl_vfy.get_verify_cert() + + -- ocsp to verify cert + -- check crl + proxy_ssl_vfy.set_verify_result() + ... + } + } + ... + } +``` + +See more information in the [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) +Lua modules' official documentation. + +Uncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the +[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`. + +This Lua code execution context *does* support yielding, so Lua APIs that may yield +(like cosockets, sleeping, and "light threads") +are enabled in this context + +Note, `ngx.ctx` in proxy_ssl_verify_by_lua_block is belonging to upstream connection, not downstream connection, so it's different from `ngx.ctx` in contexts like ssl_certificate_by_lua etc. + +This directive requires OpenSSL 3.0.2 or greater. + +[Back to TOC](#directives) + +proxy_ssl_verify_by_lua_file +---------------------------- + +**syntax:** *proxy_ssl_verify_by_lua_file <path-to-lua-script-file>* + +**context:** *location* + +**phase:** *right-after-server-certificate-message-was-processed* + +Equivalent to [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed. + +When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. + +[Back to TOC](#directives) + lua_shared_dict --------------- diff --git a/config b/config index 24ebd126d6..7b1b061362 100644 --- a/config +++ b/config @@ -296,6 +296,7 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl.c \ + $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.c \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \ $ngx_addon_dir/src/ngx_http_lua_input_filters.c \ $ngx_addon_dir/src/ngx_http_lua_pipe.c \ @@ -359,6 +360,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl.h \ + $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.h \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \ $ngx_addon_dir/src/ngx_http_lua_input_filters.h \ $ngx_addon_dir/src/ngx_http_lua_pipe.h \ diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 9db3e19ce5..e962737698 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -131,23 +131,24 @@ typedef struct { (NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) -/* must be within 16 bit */ -#define NGX_HTTP_LUA_CONTEXT_SET 0x0001 -#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x0002 -#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x0004 -#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x0008 -#define NGX_HTTP_LUA_CONTEXT_LOG 0x0010 -#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x0020 -#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x0040 -#define NGX_HTTP_LUA_CONTEXT_TIMER 0x0080 -#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x0100 -#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x0200 -#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 -#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000 -#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x4000 -#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x8000 +/* must be within 32 bits */ +#define NGX_HTTP_LUA_CONTEXT_SET 0x00000001 +#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x00000002 +#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x00000004 +#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x00000008 +#define NGX_HTTP_LUA_CONTEXT_LOG 0x00000010 +#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x00000020 +#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x00000040 +#define NGX_HTTP_LUA_CONTEXT_TIMER 0x00000080 +#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x00000100 +#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x00000200 +#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x00000400 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x00000800 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x00001000 +#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x00002000 +#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x00004000 +#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x00008000 +#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY 0x00010000 #define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100 @@ -171,6 +172,8 @@ typedef struct ngx_http_lua_srv_conf_s ngx_http_lua_srv_conf_t; typedef struct ngx_http_lua_main_conf_s ngx_http_lua_main_conf_t; +typedef struct ngx_http_lua_loc_conf_s ngx_http_lua_loc_conf_t; + typedef struct ngx_http_lua_header_val_s ngx_http_lua_header_val_t; typedef struct ngx_http_lua_posted_thread_s ngx_http_lua_posted_thread_t; @@ -184,6 +187,9 @@ typedef ngx_int_t (*ngx_http_lua_main_conf_handler_pt)(ngx_log_t *log, typedef ngx_int_t (*ngx_http_lua_srv_conf_handler_pt)(ngx_http_request_t *r, ngx_http_lua_srv_conf_t *lscf, lua_State *L); +typedef ngx_int_t (*ngx_http_lua_loc_conf_handler_pt)(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + typedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); @@ -369,7 +375,7 @@ struct ngx_http_lua_srv_conf_s { }; -typedef struct { +struct ngx_http_lua_loc_conf_s { #if (NGX_HTTP_SSL) ngx_ssl_t *ssl; /* shared by SSL cosockets */ ngx_array_t *ssl_certificates; @@ -383,6 +389,12 @@ typedef struct { #if (nginx_version >= 1019004) ngx_array_t *ssl_conf_commands; #endif + + ngx_http_lua_loc_conf_handler_pt proxy_ssl_verify_handler; + ngx_str_t proxy_ssl_verify_src; + u_char *proxy_ssl_verify_src_key; + u_char *proxy_ssl_verify_chunkname; + int proxy_ssl_verify_src_ref; #endif ngx_flag_t force_read_body; /* whether force request body to @@ -464,7 +476,7 @@ typedef struct { ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; ngx_flag_t use_default_type; -} ngx_http_lua_loc_conf_t; +}; typedef enum { @@ -628,7 +640,7 @@ typedef struct ngx_http_lua_ctx_s { int uthreads; /* number of active user threads */ - uint16_t context; /* the current running directive context + uint32_t context; /* the current running directive context (or running phase) for the current Lua chunk */ diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index d7e427385d..7f2e4fffd0 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -384,6 +384,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE @@ -394,7 +395,8 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, return NGX_ERROR; } - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY + | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) diff --git a/src/ngx_http_lua_ctx.c b/src/ngx_http_lua_ctx.c index d5431be724..2f06ed04d6 100644 --- a/src/ngx_http_lua_ctx.c +++ b/src/ngx_http_lua_ctx.c @@ -87,7 +87,8 @@ ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase, return ctx->ctx_ref; } - *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY + | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE); @@ -123,7 +124,8 @@ ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref) } #if (NGX_HTTP_SSL) - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY + | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE)) diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index cae92b21ee..b2fcb3f724 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -31,6 +31,7 @@ #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl_session_storeby.h" #include "ngx_http_lua_ssl_session_fetchby.h" +#include "ngx_http_lua_proxy_ssl_verifyby.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_headers_out.h" #if !(NGX_WIN32) @@ -660,6 +661,21 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_ssl_sess_fetch_handler_file }, + /* same context as proxy_pass directive */ + { ngx_string("proxy_ssl_verify_by_lua_block"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_proxy_ssl_verify_by_lua_block, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_verify_handler_inline }, + + { ngx_string("proxy_ssl_verify_by_lua_file"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_lua_proxy_ssl_verify_by_lua, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_verify_handler_file }, + { ngx_string("lua_ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -1446,6 +1462,11 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) * conf->ssl_trusted_certificate = { 0, NULL }; * conf->ssl_crl = { 0, NULL }; * conf->ssl_key_log = { 0, NULL }; + * + * conf->proxy_ssl_verify_handler = NULL; + * conf->proxy_ssl_verify_src = { 0, NULL }; + * conf->proxy_ssl_verify_chunkname = NULL; + * conf->proxy_ssl_verify_src_key = NULL; */ conf->force_read_body = NGX_CONF_UNSET; @@ -1479,6 +1500,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) #if (nginx_version >= 1019004) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif + conf->proxy_ssl_verify_src_ref = LUA_REFNIL; #endif return conf; @@ -1573,6 +1595,20 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) NULL); #endif + if (conf->proxy_ssl_verify_src.len == 0) { + conf->proxy_ssl_verify_src = prev->proxy_ssl_verify_src; + conf->proxy_ssl_verify_handler = prev->proxy_ssl_verify_handler; + conf->proxy_ssl_verify_src_ref = prev->proxy_ssl_verify_src_ref; + conf->proxy_ssl_verify_src_key = prev->proxy_ssl_verify_src_key; + conf->proxy_ssl_verify_chunkname = prev->proxy_ssl_verify_chunkname; + } + + if (conf->proxy_ssl_verify_src.len) { + if (ngx_http_lua_proxy_ssl_verify_set_callback(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c new file mode 100644 index 0000000000..b7ea5bb4a0 --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -0,0 +1,738 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_HTTP_SSL) + + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_proxy_ssl_verifyby.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl.h" + + +static void ngx_http_lua_proxy_ssl_verify_done(void *data); +static void ngx_http_lua_proxy_ssl_verify_aborted(void *data); +static u_char *ngx_http_lua_log_proxy_ssl_verify_error(ngx_log_t *log, + u_char *buf, size_t len); +static ngx_int_t ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, + ngx_http_request_t *r); + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf) +{ + +#ifdef LIBRESSL_VERSION_NUMBER + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support by proxy_ssl_verify_by_lua*"); + + return NGX_ERROR; + +#else + + ngx_flag_t proxy_pass_ssl = 0; + ngx_pool_cleanup_t *cln; + ngx_ssl_t *ssl; + void *plcf; + + /* + * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly + * get plcf here, and we also don't want to change ngx_http_proxy_module's + * code organization, since that it means to add a header file to Nginx. + * I know it's a bit clumsy here, anyway the solution is good enough + */ + for (cln = cf->pool->cleanup; cln; cln = cln->next) { + if (cln->handler != ngx_ssl_cleanup_ctx) { + continue; + } + + ssl = cln->data; + + plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); + if (plcf == ngx_ssl_get_server_conf(ssl->ctx)) { + /* here we make sure that ssl is plcf->upstream.ssl */ + proxy_pass_ssl = 1; + + break; + } + } + + if (!proxy_pass_ssl) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "proxy_ssl_verify_by_lua* " + "should be used with proxy_pass https url"); + + return NGX_ERROR; + } + +#if (!defined SSL_ERROR_WANT_RETRY_VERIFY \ + || OPENSSL_VERSION_NUMBER < 0x30000020L) + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "OpenSSL too old to support " + "proxy_ssl_verify_by_lua*"); + + return NGX_ERROR; + +#else + + SSL_CTX_set_cert_verify_callback(ssl->ctx, + ngx_http_lua_proxy_ssl_verify_handler, + NULL); + return NGX_OK; + +#endif + +#endif +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadfile(r->connection->log, L, + llcf->proxy_ssl_verify_src.data, + &llcf->proxy_ssl_verify_src_ref, + llcf->proxy_ssl_verify_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, + llcf->proxy_ssl_verify_src.data, + llcf->proxy_ssl_verify_src.len, + &llcf->proxy_ssl_verify_src_ref, + llcf->proxy_ssl_verify_src_key, + (const char *) llcf->proxy_ssl_verify_chunkname); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r); +} + + +char * +ngx_http_lua_proxy_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_http_lua_proxy_ssl_verify_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if (!defined SSL_ERROR_WANT_RETRY_VERIFY \ + || OPENSSL_VERSION_NUMBER < 0x30000020L) + + /* SSL_set_retry_verify() was added in OpenSSL 3.0.2 */ + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 3.0.2 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + size_t chunkname_len; + u_char *chunkname; + u_char *cache_key = NULL; + u_char *name; + ngx_str_t *value; + ngx_http_lua_loc_conf_t *llcf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (llcf->proxy_ssl_verify_handler) { + return "is duplicate"; + } + + if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + llcf->proxy_ssl_verify_handler = + (ngx_http_lua_loc_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_proxy_ssl_verify_handler_file) { + /* Lua code in an external file */ + + name = ngx_http_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + llcf->proxy_ssl_verify_src.data = name; + llcf->proxy_ssl_verify_src.len = ngx_strlen(name); + + } else { + cache_key = ngx_http_lua_gen_chunk_cache_key(cf, + "proxy_ssl_verify_by_lua", + value[1].data, + value[1].len); + if (cache_key == NULL) { + return NGX_CONF_ERROR; + } + + chunkname = ngx_http_lua_gen_chunk_name(cf, "proxy_ssl_verify_by_lua", + sizeof("proxy_ssl_verify_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ + llcf->proxy_ssl_verify_src = value[1]; + llcf->proxy_ssl_verify_chunkname = chunkname; + } + + llcf->proxy_ssl_verify_src_key = cache_key; + + return NGX_CONF_OK; + +#endif /* SSL_ERROR_WANT_RETRY_VERIFY */ +} + + +int +ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_http_request_t *r = NULL, *fr = NULL; + ngx_pool_cleanup_t *cln; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_ssl_ctx_t *cctx; + ngx_http_core_srv_conf_t *cscf; + 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); /* connection to upstream */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy ssl verify: connection reusable: %ud", c->reusable); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("proxy ssl verify handler, cert-verify-ctx=%p", cctx); + + if (cctx && cctx->entered_proxy_ssl_verify_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: " + "cert verify callback exit code: %d", + cctx->exit_code); + + dd("lua proxy ssl verify done, finally"); + return cctx->exit_code; + } + + return SSL_set_retry_verify(ssl_conn); + } + + dd("first time"); + +#if (nginx_version < 1017009) + ngx_reusable_connection(c, 0); +#endif + + r = c->data; + + fc = ngx_http_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_http_lua_log_proxy_ssl_verify_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + fr = ngx_http_lua_create_fake_request(fc); + if (fr == NULL) { + goto failed; + } + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + fr->main_conf = cscf->ctx->main_conf; + fr->srv_conf = cscf->ctx->srv_conf; + /* + * the hook is running after find config phase, and r->loc_conf may + * already been changed, we need to get correct location configs + */ + fr->loc_conf = r->loc_conf; + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + clcf = ngx_http_get_module_loc_conf(fr, ngx_http_core_module); + +#if nginx_version >= 1009000 + ngx_set_connection_log(fc, clcf->error_log); +#else + ngx_http_set_connection_log(fc, clcf->error_log); +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + + cctx->ctx_ref = LUA_NOREF; + } + + cctx->exit_code = 1; /* successful by default */ + cctx->x509_store = x509_store; + cctx->connection = c; + cctx->request = fr; + cctx->entered_proxy_ssl_verify_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) + == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + llcf = ngx_http_get_module_loc_conf(fr, ngx_http_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(fr, NULL); + + c->log->action = "loading proxy ssl verify by lua"; + + if (llcf->proxy_ssl_verify_handler == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "no proxy_ssl_verify_by_lua* defined in " + "server %V", &cscf->server_name); + + goto failed; + } + + rc = llcf->proxy_ssl_verify_handler(fr, llcf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: handler return value: %i, " + "cert verify callback exit code: %d", rc, cctx->exit_code); + + c->log->action = "proxy pass 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_http_lua_proxy_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_http_lua_proxy_ssl_verify_aborted; + + return SSL_set_retry_verify(ssl_conn); + +failed: + + if (fr && fr->pool) { + ngx_http_lua_free_fake_request(fr); + } + + if (fc) { + ngx_http_lua_close_fake_connection(fc); + } + + return 0; /* verify failure or error */ +} + + +static void +ngx_http_lua_proxy_ssl_verify_done(void *data) +{ + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl verify done"); + + if (cctx->aborted) { + return; + } + + ngx_http_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "proxy pass SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_http_lua_proxy_ssl_verify_aborted(void *data) +{ + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl verify aborted"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, + "proxy_ssl_verify_by_lua: cert verify callback aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_http_lua_log_proxy_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: proxy_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_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_http_lua_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_http_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_http_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + rc = NGX_ERROR; + ngx_http_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_http_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_http_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_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_http_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_http_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_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, + int verify_result, char **err) +{ +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_ssl_conn_t *ssl_conn; + ngx_http_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; + } + + dd("get cctx session"); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + 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 +} + + +int +ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) +{ +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_ssl_conn_t *ssl_conn; + ngx_http_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; + } + + dd("get cctx session"); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + 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 +} + + +void * +ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) +{ +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_ssl_conn_t *ssl_conn; + ngx_http_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; + } + + dd("get cctx session"); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx == NULL) { + *err = "bad lua context"; + return NULL; + } + + x509_store = cctx->x509_store; + + x509 = X509_STORE_CTX_get0_cert(x509_store); + + return x509; +#else + *err = "OpenSSL too old to support this function"; + + return NULL; +#endif +} + +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.h b/src/ngx_http_lua_proxy_ssl_verifyby.h new file mode 100644 index 0000000000..946fd0d2ba --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_verifyby.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#if (NGX_HTTP_SSL) + +/* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */ +extern ngx_module_t ngx_http_proxy_module; + +ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + +ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + +char *ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, + void *arg); + +ngx_int_t ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf); + +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index 1ee0a3626c..43dcaf9557 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -24,9 +24,12 @@ typedef struct { ngx_str_t session_id; + X509_STORE_CTX *x509_store; + int exit_code; /* exit code for openssl's set_client_hello_cb or - set_cert_cb callback */ + set_cert_cb callback or + SSL_CTX_set_cert_verify_callback */ int ctx_ref; /* reference to anchor request ctx data in lua @@ -38,6 +41,7 @@ typedef struct { unsigned entered_client_hello_handler:1; unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; + unsigned entered_proxy_ssl_verify_handler:1; } ngx_http_lua_ssl_ctx_t; diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index d76508868c..af7dc5f762 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -37,6 +37,7 @@ | NGX_HTTP_LUA_CONTEXT_ACCESS \ | NGX_HTTP_LUA_CONTEXT_CONTENT \ | NGX_HTTP_LUA_CONTEXT_TIMER \ + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY \ | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO \ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) @@ -59,6 +60,8 @@ : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY ? \ + "proxy_ssl_verify_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ "ssl_client_hello_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t new file mode 100644 index 0000000000..9133858d32 --- /dev/null +++ b/t/169-proxy-ssl-verify.t @@ -0,0 +1,1002 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +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 (0\S*|1\.0\S*|1\.1\.0\S*)/) { + plan(skip_all => "too old OpenSSL, need 1.1.1, was $1"); +} elsif ($openssl_version =~ m/running with BoringSSL/) { + plan(skip_all => "does not support BoringSSL"); +} elsif ($ENV{TEST_NGINX_USE_HTTP3}) { + plan tests => repeat_each() * (blocks() * 6 + 6); +} else { + plan tests => repeat_each() * (blocks() * 6 + 10); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; +$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: invalid proxy_pass url +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + } +--- request +GET /t +--- error_log +proxy_ssl_verify_by_lua* should be used with proxy_pass https url +--- must_die + + + +=== TEST 2: proxy_ssl_verify_by_lua in http {} block +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } +--- config + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- request +GET /t +--- error_log +"proxy_ssl_verify_by_lua_block" directive is not allowed here +--- must_die + + + +=== TEST 3: proxy_ssl_verify_by_lua in server {} block +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } + +--- config + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + + location /t { + proxy_pass http://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + } +--- request +GET /t +--- error_log +"proxy_ssl_verify_by_lua_block" directive is not allowed here +--- must_die + + + +=== TEST 4: simple logging +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +proxy ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 5: sleep +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("sleep") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in proxy ssl verify by lua: ", ngx.now() - begin) + } + } +--- request +GET /t +--- response_body +sleep +--- error_log eval +qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+,/, +--- no_error_log +[error] +[alert] + + + +=== TEST 6: timer +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("timer") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_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 + } + } +--- request +GET /t +--- response_body +timer +--- error_log +my timer run! +--- no_error_log +[error] +[alert] + + + +=== TEST 7: ngx.exit(0) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(0) no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(0) no yield +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.ERROR) no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'lua exit with code -1', +'proxy_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 +[error] +[alert] +[emerg] + + + +=== TEST 9: ngx.exit(0) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(0) yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(0) yield +--- error_log +lua exit with code 0 +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 10: ngx.exit(ngx.ERROR) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.ERROR) yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'lua exit with code -1', +'proxy_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 +[error] +[alert] +[emerg] + + + +=== TEST 11: lua exception - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua exception - no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:63):2: bad bad bad', +'proxy_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 12: lua exception - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua exception - yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:63):3: bad bad bad', +'proxy_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 13: get phase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("get phase return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + print("get_phase: ", ngx.get_phase()) + } + } +--- request +GET /t +--- response_body +get phase return +--- error_log +get_phase: proxy_ssl_verify +--- no_error_log +[error] +[alert] + + + +=== TEST 14: subrequests disabled +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("subrequests disabled") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.location.capture("/foo") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'proxy_ssl_verify_by_lua(nginx.conf:63):2: API disabled in the context of proxy_ssl_verify_by_lua*', +'proxy_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 +[alert] + + + +=== TEST 15: simple logging (by_lua_file) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging by lua file") + } + + more_clear_headers Date; + } + } +--- user_files +>>> a.lua +print("proxy ssl verify by lua is running!") + +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_file html/a.lua; + } +--- request +GET /t +--- response_body +simple logging by lua file +--- error_log +a.lua:1: proxy ssl verify by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 16: coroutine API +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("coroutine API") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_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 + } + } +--- request +GET /t +--- response_body +coroutine API +--- 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 17: simple user thread wait with yielding +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple user thread wait with yielding") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_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) + } + } +--- request +GET /t +--- response_body +simple user thread wait with yielding +--- 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 18: uthread (kill) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("uthread (kill)") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_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 + } + } +--- request +GET /t +--- response_body +uthread (kill) +--- 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 19: ngx.exit(ngx.OK) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("ngx.exit(ngx.OK) - no yield") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + ngx.exit(ngx.OK) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- response_body +ngx.exit(ngx.OK) - no yield +--- error_log eval +[ +'proxy_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 20: proxy_ssl_verify_by_lua* without yield API (simple logic) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("without yield API, simple logic") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + + proxy_ssl_verify_by_lua_block { + print("proxy ssl verify: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("proxy ssl verify: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("proxy 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("proxy ssl verify: table sum: ", count) + + print("proxy ssl verify: simple test done") + } + } +--- request +GET /t +--- response_body +without yield API, simple logic +--- grep_error_log eval: qr/(proxy ssl verify: simple test start|proxy ssl verify: calculated sum: 55|proxy ssl verify: concatenated string: hello world|proxy ssl verify: table sum: 6|proxy ssl verify: simple test done)/ +--- grep_error_log_out +proxy ssl verify: simple test start +proxy ssl verify: calculated sum: 55 +proxy ssl verify: concatenated string: hello world +proxy ssl verify: table sum: 6 +proxy ssl verify: simple test done + +--- no_error_log +[error] +[alert] +[emerg] From 58dda9a60c4a13752883816e3d13c1cd8b4e6386 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 20 Jul 2025 16:11:07 +0800 Subject: [PATCH 02/17] proxy_ssl_verify_by_lua ffi functions build fix --- src/ngx_http_lua_proxy_ssl_verifyby.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index b7ea5bb4a0..e5234ce0b1 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -622,6 +622,7 @@ ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, { #ifdef SSL_ERROR_WANT_RETRY_VERIFY ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; @@ -638,6 +639,8 @@ ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, dd("get cctx session"); + c = ngx_ssl_get_connection(ssl_conn); + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); if (cctx == NULL) { *err = "bad lua context"; @@ -662,6 +665,7 @@ ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; @@ -678,6 +682,8 @@ ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) dd("get cctx session"); + c = ngx_ssl_get_connection(ssl_conn); + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); if (cctx == NULL) { *err = "bad lua context"; @@ -700,6 +706,7 @@ ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; X509 *x509; @@ -717,6 +724,8 @@ ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) dd("get cctx session"); + c = ngx_ssl_get_connection(ssl_conn); + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); if (cctx == NULL) { *err = "bad lua context"; From 0292dcd2ae8e13e374926b66b14650088fc9a35b Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 20 Jul 2025 16:27:11 +0800 Subject: [PATCH 03/17] feature: lua_upstream_skip_openssl_default_verify directive to control whether to skip openssl's default verify function --- README.markdown | 16 +++++ doc/HttpLuaModule.wiki | 13 ++++ src/ngx_http_lua_common.h | 1 + src/ngx_http_lua_module.c | 11 ++++ src/ngx_http_lua_proxy_ssl_verifyby.c | 11 ++++ t/169-proxy-ssl-verify.t | 91 +++++++++++++++++++++++++++ 6 files changed, 143 insertions(+) diff --git a/README.markdown b/README.markdown index 6e4b4015c9..1e7f2d917b 100644 --- a/README.markdown +++ b/README.markdown @@ -1190,6 +1190,7 @@ Directives * [lua_ssl_verify_depth](#lua_ssl_verify_depth) * [lua_ssl_key_log](#lua_ssl_key_log) * [lua_ssl_conf_command](#lua_ssl_conf_command) +* [lua_upstream_skip_openssl_default_verify](#lua_upstream_skip_openssl_default_verify) * [lua_http10_buffering](#lua_http10_buffering) * [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) * [access_by_lua_no_postpone](#access_by_lua_no_postpone) @@ -3570,6 +3571,21 @@ This directive was first introduced in the `v0.10.21` release. +[Back to TOC](#directives) + +lua_upstream_skip_openssl_default_verify +-------------------- + +**syntax:** *lua_upstream_skip_openssl_default_verify on|off* + +**default:** *lua_upstream_skip_openssl_default_verify off* + +**context:** *location, location-if* + +When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate. + +This directive is turned `off` by default. + [Back to TOC](#directives) lua_http10_buffering diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 69e1a86303..09af86f771 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -2960,6 +2960,19 @@ Note though that configuring OpenSSL directly with lua_ssl_conf_commandv0.10.21 release. +== lua_upstream_skip_openssl_default_verify == + +'''syntax:''' ''lua_upstream_skip_openssl_default_verify on|off'' + +'''default:''' ''lua_upstream_skip_openssl_default_verify off'' + +'''context:''' ''location, location-if'' + +When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate. + +This directive is turned off by default. + +[Back to TOC](#directives) == lua_http10_buffering == diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index e962737698..544404d657 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -395,6 +395,7 @@ struct ngx_http_lua_loc_conf_s { u_char *proxy_ssl_verify_src_key; u_char *proxy_ssl_verify_chunkname; int proxy_ssl_verify_src_ref; + ngx_flag_t upstream_skip_openssl_default_verify; #endif ngx_flag_t force_read_body; /* whether force request body to diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index b2fcb3f724..f4214bee87 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -676,6 +676,13 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_proxy_ssl_verify_handler_file }, + { ngx_string("lua_upstream_skip_openssl_default_verify"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, upstream_skip_openssl_default_verify), + NULL }, + { ngx_string("lua_ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -1501,6 +1508,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif conf->proxy_ssl_verify_src_ref = LUA_REFNIL; + conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET; #endif return conf; @@ -1609,6 +1617,9 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) } } + ngx_conf_merge_value(conf->upstream_skip_openssl_default_verify, + prev->upstream_skip_openssl_default_verify, 0); + if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index e5234ce0b1..110e95c3b0 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -298,6 +298,17 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) r = c->data; + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->upstream_skip_openssl_default_verify == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_verify_by_lua: openssl default verify"); + + rc = X509_verify_cert(x509_store); + if (rc == 0) { + return 0; /* verify failure or error */ + } + } + fc = ngx_http_lua_create_fake_connection(NULL); if (fc == NULL) { goto failed; diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 9133858d32..632c4b874a 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -1000,3 +1000,94 @@ proxy ssl verify: simple test done [error] [alert] [emerg] + + + +=== TEST 21: lua_upstream_skip_openssl_default_verify default off +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua_upstream_skip_openssl_default_verify default off") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + proxy_ssl_conf_command VerifyMode Peer; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- error_code: 502 +--- response_body_like: 502 Bad Gateway +--- error_log eval +[ +'proxy_ssl_verify_by_lua: openssl default verify', +qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, +] +--- no_error_log +[error] +[alert] + + + +=== TEST 22: lua_upstream_skip_openssl_default_verify on +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test2.crt; + ssl_certificate_key ../../cert/test2.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("lua_upstream_skip_openssl_default_verify default off") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_certificate ../../cert/test.crt; + proxy_ssl_certificate_key ../../cert/test.key; + proxy_ssl_session_reuse off; + # proxy_ssl_conf_command VerifyMode Peer; + lua_upstream_skip_openssl_default_verify on; + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") + } + } +--- request +GET /t +--- response_body +lua_upstream_skip_openssl_default_verify default off +--- error_log +proxy ssl verify by lua is running! +--- no_error_log +proxy_ssl_verify_by_lua: openssl default verify +[error] +[alert] From fe7bf4c3f28336a83196217bebb5d6b4a494377f Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 20 Jul 2025 20:20:22 +0800 Subject: [PATCH 04/17] optimize: better way to get server certificate of upstream SSL connection --- src/ngx_http_lua_proxy_ssl_verifyby.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 110e95c3b0..d267549227 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -712,6 +712,15 @@ ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) } +void +ngx_http_lua_ffi_ssl_free_verify_cert(void *cdata) +{ + X509 *cert = cdata; + + X509_free(cert); +} + + void * ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) { @@ -747,6 +756,11 @@ ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) x509 = X509_STORE_CTX_get0_cert(x509_store); + if (!X509_up_ref(x509)) { + *err = "get verify result failed"; + return NULL; + } + return x509; #else *err = "OpenSSL too old to support this function"; From e655d5380874bc72546c2c414d3705375414462b Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 21 Jul 2025 20:59:19 +0800 Subject: [PATCH 05/17] adjust where to process upstream skip openssl default verify --- src/ngx_http_lua_proxy_ssl_verifyby.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index d267549227..62444b262a 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -298,17 +298,6 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) r = c->data; - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->upstream_skip_openssl_default_verify == 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "proxy_ssl_verify_by_lua: openssl default verify"); - - rc = X509_verify_cert(x509_store); - if (rc == 0) { - return 0; /* verify failure or error */ - } - } - fc = ngx_http_lua_create_fake_connection(NULL); if (fc == NULL) { goto failed; @@ -372,6 +361,15 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) } llcf = ngx_http_get_module_loc_conf(fr, ngx_http_lua_module); + if (llcf->upstream_skip_openssl_default_verify == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_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_http_lua_get_lua_vm(fr, NULL); From eef820703baa38a49fb9f7d4bb2deefdec8d7929 Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 21 Jul 2025 20:59:47 +0800 Subject: [PATCH 06/17] chore: code style --- src/ngx_http_lua_proxy_ssl_verifyby.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 62444b262a..f8f1fa000c 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -353,8 +353,8 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) dd("setting cctx"); - if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) - == 0) + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, + cctx) == 0) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); goto failed; From c774bd0f3512810822b89b252f224291c6c793af Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 21 Jul 2025 21:29:13 +0800 Subject: [PATCH 07/17] tests: update t/169-proxy-ssl-verify.t for client to successfully verify server certificate --- t/169-proxy-ssl-verify.t | 347 ++++++++++++++++++++++----------------- 1 file changed, 192 insertions(+), 155 deletions(-) diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 632c4b874a..32a74c47cc 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -132,8 +132,8 @@ GET /t listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -147,10 +147,13 @@ GET /t } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; proxy_ssl_verify_by_lua_block { ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") @@ -172,10 +175,9 @@ proxy ssl verify by lua is running! --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -189,10 +191,13 @@ proxy ssl verify by lua is running! } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; proxy_ssl_verify_by_lua_block { local begin = ngx.now() @@ -216,10 +221,9 @@ qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+,/, --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -233,10 +237,13 @@ qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+,/, } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; proxy_ssl_verify_by_lua_block { local function f() @@ -265,10 +272,9 @@ my timer run! --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -282,10 +288,13 @@ my timer run! } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; proxy_ssl_verify_by_lua_block { ngx.exit(0) @@ -310,10 +319,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -327,11 +335,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.exit(ngx.ERROR) @@ -341,7 +352,6 @@ should never reached here --- request GET /t --- error_code: 502 ---- response_body_like: 502 Bad Gateway --- error_log eval [ 'lua exit with code -1', @@ -360,10 +370,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -377,10 +386,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.sleep(0.001) @@ -407,10 +420,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -424,11 +436,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.sleep(0.001) @@ -440,7 +455,6 @@ should never reached here --- request GET /t --- error_code: 502 ---- response_body_like: 502 Bad Gateway --- error_log eval [ 'lua exit with code -1', @@ -459,10 +473,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -476,11 +489,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { error("bad bad bad") @@ -490,10 +506,9 @@ should never reached here --- request GET /t --- error_code: 502 ---- response_body_like: 502 Bad Gateway --- error_log eval [ -'runtime error: proxy_ssl_verify_by_lua(nginx.conf:63):2: bad bad bad', +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):2: bad bad bad', 'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0', qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, ] @@ -508,10 +523,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -525,11 +539,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.sleep(0.001) @@ -540,10 +557,9 @@ should never reached here --- request GET /t --- error_code: 502 ---- response_body_like: 502 Bad Gateway --- error_log eval [ -'runtime error: proxy_ssl_verify_by_lua(nginx.conf:63):3: bad bad bad', +'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):3: bad bad bad', 'proxy_ssl_verify_by_lua: cert verify callback exit code: 0', qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, ] @@ -558,10 +574,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -575,10 +590,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { print("get_phase: ", ngx.get_phase()) @@ -600,10 +619,9 @@ get_phase: proxy_ssl_verify --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -617,11 +635,14 @@ get_phase: proxy_ssl_verify } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.location.capture("/foo") @@ -630,10 +651,9 @@ get_phase: proxy_ssl_verify --- request GET /t --- error_code: 502 ---- response_body_like: 502 Bad Gateway --- error_log eval [ -'proxy_ssl_verify_by_lua(nginx.conf:63):2: API disabled in the context of proxy_ssl_verify_by_lua*', +'proxy_ssl_verify_by_lua(nginx.conf:65):2: API disabled in the context of proxy_ssl_verify_by_lua*', 'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0', qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, ] @@ -646,10 +666,9 @@ qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -667,11 +686,14 @@ print("proxy ssl verify by lua is running!") --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_file html/a.lua; } @@ -691,10 +713,9 @@ a.lua:1: proxy ssl verify by lua is running! --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -708,10 +729,14 @@ a.lua:1: proxy ssl verify by lua is running! } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield @@ -754,10 +779,9 @@ co yield: 2 --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -771,10 +795,14 @@ co yield: 2 } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { local function f() @@ -819,10 +847,9 @@ uthread: done --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -836,10 +863,14 @@ uthread: done } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { local function f() @@ -887,10 +918,9 @@ uthread: failed to kill: already waited or killed --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -904,10 +934,14 @@ uthread: failed to kill: already waited or killed } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.exit(ngx.OK) @@ -935,10 +969,9 @@ should never reached here --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -952,10 +985,14 @@ should never reached here } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { print("proxy ssl verify: simple test start") @@ -1007,10 +1044,9 @@ proxy ssl verify: simple test done --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -1024,11 +1060,14 @@ proxy ssl verify: simple test done } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { ngx.log(ngx.INFO, "proxy ssl verify by lua is running!") @@ -1036,13 +1075,8 @@ proxy ssl verify: simple test done } --- request GET /t ---- error_code: 502 ---- response_body_like: 502 Bad Gateway ---- error_log eval -[ -'proxy_ssl_verify_by_lua: openssl default verify', -qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, -] +--- error_log +proxy_ssl_verify_by_lua: openssl default verify --- no_error_log [error] [alert] @@ -1053,10 +1087,9 @@ qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, --- http_config server { listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; - server_name test.com; - ssl_certificate ../../cert/test2.crt; - ssl_certificate_key ../../cert/test2.key; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; location / { default_type 'text/plain'; @@ -1070,11 +1103,15 @@ qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/, } --- config location /t { - proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; - proxy_ssl_certificate ../../cert/test.crt; - proxy_ssl_certificate_key ../../cert/test.key; - proxy_ssl_session_reuse off; - # proxy_ssl_conf_command VerifyMode Peer; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_ssl_conf_command VerifyMode Peer; + lua_upstream_skip_openssl_default_verify on; proxy_ssl_verify_by_lua_block { From 3923d542d299a572f04bdc1b5c82f60eca733008 Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 21 Jul 2025 21:39:55 +0800 Subject: [PATCH 08/17] chore: test case code style --- t/169-proxy-ssl-verify.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 32a74c47cc..9a687cfd8e 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -496,7 +496,7 @@ should never reached here proxy_ssl_certificate_key ../../cert/mtls_client.key; proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; proxy_ssl_session_reuse off; - proxy_ssl_conf_command VerifyMode Peer; + proxy_ssl_conf_command VerifyMode Peer; proxy_ssl_verify_by_lua_block { error("bad bad bad") From 744bebfdebbefd084eb72f75ae4d75553274d9a8 Mon Sep 17 00:00:00 2001 From: willmafh Date: Thu, 14 Aug 2025 12:52:06 +0800 Subject: [PATCH 09/17] better way to get proxy ssl context --- src/ngx_http_lua_proxy_ssl_verifyby.c | 30 +++++++-------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index f8f1fa000c..b3a13727de 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -42,34 +42,20 @@ ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf) #else - ngx_flag_t proxy_pass_ssl = 0; - ngx_pool_cleanup_t *cln; - ngx_ssl_t *ssl; - void *plcf; + void *plcf; + ngx_http_upstream_conf_t *ucf; + ngx_ssl_t *ssl; /* * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly - * get plcf here, and we also don't want to change ngx_http_proxy_module's - * code organization, since that it means to add a header file to Nginx. - * I know it's a bit clumsy here, anyway the solution is good enough + * get plcf here, but the first member of plcf is ngx_http_upstream_conf_t */ - for (cln = cf->pool->cleanup; cln; cln = cln->next) { - if (cln->handler != ngx_ssl_cleanup_ctx) { - continue; - } - - ssl = cln->data; - - plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); - if (plcf == ngx_ssl_get_server_conf(ssl->ctx)) { - /* here we make sure that ssl is plcf->upstream.ssl */ - proxy_pass_ssl = 1; + plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); + ucf = plcf; - break; - } - } + ssl = ucf->ssl; - if (!proxy_pass_ssl) { + if (!ssl->ctx) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "proxy_ssl_verify_by_lua* " "should be used with proxy_pass https url"); From e23517eba33ee87033330c45c18c8afb7f00d550 Mon Sep 17 00:00:00 2001 From: willmafh Date: Fri, 15 Aug 2025 16:14:45 +0800 Subject: [PATCH 10/17] changes so that we can use ngx.ctx to pass data from downstream phases to upstream phases & its related test case --- src/ngx_http_lua_ctx.c | 3 +- src/ngx_http_lua_proxy_ssl_verifyby.c | 9 +++++ t/169-proxy-ssl-verify.t | 49 +++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_ctx.c b/src/ngx_http_lua_ctx.c index 2f06ed04d6..2f61fe6d8a 100644 --- a/src/ngx_http_lua_ctx.c +++ b/src/ngx_http_lua_ctx.c @@ -87,8 +87,7 @@ ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase, return ctx->ctx_ref; } - *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY - | NGX_HTTP_LUA_CONTEXT_SSL_CERT + *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE); diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index b3a13727de..43eb74dba0 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -244,6 +244,7 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) ngx_pool_cleanup_t *cln; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_ctx_t *ctx; ngx_http_lua_ssl_ctx_t *cctx; ngx_http_core_srv_conf_t *cscf; ngx_ssl_conn_t *ssl_conn; @@ -308,6 +309,14 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) * already been changed, we need to get correct location configs */ fr->loc_conf = r->loc_conf; + /* + * so that we can use ngx.ctx to pass data from downstream phases to + * upstream phases if there is any + */ + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx) { + ngx_http_set_ctx(fr, ctx, ngx_http_lua_module); + } fc->log->file = c->log->file; fc->log->log_level = c->log->log_level; diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 9a687cfd8e..539f90a193 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -1128,3 +1128,52 @@ proxy ssl verify by lua is running! proxy_ssl_verify_by_lua: openssl default verify [error] [alert] + + + +=== TEST 23: ngx.ctx to pass data from downstream phase to upstream phase +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + + rewrite_by_lua_block { + ngx.ctx.greeting = "I am from rewrite phase" + } + + proxy_ssl_verify_by_lua_block { + ngx.log(ngx.INFO, "greeting: ", ngx.ctx.greeting) + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +greeting: I am from rewrite phase +--- no_error_log +[error] +[alert] From 1a5c4f5e486264cc9710a3d6f56038344514b448 Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 25 Aug 2025 18:29:07 +0800 Subject: [PATCH 11/17] delete unnecessary proxy ssl verify context macro --- src/ngx_http_lua_ctx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ngx_http_lua_ctx.c b/src/ngx_http_lua_ctx.c index 2f61fe6d8a..d5431be724 100644 --- a/src/ngx_http_lua_ctx.c +++ b/src/ngx_http_lua_ctx.c @@ -123,8 +123,7 @@ ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref) } #if (NGX_HTTP_SSL) - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY - | NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE)) From 1de837f2331414e014f849b1867d8d04289f5e81 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sat, 13 Sep 2025 17:05:40 +0800 Subject: [PATCH 12/17] refactor: using real request and connection to implement proxy ssl verify instead of fake request and connection. --- src/ngx_http_lua_proxy_ssl_verifyby.c | 179 ++++++++++---------------- src/ngx_http_lua_ssl.h | 5 +- src/ngx_http_lua_util.c | 37 ++++++ t/169-proxy-ssl-verify.t | 14 +- 4 files changed, 116 insertions(+), 119 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 43eb74dba0..c9bb24c75c 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -23,8 +23,6 @@ static void ngx_http_lua_proxy_ssl_verify_done(void *data); static void ngx_http_lua_proxy_ssl_verify_aborted(void *data); -static u_char *ngx_http_lua_log_proxy_ssl_verify_error(ngx_log_t *log, - u_char *buf, size_t len); static ngx_int_t ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r); @@ -239,10 +237,9 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) { lua_State *L; ngx_int_t rc; - ngx_connection_t *c, *fc; - ngx_http_request_t *r = NULL, *fr = NULL; + ngx_connection_t *c; + ngx_http_request_t *r = NULL; ngx_pool_cleanup_t *cln; - ngx_http_core_loc_conf_t *clcf; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_http_lua_ssl_ctx_t *cctx; @@ -252,7 +249,7 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) 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); /* connection to upstream */ + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "proxy ssl verify: connection reusable: %ud", c->reusable); @@ -285,51 +282,6 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) r = c->data; - fc = ngx_http_lua_create_fake_connection(NULL); - if (fc == NULL) { - goto failed; - } - - fc->log->handler = ngx_http_lua_log_proxy_ssl_verify_error; - fc->log->data = fc; - - fc->addr_text = c->addr_text; - fc->listening = c->listening; - - fr = ngx_http_lua_create_fake_request(fc); - if (fr == NULL) { - goto failed; - } - - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - fr->main_conf = cscf->ctx->main_conf; - fr->srv_conf = cscf->ctx->srv_conf; - /* - * the hook is running after find config phase, and r->loc_conf may - * already been changed, we need to get correct location configs - */ - fr->loc_conf = r->loc_conf; - /* - * so that we can use ngx.ctx to pass data from downstream phases to - * upstream phases if there is any - */ - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx) { - ngx_http_set_ctx(fr, ctx, ngx_http_lua_module); - } - - fc->log->file = c->log->file; - fc->log->log_level = c->log->log_level; - fc->ssl = c->ssl; - - clcf = ngx_http_get_module_loc_conf(fr, ngx_http_core_module); - -#if nginx_version >= 1009000 - ngx_set_connection_log(fc, clcf->error_log); -#else - ngx_http_set_connection_log(fc, clcf->error_log); -#endif - if (cctx == NULL) { cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); if (cctx == NULL) { @@ -339,12 +291,17 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) cctx->ctx_ref = LUA_NOREF; } - cctx->exit_code = 1; /* successful by default */ - cctx->x509_store = x509_store; cctx->connection = c; - cctx->request = fr; - cctx->entered_proxy_ssl_verify_handler = 1; + cctx->request = r; + cctx->x509_store = x509_store; + cctx->exit_code = 1; /* successful by default */ + cctx->original_request_count = r->main->count; cctx->done = 0; + cctx->entered_proxy_ssl_verify_handler = 1; + cctx->pool = ngx_create_pool(128, c->log); + if (cctx->pool == NULL) { + goto failed; + } dd("setting cctx"); @@ -355,7 +312,7 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) goto failed; } - llcf = ngx_http_get_module_loc_conf(fr, ngx_http_lua_module); + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->upstream_skip_openssl_default_verify == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "proxy_ssl_verify_by_lua: openssl default verify"); @@ -367,11 +324,12 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) } /* TODO honor lua_code_cache off */ - L = ngx_http_lua_get_lua_vm(fr, NULL); + L = ngx_http_lua_get_lua_vm(r, NULL); c->log->action = "loading proxy ssl verify by lua"; if (llcf->proxy_ssl_verify_handler == NULL) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ngx_log_error(NGX_LOG_ALERT, c->log, 0, "no proxy_ssl_verify_by_lua* defined in " "server %V", &cscf->server_name); @@ -379,7 +337,7 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) goto failed; } - rc = llcf->proxy_ssl_verify_handler(fr, llcf, L); + rc = llcf->proxy_ssl_verify_handler(r, llcf, L); if (rc >= NGX_OK || rc == NGX_ERROR) { cctx->done = 1; @@ -398,7 +356,7 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) /* rc == NGX_DONE */ - cln = ngx_pool_cleanup_add(fc->pool, 0); + cln = ngx_pool_cleanup_add(cctx->pool, 0); if (cln == NULL) { goto failed; } @@ -421,13 +379,8 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) return SSL_set_retry_verify(ssl_conn); failed: - - if (fr && fr->pool) { - ngx_http_lua_free_fake_request(fr); - } - - if (fc) { - ngx_http_lua_close_fake_connection(fc); + if (cctx && cctx->pool) { + ngx_destroy_pool(cctx->pool); } return 0; /* verify failure or error */ @@ -456,6 +409,14 @@ ngx_http_lua_proxy_ssl_verify_done(void *data) c = cctx->connection; + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + c->log->action = "proxy pass SSL handshaking"; ngx_post_event(c->write, &ngx_posted_events); @@ -477,45 +438,10 @@ ngx_http_lua_proxy_ssl_verify_aborted(void *data) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, "proxy_ssl_verify_by_lua: cert verify callback aborted"); - cctx->aborted = 1; - cctx->request->connection->ssl = NULL; - - ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR); -} - - -static u_char * -ngx_http_lua_log_proxy_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: proxy_ssl_verify_by_lua*"); - len -= p - buf; - buf = p; + ngx_http_lua_finalize_request(cctx->request, NGX_ERROR); - 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; + cctx->aborted = 1; + cctx->connection->ssl = NULL; } @@ -527,6 +453,9 @@ ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r) lua_State *co; ngx_http_lua_ctx_t *ctx; ngx_pool_cleanup_t *cln; + ngx_http_upstream_t *u; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); @@ -580,7 +509,11 @@ ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r) /* register request cleanup hooks */ if (ctx->cleanup == NULL) { - cln = ngx_pool_cleanup_add(r->pool, 0); + u = r->upstream; + c = u->peer.connection; + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + cln = ngx_pool_cleanup_add(cctx->pool, 0); if (cln == NULL) { rc = NGX_ERROR; ngx_http_lua_finalize_request(r, rc); @@ -625,17 +558,25 @@ ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, int verify_result, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; ngx_ssl_conn_t *ssl_conn; ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; - if (r->connection == NULL || r->connection->ssl == NULL) { + u = r->upstream; + if (u == NULL) { *err = "bad request"; return NGX_ERROR; } - ssl_conn = r->connection->ssl->connection; + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; if (ssl_conn == NULL) { *err = "bad ssl conn"; return NGX_ERROR; @@ -668,17 +609,25 @@ int ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; ngx_ssl_conn_t *ssl_conn; ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; - if (r->connection == NULL || r->connection->ssl == NULL) { + u = r->upstream; + if (u == NULL) { *err = "bad request"; return NGX_ERROR; } - ssl_conn = r->connection->ssl->connection; + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; if (ssl_conn == NULL) { *err = "bad ssl conn"; return NGX_ERROR; @@ -718,18 +667,26 @@ void * ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY + ngx_http_upstream_t *u; ngx_ssl_conn_t *ssl_conn; ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; X509_STORE_CTX *x509_store; X509 *x509; - if (r->connection == NULL || r->connection->ssl == NULL) { + u = r->upstream; + if (u == NULL) { *err = "bad request"; return NULL; } - ssl_conn = r->connection->ssl->connection; + c = u->peer.connection; + if (c == NULL || c->ssl == NULL) { + *err = "bad upstream connection"; + return NULL; + } + + ssl_conn = c->ssl->connection; if (ssl_conn == NULL) { *err = "bad ssl conn"; return NULL; diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index 43dcaf9557..b49f41fad5 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -16,7 +16,7 @@ typedef struct { ngx_connection_t *connection; /* original true connection */ - ngx_http_request_t *request; /* fake request */ + ngx_http_request_t *request; ngx_pool_cleanup_pt *cleanup; ngx_ssl_session_t *session; /* return value for openssl's @@ -25,6 +25,7 @@ typedef struct { ngx_str_t session_id; X509_STORE_CTX *x509_store; + ngx_pool_t *pool; int exit_code; /* exit code for openssl's set_client_hello_cb or @@ -35,6 +36,8 @@ typedef struct { request ctx data in lua registry */ + /* same size as count field of ngx_http_request_t */ + unsigned original_request_count:16; unsigned done:1; unsigned aborted:1; diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index d47c691000..83b6dcded2 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1682,6 +1682,9 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; done: + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + return NGX_OK; + } if (ctx->entered_content_phase && r->connection->fd != (ngx_socket_t) -1) @@ -2438,6 +2441,10 @@ ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, return ctx->exit_code; } + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + return ctx->exit_code; + } + #if 1 if (!r->header_sent && !ctx->header_sent @@ -3673,12 +3680,42 @@ void ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_http_lua_ctx_t *ctx; +#if (NGX_HTTP_SSL) + ngx_http_upstream_t *u; + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx; +#endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx && ctx->cur_co_ctx) { ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); } +#if (NGX_HTTP_SSL) + u = r->upstream; + if (u) { + c = u->peer.connection; + if (c && c->ssl) { + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx && cctx->pool) { + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + cctx->exit_code = 0; + } + + if (r->main->count > cctx->original_request_count) { + r->main->count--; + return; + } + + ngx_destroy_pool(cctx->pool); + cctx->pool = NULL; + + return; + } + } + } +#endif + if (r->connection->fd != (ngx_socket_t) -1) { ngx_http_finalize_request(r, rc); return; diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 539f90a193..013029fcdb 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -210,7 +210,7 @@ GET /t --- response_body sleep --- error_log eval -qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+,/, +qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+ while loading proxy ssl verify by lua,/, --- no_error_log [error] [alert] @@ -837,9 +837,9 @@ simple user thread wait with yielding [alert] --- grep_error_log eval: qr/uthread: [^.,]+/ --- grep_error_log_out -uthread: thread created: running -uthread: hello in thread -uthread: done +uthread: thread created: running while loading proxy ssl verify by lua +uthread: hello in thread while loading proxy ssl verify by lua +uthread: done while loading proxy ssl verify by lua @@ -908,9 +908,9 @@ uthread (kill) [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 +uthread: hello from f() while loading proxy ssl verify by lua +uthread: killed while loading proxy ssl verify by lua +uthread: failed to kill: already waited or killed while loading proxy ssl verify by lua From a7d8ed19791148bbcfdc9e0025dc604314d52d6c Mon Sep 17 00:00:00 2001 From: lijunlong Date: Sun, 14 Sep 2025 19:17:51 +0800 Subject: [PATCH 13/17] fixed build --- src/ngx_http_lua_proxy_ssl_verifyby.c | 1 - t/169-proxy-ssl-verify.t | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index c9bb24c75c..4af98767dc 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -241,7 +241,6 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) ngx_http_request_t *r = NULL; ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; - ngx_http_lua_ctx_t *ctx; ngx_http_lua_ssl_ctx_t *cctx; ngx_http_core_srv_conf_t *cscf; ngx_ssl_conn_t *ssl_conn; diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 013029fcdb..00a32f1d36 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -15,7 +15,7 @@ if ($openssl_version =~ m/built with OpenSSL (0\S*|1\.0\S*|1\.1\.0\S*)/) { } elsif ($ENV{TEST_NGINX_USE_HTTP3}) { plan tests => repeat_each() * (blocks() * 6 + 6); } else { - plan tests => repeat_each() * (blocks() * 6 + 10); + plan tests => repeat_each() * (blocks() * 5 + 10); } $ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); From bfdc5a3c867e0d162c906105a0a36af46245f1d5 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 14 Sep 2025 22:12:06 +0800 Subject: [PATCH 14/17] chore: code cleanup --- src/ngx_http_lua_proxy_ssl_verifyby.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 4af98767dc..bf1de38933 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -242,7 +242,6 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ssl_ctx_t *cctx; - ngx_http_core_srv_conf_t *cscf; ngx_ssl_conn_t *ssl_conn; ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, @@ -327,15 +326,6 @@ ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg) c->log->action = "loading proxy ssl verify by lua"; - if (llcf->proxy_ssl_verify_handler == NULL) { - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "no proxy_ssl_verify_by_lua* defined in " - "server %V", &cscf->server_name); - - goto failed; - } - rc = llcf->proxy_ssl_verify_handler(r, llcf, L); if (rc >= NGX_OK || rc == NGX_ERROR) { From c4888f818919d9174462c2f71314b51c020b047e Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 14 Sep 2025 22:35:44 +0800 Subject: [PATCH 15/17] macro to conditional build proxy ssl verify --- src/ngx_http_lua_common.h | 6 ++++ src/ngx_http_lua_control.c | 8 +++-- src/ngx_http_lua_module.c | 10 +++++++ src/ngx_http_lua_proxy_ssl_verifyby.c | 42 +++++++++++++++++++++++++- src/ngx_http_lua_proxy_ssl_verifyby.h | 2 ++ src/ngx_http_lua_ssl.h | 6 ++++ src/ngx_http_lua_util.c | 8 +++++ src/ngx_http_lua_util.h | 43 +++++++++++++++++++++++++++ 8 files changed, 122 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 544404d657..40c330baff 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -148,7 +148,10 @@ typedef struct { #define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x00002000 #define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x00004000 #define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x00008000 + +#ifdef HAVE_PROXY_SSL_PATCH #define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY 0x00010000 +#endif #define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100 @@ -390,12 +393,15 @@ struct ngx_http_lua_loc_conf_s { ngx_array_t *ssl_conf_commands; #endif +#ifdef HAVE_PROXY_SSL_PATCH ngx_http_lua_loc_conf_handler_pt proxy_ssl_verify_handler; ngx_str_t proxy_ssl_verify_src; u_char *proxy_ssl_verify_src_key; u_char *proxy_ssl_verify_chunkname; int proxy_ssl_verify_src_ref; ngx_flag_t upstream_skip_openssl_default_verify; +#endif + #endif ngx_flag_t force_read_body; /* whether force request body to diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 7f2e4fffd0..8358abcbb6 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -384,7 +384,9 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER +#ifdef HAVE_PROXY_SSL_PATCH | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY +#endif | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE @@ -395,8 +397,10 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, return NGX_ERROR; } - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY - | NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT +#ifdef HAVE_PROXY_SSL_PATCH + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY +#endif | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index f4214bee87..16726abbe1 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -31,7 +31,11 @@ #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl_session_storeby.h" #include "ngx_http_lua_ssl_session_fetchby.h" + +#ifdef HAVE_PROXY_SSL_PATCH #include "ngx_http_lua_proxy_ssl_verifyby.h" +#endif + #include "ngx_http_lua_headers.h" #include "ngx_http_lua_headers_out.h" #if !(NGX_WIN32) @@ -661,6 +665,7 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_ssl_sess_fetch_handler_file }, +#ifdef HAVE_PROXY_SSL_PATCH /* same context as proxy_pass directive */ { ngx_string("proxy_ssl_verify_by_lua_block"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, @@ -682,6 +687,7 @@ static ngx_command_t ngx_http_lua_cmds[] = { NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, upstream_skip_openssl_default_verify), NULL }, +#endif { ngx_string("lua_ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -1507,8 +1513,10 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) #if (nginx_version >= 1019004) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif +#ifdef HAVE_PROXY_SSL_PATCH conf->proxy_ssl_verify_src_ref = LUA_REFNIL; conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET; +#endif #endif return conf; @@ -1603,6 +1611,7 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) NULL); #endif +#ifdef HAVE_PROXY_SSL_PATCH if (conf->proxy_ssl_verify_src.len == 0) { conf->proxy_ssl_verify_src = prev->proxy_ssl_verify_src; conf->proxy_ssl_verify_handler = prev->proxy_ssl_verify_handler; @@ -1619,6 +1628,7 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream_skip_openssl_default_verify, prev->upstream_skip_openssl_default_verify, 0); +#endif if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index bf1de38933..82370a1be3 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -16,10 +16,12 @@ #include "ngx_http_lua_util.h" #include "ngx_http_ssl_module.h" #include "ngx_http_lua_contentby.h" -#include "ngx_http_lua_proxy_ssl_verifyby.h" #include "ngx_http_lua_directive.h" #include "ngx_http_lua_ssl.h" +#ifdef HAVE_PROXY_SSL_PATCH +#include "ngx_http_lua_proxy_ssl_verifyby.h" + static void ngx_http_lua_proxy_ssl_verify_done(void *data); static void ngx_http_lua_proxy_ssl_verify_aborted(void *data); @@ -708,4 +710,42 @@ ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) #endif } + +#else /* HAVE_PROXY_SSL_PATCH */ + + +int +ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, + int verify_result, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +void +ngx_http_lua_ffi_ssl_free_verify_cert(void *cdata) +{ +} + + +void * +ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NULL; +} + +#endif /* HAVE_PROXY_SSL_PATCH */ #endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.h b/src/ngx_http_lua_proxy_ssl_verifyby.h index 946fd0d2ba..3e0b178dee 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.h +++ b/src/ngx_http_lua_proxy_ssl_verifyby.h @@ -10,6 +10,7 @@ #if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH /* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */ extern ngx_module_t ngx_http_proxy_module; @@ -31,6 +32,7 @@ int ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, ngx_int_t ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf); +#endif /* HAVE_PROXY_SSL_PATCH */ #endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index b49f41fad5..f709e6530f 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -24,8 +24,10 @@ typedef struct { ngx_str_t session_id; +#ifdef HAVE_PROXY_SSL_PATCH X509_STORE_CTX *x509_store; ngx_pool_t *pool; +#endif int exit_code; /* exit code for openssl's set_client_hello_cb or @@ -36,15 +38,19 @@ typedef struct { request ctx data in lua registry */ +#ifdef HAVE_PROXY_SSL_PATCH /* same size as count field of ngx_http_request_t */ unsigned original_request_count:16; +#endif unsigned done:1; unsigned aborted:1; unsigned entered_client_hello_handler:1; unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; +#ifdef HAVE_PROXY_SSL_PATCH unsigned entered_proxy_ssl_verify_handler:1; +#endif } ngx_http_lua_ssl_ctx_t; diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 83b6dcded2..74f112fb6d 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1682,9 +1682,11 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; done: +#ifdef HAVE_PROXY_SSL_PATCH if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { return NGX_OK; } +#endif if (ctx->entered_content_phase && r->connection->fd != (ngx_socket_t) -1) @@ -2441,9 +2443,11 @@ ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, return ctx->exit_code; } +#ifdef HAVE_PROXY_SSL_PATCH if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { return ctx->exit_code; } +#endif #if 1 if (!r->header_sent @@ -3681,9 +3685,11 @@ ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_http_lua_ctx_t *ctx; #if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH ngx_http_upstream_t *u; ngx_connection_t *c; ngx_http_lua_ssl_ctx_t *cctx; +#endif #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); @@ -3692,6 +3698,7 @@ ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) } #if (NGX_HTTP_SSL) +#ifdef HAVE_PROXY_SSL_PATCH u = r->upstream; if (u) { c = u->peer.connection; @@ -3714,6 +3721,7 @@ ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) } } } +#endif #endif if (r->connection->fd != (ngx_socket_t) -1) { diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index af7dc5f762..8bdc894fe5 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -32,6 +32,8 @@ #define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE 8 +#ifdef HAVE_PROXY_SSL_PATCH + #define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE \ | NGX_HTTP_LUA_CONTEXT_ACCESS \ @@ -42,11 +44,26 @@ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) +#else + +#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ + | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE \ + | NGX_HTTP_LUA_CONTEXT_ACCESS \ + | NGX_HTTP_LUA_CONTEXT_CONTENT \ + | NGX_HTTP_LUA_CONTEXT_TIMER \ + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO \ + | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) + +#endif /* HAVE_PROXY_SSL_PATCH */ + /* key in Lua vm registry for all the "ngx.ctx" tables */ #define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables" +#ifdef HAVE_PROXY_SSL_PATCH + #define ngx_http_lua_context_name(c) \ ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*" \ @@ -71,6 +88,32 @@ "ssl_session_fetch_by_lua*" \ : "(unknown)") +#else + +#define ngx_http_lua_context_name(c) \ + ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE ? "server_rewrite_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? "access_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? "log_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_HEADER_FILTER ? "header_filter_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_BODY_FILTER ? "body_filter_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer" \ + : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ + "ssl_client_hello_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ? \ + "ssl_session_store_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ? \ + "ssl_session_fetch_by_lua*" \ + : "(unknown)") + +#endif /* HAVE_PROXY_SSL_PATCH */ + #define ngx_http_lua_check_context(L, ctx, flags) \ if (!((ctx)->context & (flags))) { \ From b0f2c30750e34fc2116e3b4fa74ac425abf67d76 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sat, 20 Sep 2025 16:20:20 +0800 Subject: [PATCH 16/17] refactor: upstream connection aborted handling process & related test case --- src/ngx_http_lua_proxy_ssl_verifyby.c | 7 ++-- t/169-proxy-ssl-verify.t | 47 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 82370a1be3..82ebb356cc 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -429,10 +429,13 @@ ngx_http_lua_proxy_ssl_verify_aborted(void *data) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, "proxy_ssl_verify_by_lua: cert verify callback aborted"); - ngx_http_lua_finalize_request(cctx->request, NGX_ERROR); - cctx->aborted = 1; cctx->connection->ssl = NULL; + cctx->exit_code = 0; + if (cctx->pool) { + ngx_destroy_pool(cctx->pool); + cctx->pool = NULL; + } } diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 00a32f1d36..70e4a46bf1 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -1177,3 +1177,50 @@ greeting: I am from rewrite phase --- no_error_log [error] [alert] + + + +=== TEST 24: upstream connection aborted +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("hello world") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + proxy_connect_timeout 100ms; + + proxy_ssl_verify_by_lua_block { + ngx.sleep(0.2) + } + } +--- request +GET /t +--- error_code: 504 +--- response_body_like: 504 Gateway Time-out +--- error_log +upstream timed out (110: Connection timed out) while loading proxy ssl verify by lua +proxy_ssl_verify_by_lua: cert verify callback aborted +--- no_error_log +[alert] +--- wait: 0.5 From c495c6196d25e73aacf21fd27503ed4c1fcf14bf Mon Sep 17 00:00:00 2001 From: willmafh Date: Sat, 20 Sep 2025 17:17:42 +0800 Subject: [PATCH 17/17] proxy ssl verify cosocket test case --- t/169-proxy-ssl-verify.t | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/t/169-proxy-ssl-verify.t b/t/169-proxy-ssl-verify.t index 70e4a46bf1..4c44a8b9be 100644 --- a/t/169-proxy-ssl-verify.t +++ b/t/169-proxy-ssl-verify.t @@ -1224,3 +1224,109 @@ proxy_ssl_verify_by_lua: cert verify callback aborted --- no_error_log [alert] --- wait: 0.5 + + + +=== TEST 25: cosocket +--- http_config + server { + listen *:80; + server_name test.com; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { + ngx.sleep(0.1) + + ngx.status = 201 + ngx.say("foo") + ngx.exit(201) + } + more_clear_headers Date; + } + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + + location / { + default_type 'text/plain'; + + content_by_lua_block { + ngx.say("simple logging return") + } + + more_clear_headers Date; + } + } +--- config + location /t { + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock; + proxy_ssl_verify 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; + + proxy_ssl_verify_by_lua_block { + do + local sock = ngx.socket.tcp() + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", "80") + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send http request: ", err) + return + end + + ngx.log(ngx.INFO, "sent http request: ", bytes, " bytes.") + + 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() + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +connected: 1 +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: openresty +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil +--- no_error_log +[error] +[alert]