diff --git a/README.markdown b/README.markdown index 8c6edec844..a383ac1bad 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,96 @@ 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. + +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) + +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 96e9ae5b20..1f7cf4f725 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1684,7 +1684,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 @@ -2445,7 +2446,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..17567ca1f4 --- /dev/null +++ b/t/170-proxy-ssl-cert.t @@ -0,0 +1,1739 @@ +# 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|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 + 17); +} + +$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 127.0.0.1:$TEST_NGINX_RAND_PORT_1; + 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", $TEST_NGINX_RAND_PORT_1) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + 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]