From 89923f2f54612c2a0ea0f1a1d1e14424f5110700 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 20 Jul 2025 12:49:15 +0800 Subject: [PATCH 01/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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] From 25847450e74e5cf1cf89a7bf12dd587f1f371fa3 Mon Sep 17 00:00:00 2001 From: willmafh Date: Tue, 14 Oct 2025 12:13:38 +0800 Subject: [PATCH 18/22] better prefix for proxy ssl verify ffi functions --- src/ngx_http_lua_proxy_ssl_verifyby.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_proxy_ssl_verifyby.c b/src/ngx_http_lua_proxy_ssl_verifyby.c index 82ebb356cc..1695e56ffb 100644 --- a/src/ngx_http_lua_proxy_ssl_verifyby.c +++ b/src/ngx_http_lua_proxy_ssl_verifyby.c @@ -548,7 +548,7 @@ ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r) * which can be done using X509_STORE_CTX_set_error. */ int -ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, +ngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r, int verify_result, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY @@ -600,7 +600,7 @@ ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, int -ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) +ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY ngx_http_upstream_t *u; @@ -649,7 +649,7 @@ 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) +ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata) { X509 *cert = cdata; @@ -658,7 +658,7 @@ 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) +ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err) { #ifdef SSL_ERROR_WANT_RETRY_VERIFY ngx_http_upstream_t *u; @@ -718,7 +718,7 @@ ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) int -ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, +ngx_http_lua_ffi_proxy_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"; @@ -728,7 +728,7 @@ ngx_http_lua_ffi_ssl_set_verify_result(ngx_http_request_t *r, int -ngx_http_lua_ffi_ssl_get_verify_result(ngx_http_request_t *r, char **err) +ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err) { *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; @@ -737,13 +737,13 @@ 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) +ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata) { } void * -ngx_http_lua_ffi_ssl_get_verify_cert(ngx_http_request_t *r, char **err) +ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err) { *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; From 59be320c93fc01673e1c9675e40d2d541a6620ba Mon Sep 17 00:00:00 2001 From: willmafh Date: Tue, 14 Oct 2025 23:05:47 +0800 Subject: [PATCH 19/22] feature: proxy_ssl_certificate_by_lua directives --- README.markdown | 90 ++ config | 2 + src/ngx_http_lua_common.h | 7 + src/ngx_http_lua_control.c | 2 + src/ngx_http_lua_module.c | 36 + src/ngx_http_lua_proxy_ssl_certby.c | 980 +++++++++++++++ src/ngx_http_lua_proxy_ssl_certby.h | 40 + src/ngx_http_lua_ssl.h | 1 + src/ngx_http_lua_util.c | 6 +- src/ngx_http_lua_util.h | 3 + t/170-proxy-ssl-cert.t | 1743 +++++++++++++++++++++++++++ 11 files changed, 2908 insertions(+), 2 deletions(-) create mode 100644 src/ngx_http_lua_proxy_ssl_certby.c create mode 100644 src/ngx_http_lua_proxy_ssl_certby.h create mode 100644 t/170-proxy-ssl-cert.t diff --git a/README.markdown b/README.markdown index 8c6edec844..5f35e16981 100644 --- a/README.markdown +++ b/README.markdown @@ -1171,6 +1171,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_certificate_by_lua_block](#proxy_ssl_certificate_by_lua_block) +* [proxy_ssl_certificate_by_lua_file](#proxy_ssl_certificate_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) @@ -3168,6 +3170,94 @@ Note that: this directive is only allowed to used in **http context** from the ` [Back to TOC](#directives) +proxy_ssl_certificate_by_lua_block +---------------------------------- + +**syntax:** *proxy_ssl_certificate_by_lua_block { lua-script }* + +**context:** *location* + +**phase:** *right-after-server-certificate-request-message-was-processed* + +This directive runs user Lua code when Nginx is about to post-process the SSL server certificate request message from upstream. It is particularly useful for setting the SSL certificate chain and the corresponding private key for the upstream SSL (https) connections. It is also useful to load such handshake configurations nonblockingly from the remote (for example, with the [cosocket](#ngxsockettcp) API). + +The [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) Lua module 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](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md) module and the [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.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_pass https://upstream; + + proxy_ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local proxy_ssl_cert = require "ngx.ssl.proxysslcert" + + -- NOTE: for illustration only, we don't handle error below + + local f = assert(io.open("/path/to/cert.crt")) + local cert_data = f:read("*a") + f:close() + + local cert, err = ssl.parse_pem_cert(cert_data) + local ok, err = proxy_ssl_cert.set_cert(cert) + + local f = assert(io.open("/path/to/key.key")) + local pkey_data = f:read("*a") + f:close() + + local pkey, err = ssl.parse_pem_priv_key(pkey_data) + local ok, err = proxy_ssl_cert.set_priv_key(pkey) + -- ... + } + } + ... + } +``` + +See more information in the [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) Lua module's 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 that, unlike the relations between the [ssl_certificate](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate) and [ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key) directives and [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block), the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives can be used together with [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block). + +* When there are only [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives, the original Nginx behavior will obviously remain the same. + +* When there is only [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block), Nginx will send the certificate and its related private key and chain set by Lua codes. + +* When the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives and [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) are used at the same time, then [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) will take precedence over the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives. + +Please refer to corresponding test case file and [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) for more details. + +This directive requires OpenSSL 1.0.2e or greater. + +[Back to TOC](#directives) + +proxy_ssl_certificate_by_lua_file +--------------------------------- + +**syntax:** *proxy_ssl_certificate_by_lua_file <path-to-lua-script-file>* + +**context:** *location* + +**phase:** *right-after-server-certificate-request-message-was-processed* + +Equivalent to [proxy_ssl_certificate_by_lua_block](#proxy_ssl_certificate_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) + proxy_ssl_verify_by_lua_block ----------------------------- diff --git a/config b/config index 7b1b061362..bde43348f7 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_certby.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 \ @@ -360,6 +361,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_certby.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 \ diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 40c330baff..f46b66c0c0 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -151,6 +151,7 @@ typedef struct { #ifdef HAVE_PROXY_SSL_PATCH #define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY 0x00010000 +#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT 0x00020000 #endif @@ -394,6 +395,12 @@ struct ngx_http_lua_loc_conf_s { #endif #ifdef HAVE_PROXY_SSL_PATCH + ngx_http_lua_loc_conf_handler_pt proxy_ssl_cert_handler; + ngx_str_t proxy_ssl_cert_src; + u_char *proxy_ssl_cert_src_key; + u_char *proxy_ssl_cert_chunkname; + int proxy_ssl_cert_src_ref; + 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; diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 8358abcbb6..b59a7058b0 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -385,6 +385,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER #ifdef HAVE_PROXY_SSL_PATCH + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY #endif | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO @@ -399,6 +400,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT #ifdef HAVE_PROXY_SSL_PATCH + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY #endif | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 16726abbe1..cd297abd30 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -33,6 +33,7 @@ #include "ngx_http_lua_ssl_session_fetchby.h" #ifdef HAVE_PROXY_SSL_PATCH +#include "ngx_http_lua_proxy_ssl_certby.h" #include "ngx_http_lua_proxy_ssl_verifyby.h" #endif @@ -666,6 +667,21 @@ static ngx_command_t ngx_http_lua_cmds[] = { (void *) ngx_http_lua_ssl_sess_fetch_handler_file }, #ifdef HAVE_PROXY_SSL_PATCH + /* same context as proxy_pass directive */ + { ngx_string("proxy_ssl_certificate_by_lua_block"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_proxy_ssl_cert_by_lua_block, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_cert_handler_inline }, + + { ngx_string("proxy_ssl_certificate_by_lua_file"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_lua_proxy_ssl_cert_by_lua, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) ngx_http_lua_proxy_ssl_cert_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, @@ -1476,6 +1492,11 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) * conf->ssl_crl = { 0, NULL }; * conf->ssl_key_log = { 0, NULL }; * + * conf->proxy_ssl_cert_handler = NULL; + * conf->proxy_ssl_cert_src = { 0, NULL }; + * conf->proxy_ssl_cert_chunkname = NULL; + * conf->proxy_ssl_cert_src_key = NULL; + * * conf->proxy_ssl_verify_handler = NULL; * conf->proxy_ssl_verify_src = { 0, NULL }; * conf->proxy_ssl_verify_chunkname = NULL; @@ -1514,6 +1535,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif #ifdef HAVE_PROXY_SSL_PATCH + conf->proxy_ssl_cert_src_ref = LUA_REFNIL; conf->proxy_ssl_verify_src_ref = LUA_REFNIL; conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET; #endif @@ -1612,6 +1634,20 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif #ifdef HAVE_PROXY_SSL_PATCH + if (conf->proxy_ssl_cert_src.len == 0) { + conf->proxy_ssl_cert_src = prev->proxy_ssl_cert_src; + conf->proxy_ssl_cert_handler = prev->proxy_ssl_cert_handler; + conf->proxy_ssl_cert_src_ref = prev->proxy_ssl_cert_src_ref; + conf->proxy_ssl_cert_src_key = prev->proxy_ssl_cert_src_key; + conf->proxy_ssl_cert_chunkname = prev->proxy_ssl_cert_chunkname; + } + + if (conf->proxy_ssl_cert_src.len) { + if (ngx_http_lua_proxy_ssl_cert_set_callback(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + 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; diff --git a/src/ngx_http_lua_proxy_ssl_certby.c b/src/ngx_http_lua_proxy_ssl_certby.c new file mode 100644 index 0000000000..bbb95874d0 --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_certby.c @@ -0,0 +1,980 @@ +/* + * 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_directive.h" +#include "ngx_http_lua_ssl.h" + +#ifdef HAVE_PROXY_SSL_PATCH +#include "ngx_http_lua_proxy_ssl_certby.h" + + +static void ngx_http_lua_proxy_ssl_cert_done(void *data); +static void ngx_http_lua_proxy_ssl_cert_aborted(void *data); +static ngx_int_t ngx_http_lua_proxy_ssl_cert_by_chunk(lua_State *L, + ngx_http_request_t *r); + + +ngx_int_t +ngx_http_lua_proxy_ssl_cert_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_certificate_by_lua*"); + + return NGX_ERROR; + +#else + + 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, but the first member of plcf is ngx_http_upstream_conf_t + */ + plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); + ucf = plcf; + + ssl = ucf->ssl; + + if (!ssl->ctx) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "proxy_ssl_certificate_by_lua* should be used with " + "proxy_pass https url"); + + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER >= 0x1000205fL + + SSL_CTX_set_cert_cb(ssl->ctx, ngx_http_lua_proxy_ssl_cert_handler, NULL); + + return NGX_OK; + +#else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "OpenSSL too old to support " + "proxy_ssl_certificate_by_lua*"); + + return NGX_ERROR; + +#endif + +#endif +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_cert_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_cert_src.data, + &llcf->proxy_ssl_cert_src_ref, + llcf->proxy_ssl_cert_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_cert_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_proxy_ssl_cert_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_cert_src.data, + llcf->proxy_ssl_cert_src.len, + &llcf->proxy_ssl_cert_src_ref, + llcf->proxy_ssl_cert_src_key, + (const char *) llcf->proxy_ssl_cert_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_cert_by_chunk(L, r); +} + + +char * +ngx_http_lua_proxy_ssl_cert_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_cert_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_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if OPENSSL_VERSION_NUMBER < 0x1000205fL + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 1.0.2e 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_cert_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_cert_handler = + (ngx_http_lua_loc_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_proxy_ssl_cert_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_cert_src.data = name; + llcf->proxy_ssl_cert_src.len = ngx_strlen(name); + + } else { + cache_key = ngx_http_lua_gen_chunk_cache_key(cf, + "proxy_ssl_certificate_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_certificate_by_lua", + sizeof("proxy_ssl_certificate_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + /* Don't eval nginx variables for inline lua code */ + llcf->proxy_ssl_cert_src = value[1]; + llcf->proxy_ssl_cert_chunkname = chunkname; + } + + llcf->proxy_ssl_cert_src_key = cache_key; + + return NGX_CONF_OK; + +#endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +} + + +int +ngx_http_lua_proxy_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_ssl_ctx_t *cctx; + + c = ngx_ssl_get_connection(ssl_conn); /* upstream connection */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy ssl cert: connection reusable: %ud", c->reusable); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("proxy ssl cert handler, cert-ctx=%p", cctx); + + if (cctx && cctx->entered_proxy_ssl_cert_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "proxy_ssl_certificate_by_lua: " + "cert cb exit code: %d", + cctx->exit_code); + + dd("lua proxy ssl cert done, finally"); + return cctx->exit_code; + } + + return -1; + } + + dd("first time"); + +#if (nginx_version < 1017009) + ngx_reusable_connection(c, 0); +#endif + + r = c->data; + + 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->connection = c; + cctx->request = r; + cctx->exit_code = 1; /* successful by default */ + cctx->original_request_count = r->main->count; + cctx->done = 0; + cctx->entered_proxy_ssl_cert_handler = 1; + cctx->pool = ngx_create_pool(128, c->log); + if (cctx->pool == NULL) { + goto failed; + } + + 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(r, ngx_http_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(r, NULL); + + c->log->action = "loading proxy ssl certificate by lua"; + + rc = llcf->proxy_ssl_cert_handler(r, 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_certificate_by_lua: " + "handler return value: %i, cert cb 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(cctx->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_lua_proxy_ssl_cert_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_cert_aborted; + + return -1; + +failed: + if (cctx && cctx->pool) { + ngx_destroy_pool(cctx->pool); + } + + return 0; +} + + +static void +ngx_http_lua_proxy_ssl_cert_done(void *data) +{ + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl cert done"); + + if (cctx->aborted) { + return; + } + + ngx_http_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + 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); +} + + +static void +ngx_http_lua_proxy_ssl_cert_aborted(void *data) +{ + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua proxy ssl cert aborted"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, + "proxy_ssl_certificate_by_lua: cert cb aborted"); + + cctx->aborted = 1; + cctx->connection->ssl = NULL; + cctx->exit_code = 0; + if (cctx->pool) { + ngx_destroy_pool(cctx->pool); + cctx->pool = NULL; + } +} + + +static ngx_int_t +ngx_http_lua_proxy_ssl_cert_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; + 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); + + 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) { + 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); + 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_CERT; + + 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; +} + + +int +ngx_http_lua_ffi_proxy_ssl_get_tls1_version(ngx_http_request_t *r, char **err) +{ + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + dd("tls1 ver: %d", SSL_version(ssl_conn)); + + return SSL_version(ssl_conn); +} + + +int +ngx_http_lua_ffi_proxy_ssl_clear_certs(ngx_http_request_t *r, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + SSL_certs_clear(ssl_conn); + return NGX_OK; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_der_certificate(ngx_http_request_t *r, + const char *data, size_t len, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + BIO *bio = NULL; + X509 *x509 = NULL; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + goto failed; + } + + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed"; + goto failed; + } + + if (SSL_use_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_certificate() failed"; + goto failed; + } + + X509_free(x509); + x509 = NULL; + + /* read rest of the chain */ + + while (!BIO_eof(bio)) { + + x509 = d2i_X509_bio(bio, NULL); + if (x509 == NULL) { + *err = "d2i_X509_bio() failed"; + goto failed; + } + + if (SSL_add0_chain_cert(ssl_conn, x509) == 0) { + *err = "SSL_add0_chain_cert() failed"; + goto failed; + } + } + + BIO_free(bio); + + *err = NULL; + return NGX_OK; + +failed: + + if (bio) { + BIO_free(bio); + } + + if (x509) { + X509_free(x509); + } + + ERR_clear_error(); + + return NGX_ERROR; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_der_private_key(ngx_http_request_t *r, + const char *data, size_t len, char **err) +{ + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + bio = BIO_new_mem_buf((char *) data, len); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + goto failed; + } + + pkey = d2i_PrivateKey_bio(bio, NULL); + if (pkey == NULL) { + *err = "d2i_PrivateKey_bio() failed"; + goto failed; + } + + if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_PrivateKey() failed"; + goto failed; + } + + EVP_PKEY_free(pkey); + BIO_free(bio); + + return NGX_OK; + +failed: + + if (pkey) { + EVP_PKEY_free(pkey); + } + + if (bio) { + BIO_free(bio); + } + + ERR_clear_error(); + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_cert(ngx_http_request_t *r, + void *cdata, char **err) +{ +#ifdef LIBRESSL_VERSION_NUMBER + + *err = "LibreSSL not supported"; + return NGX_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER < 0x1000205fL + + *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT; + return NGX_ERROR; + +# else + +#ifdef OPENSSL_IS_BORINGSSL + size_t i; +#else + int i; +#endif + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + X509 *x509 = NULL; + STACK_OF(X509) *chain = cdata; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + if (sk_X509_num(chain) < 1) { + *err = "invalid certificate chain"; + goto failed; + } + + x509 = sk_X509_value(chain, 0); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (SSL_use_certificate(ssl_conn, x509) == 0) { + *err = "SSL_use_certificate() failed"; + goto failed; + } + + x509 = NULL; + + /* read rest of the chain */ + + for (i = 1; i < sk_X509_num(chain); i++) { + + x509 = sk_X509_value(chain, i); + if (x509 == NULL) { + *err = "sk_X509_value() failed"; + goto failed; + } + + if (SSL_add1_chain_cert(ssl_conn, x509) == 0) { + *err = "SSL_add1_chain_cert() failed"; + goto failed; + } + } + + *err = NULL; + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; + +# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */ +#endif +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_priv_key(ngx_http_request_t *r, + void *cdata, char **err) +{ + ngx_http_upstream_t *u; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + EVP_PKEY *pkey = NULL; + + u = r->upstream; + if (u == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + 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; + } + + pkey = cdata; + if (pkey == NULL) { + *err = "invalid private key"; + goto failed; + } + + if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) { + *err = "SSL_use_PrivateKey() failed"; + goto failed; + } + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; +} + + +#else /* HAVE_PROXY_SSL_PATCH */ + + +int +ngx_http_lua_ffi_proxy_ssl_get_tls1_version(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_clear_certs(ngx_http_request_t *r, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_der_certificate(ngx_http_request_t *r, + const char *data, size_t len, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_der_private_key(ngx_http_request_t *r, + const char *data, size_t len, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_cert(ngx_http_request_t *r, + void *cdata, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +int +ngx_http_lua_ffi_proxy_ssl_set_priv_key(ngx_http_request_t *r, + void *cdata, char **err) +{ + *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function"; + + return NGX_ERROR; +} + + +#endif /* HAVE_PROXY_SSL_PATCH */ +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_proxy_ssl_certby.h b/src/ngx_http_lua_proxy_ssl_certby.h new file mode 100644 index 0000000000..0e3905b87a --- /dev/null +++ b/src/ngx_http_lua_proxy_ssl_certby.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + +#ifndef _NGX_HTTP_LUA_PROXY_SSL_CERTBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_PROXY_SSL_CERTBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#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; + +ngx_int_t ngx_http_lua_proxy_ssl_cert_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_cert_handler_file(ngx_http_request_t *r, + ngx_http_lua_loc_conf_t *llcf, lua_State *L); + +char *ngx_http_lua_proxy_ssl_cert_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_http_lua_proxy_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_http_lua_proxy_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data); + +ngx_int_t ngx_http_lua_proxy_ssl_cert_set_callback(ngx_conf_t *cf); + +#endif /* HAVE_PROXY_SSL_PATCH */ +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_PROXY_SSL_CERTBY_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 f709e6530f..173e2dc762 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -49,6 +49,7 @@ typedef struct { unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; #ifdef HAVE_PROXY_SSL_PATCH + unsigned entered_proxy_ssl_cert_handler:1; 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 928a15beed..b2cfa6dff5 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1683,7 +1683,8 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, done: #ifdef HAVE_PROXY_SSL_PATCH - if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT + || ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { return NGX_OK; } #endif @@ -2444,7 +2445,8 @@ ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, } #ifdef HAVE_PROXY_SSL_PATCH - if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { + if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT + || ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) { return ctx->exit_code; } #endif diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 9d7a0bd1a3..59605c42bb 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -39,6 +39,7 @@ | NGX_HTTP_LUA_CONTEXT_ACCESS \ | NGX_HTTP_LUA_CONTEXT_CONTENT \ | NGX_HTTP_LUA_CONTEXT_TIMER \ + | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT \ | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY \ | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO \ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ @@ -77,6 +78,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_CERT ? \ + "proxy_ssl_certificate_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY ? \ "proxy_ssl_verify_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ diff --git a/t/170-proxy-ssl-cert.t b/t/170-proxy-ssl-cert.t new file mode 100644 index 0000000000..e10b1d1cbc --- /dev/null +++ b/t/170-proxy-ssl-cert.t @@ -0,0 +1,1743 @@ +# 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() * 5 + 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_certificate_by_lua_block { + ngx.log(ngx.INFO, "hello world") + } + } +--- request +GET /t +--- error_log +proxy_ssl_certificate_by_lua* should be used with proxy_pass https url +--- must_die + + + +=== TEST 2: proxy_ssl_certificate_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_certificate_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_certificate_by_lua_block" directive is not allowed here +--- must_die + + + +=== TEST 3: proxy_ssl_certificate_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_certificate_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_certificate_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_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_certificate_by_lua_block { + ngx.log(ngx.INFO, "proxy ssl certificate by lua is running!") + } + } +--- request +GET /t +--- response_body +simple logging return +--- error_log +proxy ssl certificate by lua is running! +--- no_error_log +[error] +[alert] + + + +=== TEST 5: sleep +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in proxy ssl certificate by lua: ", ngx.now() - begin) + } + } +--- request +GET /t +--- response_body +sleep +--- error_log eval +qr/elapsed in proxy ssl certificate by lua: 0.(?:09|1\d)\d+ while loading proxy ssl certificate by lua,/, +--- no_error_log +[error] +[alert] + + + +=== TEST 6: timer +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'lua exit with code -1', +'proxy_ssl_certificate_by_lua: handler return value: -1, cert cb exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?callback failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 9: ngx.exit(0) - yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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 +--- error_log eval +[ +'lua exit with code -1', +'proxy_ssl_certificate_by_lua: cert cb exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?callback failed/, +] +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: lua exception - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'runtime error: proxy_ssl_certificate_by_lua(nginx.conf:67):2: bad bad bad', +'proxy_ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?callback 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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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 +--- error_log eval +[ +'runtime error: proxy_ssl_certificate_by_lua(nginx.conf:67):3: bad bad bad', +'proxy_ssl_certificate_by_lua: cert cb exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?callback 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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + print("get_phase: ", ngx.get_phase()) + } + } +--- request +GET /t +--- response_body +get phase return +--- error_log +get_phase: proxy_ssl_cert +--- no_error_log +[error] +[alert] + + + +=== TEST 14: subrequests disabled +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + ngx.location.capture("/foo") + } + } +--- request +GET /t +--- error_code: 502 +--- error_log eval +[ +'proxy_ssl_certificate_by_lua(nginx.conf:67):2: API disabled in the context of proxy_ssl_certificate_by_lua*', +'proxy_ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0', +qr/.*? SSL_do_handshake\(\) failed .*?callback failed/, +] +--- no_error_log +[alert] + + + +=== TEST 15: simple logging (by_lua_file) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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 certificate by lua is running!") + +--- 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_conf_command VerifyMode Peer; + + proxy_ssl_certificate_by_lua_file html/a.lua; + } +--- request +GET /t +--- response_body +simple logging by lua file +--- error_log +a.lua:1: proxy ssl certificate 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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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 while loading proxy ssl certificate by lua +uthread: hello in thread while loading proxy ssl certificate by lua +uthread: done while loading proxy ssl certificate by lua + + + +=== TEST 18: uthread (kill) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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() while loading proxy ssl certificate by lua +uthread: killed while loading proxy ssl certificate by lua +uthread: failed to kill: already waited or killed while loading proxy ssl certificate by lua + + + +=== TEST 19: ngx.exit(ngx.OK) - no yield +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_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_certificate_by_lua: handler return value: 0, cert cb 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_certificate_by_lua* without yield API (simple logic) +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_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_certificate_by_lua_block { + print("proxy ssl certificate: simple test start") + + -- Simple calculations without yield + local sum = 0 + for i = 1, 10 do + sum = sum + i + end + + print("proxy ssl certificate: calculated sum: ", sum) + + -- String operations + local str = "hello" + str = str .. " world" + print("proxy ssl certificate: 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 certificate: table sum: ", count) + + print("proxy ssl certificate: simple test done") + } + } +--- request +GET /t +--- response_body +without yield API, simple logic +--- grep_error_log eval: qr/(proxy ssl certificate: simple test start|proxy ssl certificate: calculated sum: 55|proxy ssl certificate: concatenated string: hello world|proxy ssl certificate: table sum: 6|proxy ssl certificate: simple test done)/ +--- grep_error_log_out +proxy ssl certificate: simple test start +proxy ssl certificate: calculated sum: 55 +proxy ssl certificate: concatenated string: hello world +proxy ssl certificate: table sum: 6 +proxy ssl certificate: simple test done + +--- no_error_log +[error] +[alert] +[emerg] + + + +=== TEST 21: 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_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_certificate_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] + + + +=== TEST 22: upstream connection aborted +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_certificate_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 certificate by lua +proxy_ssl_certificate_by_lua: cert cb aborted +--- no_error_log +[alert] +--- wait: 0.5 + + + +=== TEST 23: 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_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_certificate_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] + + + +=== TEST 24: TLSv1.2, without proxy_ssl_certificate, lua does not set cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.2; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.2; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_certificate_by_lua_block { + local proxy_ssl = require "ngx.proxyssl" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body_like: 400 No required SSL certificate was sent +--- error_log +got TLS1 version: TLSv1.2 +--- no_error_log +[error] +[alert] + + + +=== TEST 25: TLSv1.2, without proxy_ssl_certificate, lua sets cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.2; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.2; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local proxy_ssl = require "ngx.proxyssl" + local proxy_ssl_cert = require "ngx.ssl.proxysslcert" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + + local f = assert(io.open("t/cert/mtls_client.crt")) + local cert_data = f:read("*a") + f:close() + + local cert, err = ssl.parse_pem_cert(cert_data) + if not cert then + ngx.log(ngx.ERR, "failed to parse pem cert: ", err) + return + end + + local ok, err = proxy_ssl_cert.set_cert(cert) + if not ok then + ngx.log(ngx.ERR, "failed to set cert: ", err) + return + end + + local f = assert(io.open("t/cert/mtls_client.key")) + local pkey_data = f:read("*a") + f:close() + + local pkey, err = ssl.parse_pem_priv_key(pkey_data) + if not pkey then + ngx.log(ngx.ERR, "failed to parse pem key: ", err) + return + end + + local ok, err = proxy_ssl_cert.set_priv_key(pkey) + if not ok then + ngx.log(ngx.ERR, "failed to set private key: ", err) + return + end + } + } +--- request +GET /t +--- error_code: 200 +--- response_body +simple logging return +--- error_log +got TLS1 version: TLSv1.2 +--- no_error_log +[error] +[alert] + + + +=== TEST 26: TLSv1.3, without proxy_ssl_certificate, lua does not set cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.3; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.3; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_certificate_by_lua_block { + local proxy_ssl = require "ngx.proxyssl" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body_like: 400 No required SSL certificate was sent +--- error_log +got TLS1 version: TLSv1.3 +--- no_error_log +[error] +[alert] + + + +=== TEST 27: TLSv1.3, without proxy_ssl_certificate, lua sets cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.3; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.3; + proxy_ssl_verify on; + proxy_ssl_name example.com; + proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt; + proxy_ssl_session_reuse off; + + proxy_ssl_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local proxy_ssl = require "ngx.proxyssl" + local proxy_ssl_cert = require "ngx.ssl.proxysslcert" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + + local f = assert(io.open("t/cert/mtls_client.crt")) + local cert_data = f:read("*a") + f:close() + + local cert, err = ssl.parse_pem_cert(cert_data) + if not cert then + ngx.log(ngx.ERR, "failed to parse pem cert: ", err) + return + end + + local ok, err = proxy_ssl_cert.set_cert(cert) + if not ok then + ngx.log(ngx.ERR, "failed to set cert: ", err) + return + end + + local f = assert(io.open("t/cert/mtls_client.key")) + local pkey_data = f:read("*a") + f:close() + + local pkey, err = ssl.parse_pem_priv_key(pkey_data) + if not pkey then + ngx.log(ngx.ERR, "failed to parse pem key: ", err) + return + end + + local ok, err = proxy_ssl_cert.set_priv_key(pkey) + if not ok then + ngx.log(ngx.ERR, "failed to set private key: ", err) + return + end + } + } +--- request +GET /t +--- error_code: 200 +--- response_body +simple logging return +--- error_log +got TLS1 version: TLSv1.3 +--- no_error_log +[error] +[alert] + + + +=== TEST 28: TLSv1.2, with proxy_ssl_certificate, lua does not set cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.2; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.2; + 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_certificate_by_lua_block { + local proxy_ssl = require "ngx.proxyssl" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + } + } +--- request +GET /t +--- error_code: 200 +--- response_body +simple logging return +--- error_log +got TLS1 version: TLSv1.2 +--- no_error_log +[error] +[alert] + + + +=== TEST 29: TLSv1.3, with proxy_ssl_certificate, lua does not set cert +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.3; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.3; + 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_certificate_by_lua_block { + local proxy_ssl = require "ngx.proxyssl" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + } + } +--- request +GET /t +--- error_code: 200 +--- response_body +simple logging return +--- error_log +got TLS1 version: TLSv1.3 +--- no_error_log +[error] +[alert] + + + +=== TEST 30: proxy_ssl_certificate_by_lua takes precedence over proxy_ssl_certificate +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1.3; + ssl_verify_client on; + ssl_certificate ../../cert/mtls_server.crt; + ssl_certificate_key ../../cert/mtls_server.key; + ssl_client_certificate ../../cert/mtls_ca.crt; + + 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_protocols TLSv1.3; + 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_certificate_by_lua_block { + local ssl = require "ngx.ssl" + local proxy_ssl = require "ngx.proxyssl" + local proxy_ssl_cert = require "ngx.ssl.proxysslcert" + + local ver, err = proxy_ssl.get_tls1_version_str() + if not ver then + ngx.log(ngx.ERR, "failed to get TLS1 version: ", err) + return + end + ngx.log(ngx.INFO, "got TLS1 version: ", ver) + + -- there exists proxy_ssl_certificate and proxy_ssl_certificate_key + -- directives in nginx conf, but here we use lua codes to clear them, + -- so that it can prove that proxy_ssl_certificate_by_lua takes + -- precedence over proxy_ssl_certificate related directives + proxy_ssl_cert.clear_certs() + } + } +--- request +GET /t +--- error_code: 400 +--- response_body_like: 400 No required SSL certificate was sent +--- error_log +got TLS1 version: TLSv1.3 +--- no_error_log +[error] +[alert] From aa578597c786ccccf43d75516cfab96fda155f06 Mon Sep 17 00:00:00 2001 From: willmafh Date: Tue, 14 Oct 2025 23:32:14 +0800 Subject: [PATCH 20/22] doc: more infos to proxy_ssl_certificate_by_lua directives --- README.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.markdown b/README.markdown index 5f35e16981..a383ac1bad 100644 --- a/README.markdown +++ b/README.markdown @@ -3239,6 +3239,8 @@ Note that, unlike the relations between the [ssl_certificate](https://nginx.org/ Please refer to corresponding test case file and [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) for more details. +Note also that, it has the same condition as the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) directive for [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) to work, that is the upstream server should enable verification of client certificates. + This directive requires OpenSSL 1.0.2e or greater. [Back to TOC](#directives) From baa159f1f1541b5661842a55078bac38b65a5859 Mon Sep 17 00:00:00 2001 From: willmafh Date: Sun, 19 Oct 2025 17:21:59 +0800 Subject: [PATCH 21/22] correct way to specify random listen port --- t/170-proxy-ssl-cert.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/170-proxy-ssl-cert.t b/t/170-proxy-ssl-cert.t index e10b1d1cbc..147b76715b 100644 --- a/t/170-proxy-ssl-cert.t +++ b/t/170-proxy-ssl-cert.t @@ -1175,7 +1175,7 @@ proxy_ssl_certificate_by_lua: cert cb aborted === TEST 23: cosocket --- http_config server { - listen *:80; + listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1; server_name test.com; server_tokens off; @@ -1226,7 +1226,7 @@ proxy_ssl_certificate_by_lua: cert cb aborted local sock = ngx.socket.tcp() sock:settimeout(2000) - local ok, err = sock:connect("127.0.0.1", "80") + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1) if not ok then ngx.log(ngx.ERR, "failed to connect: ", err) return From 342d6d13a75d5ecf3f2e2c21ff71f8e3947e69c3 Mon Sep 17 00:00:00 2001 From: willmafh Date: Mon, 20 Oct 2025 14:50:16 +0800 Subject: [PATCH 22/22] tests: fixed CI --- t/170-proxy-ssl-cert.t | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/t/170-proxy-ssl-cert.t b/t/170-proxy-ssl-cert.t index 147b76715b..17567ca1f4 100644 --- a/t/170-proxy-ssl-cert.t +++ b/t/170-proxy-ssl-cert.t @@ -8,14 +8,10 @@ repeat_each(3); 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); +if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { + plan(skip_all => "too old OpenSSL, need >= 1.0.2e, was $1"); } else { - plan tests => repeat_each() * (blocks() * 5 + 10); + plan tests => repeat_each() * (blocks() * 5 + 17); } $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();