diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88b68f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +t/servroot diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12b80b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +OPENRESTY_PREFIX ?= /usr/local/openresty +INSTALL ?= install + +.PHONY: install + +install: + $(INSTALL) -d $(OPENRESTY_PREFIX)/lualib/resty/apisix/ + $(INSTALL) -m 664 lib/resty/apisix/*.lua $(OPENRESTY_PREFIX)/lualib/resty/apisix/ diff --git a/config b/config new file mode 100644 index 0000000..08e5616 --- /dev/null +++ b/config @@ -0,0 +1,11 @@ +ngx_module_type=HTTP +ngx_module_name=ngx_http_apisix_module +ngx_module_srcs="$ngx_addon_dir/src/ngx_http_apisix_module.c" +ngx_module_deps=$ngx_addon_dir/src/ngx_http_apisix_module.h +ngx_module_incs="$ngx_addon_dir/src" + +. auto/module + +ngx_addon_name=$ngx_module_name + +have=NGX_HTTP_APISIX . auto/have diff --git a/lib/resty/apisix/upstream.lua b/lib/resty/apisix/upstream.lua new file mode 100644 index 0000000..5552ad5 --- /dev/null +++ b/lib/resty/apisix/upstream.lua @@ -0,0 +1,35 @@ +local ffi = require("ffi") +local base = require("resty.core.base") +local get_request = base.get_request +local C = ffi.C +local error = error +local NGX_ERROR = ngx.ERROR + + +base.allows_subsystem("http") + + +ffi.cdef([[ +typedef intptr_t ngx_int_t; +ngx_int_t +ngx_http_apisix_upstream_set_cert_and_key(ngx_http_request_t *r, void *cert, void *key); +]]) +local _M = {} + + +function _M.set_cert_and_key(cert, key) + if not cert or not key then + return nil, "both client certificate and private key should be given" + end + + local r = get_request() + local ret = C.ngx_http_apisix_upstream_set_cert_and_key(r, cert, key) + if ret == NGX_ERROR then + return nil, "error while setting upstream client cert and key" + end + + return true +end + + +return _M diff --git a/licenses/LICENSE-kong-build-tools.txt b/licenses/LICENSE-kong-build-tools.txt new file mode 100644 index 0000000..2de93c9 --- /dev/null +++ b/licenses/LICENSE-kong-build-tools.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Kong Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/LICENSE-lua-kong-nginx-module.txt b/licenses/LICENSE-lua-kong-nginx-module.txt new file mode 100644 index 0000000..fda79f1 --- /dev/null +++ b/licenses/LICENSE-lua-kong-nginx-module.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-2020 Kong Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/patch/1.19.3/nginx-upstream_mtls.patch b/patch/1.19.3/nginx-upstream_mtls.patch new file mode 100644 index 0000000..535bd53 --- /dev/null +++ b/patch/1.19.3/nginx-upstream_mtls.patch @@ -0,0 +1,25 @@ +diff --git src/http/ngx_http_upstream.c src/http/ngx_http_upstream.c +index 76045c4..cee3e2a 100644 +--- src/http/ngx_http_upstream.c ++++ src/http/ngx_http_upstream.c +@@ -8,6 +8,9 @@ + #include + #include + #include ++#if (NGX_HTTP_APISIX) ++#include ++#endif + + #if (T_NGX_MULTI_UPSTREAM) + #include +@@ -1766,6 +1769,10 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, + + r->connection->log->action = "SSL handshaking to upstream"; + ++#if (NGX_HTTP_APISIX) ++ ngx_http_apisix_set_upstream_ssl(r, c); ++#endif ++ + rc = ngx_ssl_handshake(c); + + if (rc == NGX_AGAIN) { diff --git a/patch/README.md b/patch/README.md index 3e720eb..9c65d4b 100644 --- a/patch/README.md +++ b/patch/README.md @@ -2,3 +2,6 @@ The `*-tlshandshake` patches originally come from the tlshandshake pull request to OpenResty. We have modified them a lot and even changed the API. + +The `*-upstream_mtls` patches originally come from the Kong's kong-build-tools and lua-kong-nginx-module +projects, which is also under Apache-2.0 License. diff --git a/patch/patch.sh b/patch/patch.sh index f513a7b..4839f7e 100755 --- a/patch/patch.sh +++ b/patch/patch.sh @@ -37,6 +37,7 @@ fi root="$1" if [[ "$root" == *openresty-1.19.3.* ]]; then patch_dir="$PWD/1.19.3" + apply_patch "$patch_dir" "$root" "nginx" "1.19.3" apply_patch "$patch_dir" "$root" "lua-resty-core" "0.1.21" apply_patch "$patch_dir" "$root" "ngx_lua" "0.10.19" apply_patch "$patch_dir" "$root" "ngx_stream_lua" "0.0.9" diff --git a/src/ngx_http_apisix_module.c b/src/ngx_http_apisix_module.c new file mode 100644 index 0000000..ec05459 --- /dev/null +++ b/src/ngx_http_apisix_module.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include "ngx_http_apisix_module.h" + + +static ngx_http_module_t ngx_http_apisix_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_apisix_module = { + NGX_MODULE_V1, + &ngx_http_apisix_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + + +#if (NGX_HTTP_SSL) +static X509 * +ngx_http_apisix_x509_copy(const X509 *in) +{ + return X509_up_ref((X509 *) in) == 0 ? NULL : (X509 *) in; +} + + +static void +ngx_http_apisix_flush_ssl_error() +{ + ERR_clear_error(); +} + + +static void +ngx_http_apisix_cleanup_cert_and_key(ngx_http_apisix_ctx_t *ctx) +{ + if (ctx->upstream_cert != NULL) { + sk_X509_pop_free(ctx->upstream_cert, X509_free); + EVP_PKEY_free(ctx->upstream_pkey); + + ctx->upstream_cert = NULL; + ctx->upstream_pkey = NULL; + } +} +#endif + + +static void +ngx_http_apisix_cleanup(void *data) +{ + ngx_http_apisix_ctx_t *ctx = data; + +#if (NGX_HTTP_SSL) + ngx_http_apisix_cleanup_cert_and_key(ctx); +#endif +} + + +static ngx_http_apisix_ctx_t * +ngx_http_apisix_get_module_ctx(ngx_http_request_t *r) +{ + ngx_http_apisix_ctx_t *ctx; + ngx_pool_cleanup_t *cln; + + ctx = ngx_http_get_module_ctx(r, ngx_http_apisix_module); + + if (ctx == NULL) { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_apisix_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->data = ctx; + cln->handler = ngx_http_apisix_cleanup; + + ngx_http_set_ctx(r, ctx, ngx_http_apisix_module); + } + + return ctx; +} + + +#if (NGX_HTTP_SSL) + + +ngx_int_t +ngx_http_apisix_upstream_set_cert_and_key(ngx_http_request_t *r, + void *data_cert, void *data_key) +{ + STACK_OF(X509) *cert = data_cert; + EVP_PKEY *key = data_key; + STACK_OF(X509) *new_chain; + ngx_http_apisix_ctx_t *ctx; + + if (cert == NULL || key == NULL) { + return NGX_ERROR; + } + + ctx = ngx_http_apisix_get_module_ctx(r); + + if (ctx == NULL) { + return NGX_ERROR; + } + + if (ctx->upstream_cert != NULL) { + ngx_http_apisix_cleanup_cert_and_key(ctx); + } + + if (EVP_PKEY_up_ref(key) == 0) { + goto failed; + } + + new_chain = sk_X509_deep_copy(cert, ngx_http_apisix_x509_copy, + X509_free); + if (new_chain == NULL) { + EVP_PKEY_free(key); + goto failed; + } + + ctx->upstream_cert = new_chain; + ctx->upstream_pkey = key; + + return NGX_OK; + +failed: + + ngx_http_apisix_flush_ssl_error(); + + return NGX_ERROR; +} + + +void +ngx_http_apisix_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c) +{ + ngx_ssl_conn_t *sc = c->ssl->connection; + ngx_http_apisix_ctx_t *ctx; + STACK_OF(X509) *cert; + EVP_PKEY *pkey; + X509 *x509; +#ifdef OPENSSL_IS_BORINGSSL + size_t i; +#else + int i; +#endif + + ctx = ngx_http_get_module_ctx(r, ngx_http_apisix_module); + + if (ctx == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "skip overriding upstream SSL configuration, " + "module ctx not set"); + return; + } + + if (ctx->upstream_cert != NULL) { + cert = ctx->upstream_cert; + pkey = ctx->upstream_pkey; + + if (sk_X509_num(cert) < 1) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "invalid client certificate provided while " + "handshaking with upstream"); + goto failed; + } + + x509 = sk_X509_value(cert, 0); + if (x509 == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "sk_X509_value() failed"); + goto failed; + } + + if (SSL_use_certificate(sc, x509) == 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_use_certificate() failed"); + goto failed; + } + + for (i = 1; i < sk_X509_num(cert); i++) { + x509 = sk_X509_value(cert, i); + if (x509 == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "sk_X509_value() failed"); + goto failed; + } + + if (SSL_add1_chain_cert(sc, x509) == 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_add1_chain_cert() failed"); + goto failed; + } + } + + if (SSL_use_PrivateKey(sc, pkey) == 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_use_PrivateKey() failed"); + goto failed; + } + } + + return; + +failed: + + ngx_http_apisix_flush_ssl_error(); +} +#endif diff --git a/src/ngx_http_apisix_module.h b/src/ngx_http_apisix_module.h new file mode 100644 index 0000000..f7c0ae1 --- /dev/null +++ b/src/ngx_http_apisix_module.h @@ -0,0 +1,17 @@ +#ifndef _NGX_HTTP_APISIX_H_INCLUDED_ +#define _NGX_HTTP_APISIX_H_INCLUDED_ + + +#include + + +typedef struct { + STACK_OF(X509) *upstream_cert; + EVP_PKEY *upstream_pkey; +} ngx_http_apisix_ctx_t; + + +void ngx_http_apisix_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c); + + +#endif /* _NGX_HTTP_APISIX_H_INCLUDED_ */ diff --git a/t/APISIX_NGINX.pm b/t/APISIX_NGINX.pm new file mode 100644 index 0000000..2cdfeed --- /dev/null +++ b/t/APISIX_NGINX.pm @@ -0,0 +1,35 @@ +package t::APISIX_NGINX; + +use Test::Nginx::Socket::Lua; +use Test::Nginx::Socket::Lua::Stream -Base; + +log_level('debug'); +no_long_string(); +no_shuffle(); +worker_connections(128); + + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } + + my $http_config = $block->http_config // ''; + $http_config .= <<_EOC_; + lua_package_path "lib/?.lua;;"; +_EOC_ + + $block->set_value("http_config", $http_config); +}); + + +1; diff --git a/t/certs/apisix.crt b/t/certs/apisix.crt new file mode 100644 index 0000000..503f277 --- /dev/null +++ b/t/certs/apisix.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G +A1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa +GA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n +RG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM +CHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe +cvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb +VDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR +2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr +abf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2 +WjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/ +Evm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1 +/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh +/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj +cTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ +tSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl +c3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC +tC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY +1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl +PYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob +rJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy +hme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1 +7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y +IJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve +U/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM= +-----END CERTIFICATE----- diff --git a/t/certs/apisix.key b/t/certs/apisix.key new file mode 100644 index 0000000..7105067 --- /dev/null +++ b/t/certs/apisix.key @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/t/certs/mtls_ca.crt b/t/certs/mtls_ca.crt new file mode 100644 index 0000000..b57e390 --- /dev/null +++ b/t/certs/mtls_ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSjCCAjICCQDmBdlKmGaJITANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJj +bjESMBAGA1UECAwJR3VhbmdEb25nMQ8wDQYDVQQHDAZaaHVIYWkxDTALBgNVBAoM +BGFwaTcxDDAKBgNVBAsMA29wczEWMBQGA1UEAwwNY2EuYXBpc2l4LmRldjAeFw0y +MDA2MjAxMzEzNDFaFw0zMDA2MTgxMzEzNDFaMGcxCzAJBgNVBAYTAmNuMRIwEAYD +VQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTENMAsGA1UECgwEYXBpNzEM +MAoGA1UECwwDb3BzMRYwFAYDVQQDDA1jYS5hcGlzaXguZGV2MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAun+Gq/bp7CcZ9i5ZdjuCvyZVXsiAaBELVi/Q +QQtC90z5aQyWudTPB1Lcpk5HosbT73eHh03hFCRMFv6Miase1T59KJ4zGSFKoFEr +j2cbNmWFJEhTGce1pn52zMzZrXERYhKBA0n4bwHK/IND0XeEZ2RQPtGnGBqj3vKL +3px+mOzIeMy4VMSkIkL2jlgo5jN0IjQIsvHRSrhIWzFhr6qtIJhuh0oI6gs+/yvA +vspGeVFtIg/1PY3bOgFfhJg08/Aw7vgMjmADypEbBabLaWOZ8RZ3Ci2is6cL/1wX +Sr8OIIBXTmTGmXEuSsMsBgC7BFwEY4XEsGx8QQJsrh1dSf2t0QIDAQABMA0GCSqG +SIb3DQEBBQUAA4IBAQCKC98wWieC66NHAYb9ICOwr+XTmoFABpFNaM4bPXMD4IUq +BaMGfBh92e4ANz2bm1D3J0ZNH3TVC7OhF2ymi6wSMde/Ygkh5xu2HgTEX2QTDQVd +J27jwEIe45VLdvuu33jvE/iNNQHI6J6zP45gs/FS+CwMoYRnNcC+428YUf9XMcgM +UkeMOnnkhw1OUzmoACY705hAEAPFbb7KkQ109lgbh6cucMy7Nw/N1t6Pyuxlqteg +d8Wy6VFYPRRK43dYoA9B0yvsZCERvxgR1IrDjo0B2wIDzM4eM6ldLfnr8pPnBFfS +g/Pdo6VZsXeSv3o00lBEY/25Vqxn3sPBK4E7a+mX +-----END CERTIFICATE----- diff --git a/t/certs/mtls_client.crt b/t/certs/mtls_client.crt new file mode 100644 index 0000000..847a544 --- /dev/null +++ b/t/certs/mtls_client.crt @@ -0,0 +1,69 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 64207 (0xfacf) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=cn, ST=GuangDong, L=ZhuHai, O=api7, OU=ops, CN=ca.apisix.dev + Validity + Not Before: Jun 20 13:15:00 2020 GMT + Not After : Jul 8 13:15:00 2030 GMT + Subject: C=cn, ST=GuangDong, O=api7, L=ZhuHai, CN=client.apisix.dev + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9f:28:8f:2e:88:41:ff:89:f6:62:91:29:d1:6b: + 7f:c4:d8:1e:28:85:55:91:c2:3a:3f:23:1c:83:11: + 6a:26:81:1e:2d:2e:4d:69:48:98:4f:ff:84:82:2d: + 6b:8c:41:31:56:4d:b4:aa:b7:52:05:63:2e:19:6d: + 54:87:1f:21:a8:34:f9:89:1a:b1:d1:24:21:84:fa: + c8:29:7f:39:f4:1a:35:78:95:74:0f:24:3d:24:e8: + 64:75:09:7d:8c:a3:54:d6:74:5a:92:27:f1:dc:e4: + 04:30:71:01:67:3d:fa:0b:03:0b:01:cb:8c:aa:ae: + 59:9f:f7:a6:40:53:2b:65:ff:b6:64:8d:fe:0f:ee: + 62:64:24:7b:4c:fd:68:12:47:4a:46:86:36:53:00: + 64:5f:e4:32:56:a0:ee:75:92:2d:e2:dc:92:3e:d7: + 99:8e:86:69:e7:0a:99:e4:b2:71:95:3d:f9:7d:da: + af:76:1f:3f:f8:bf:78:aa:13:e5:13:84:f6:11:a5: + c1:9b:9d:d7:73:32:f3:da:09:78:9a:be:0f:01:fe: + ed:8b:55:b9:f8:97:46:9d:6a:6a:90:19:ea:4e:02: + 30:ff:d7:1a:da:39:53:f6:5b:6d:96:d0:fc:ed:0d: + 72:78:ac:b7:be:71:aa:4d:4b:8a:06:b9:25:1f:90: + 81:0d + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 72:a7:1f:15:21:ba:4f:e7:2f:64:a0:e5:40:7c:e0:ea:09:7b: + 95:cf:80:d0:6f:54:c2:8d:d1:cf:cd:00:f2:95:20:f9:e2:9e: + f5:1c:1b:f9:87:78:a7:b1:3f:31:34:b0:c8:1a:44:da:2c:ef: + 93:76:d7:df:44:5f:27:6a:51:cb:09:f2:32:f4:70:db:50:da: + 4e:49:41:75:e0:d2:7b:4d:0b:8b:6e:0a:02:0a:00:e9:ce:f3: + bf:72:e6:14:86:df:a7:b9:ef:09:80:a1:52:a7:69:b8:23:7a: + 3d:3d:cc:6d:64:91:7b:c0:9a:98:2a:a3:17:95:0a:ee:e1:ed: + f2:be:02:ea:cb:6e:c1:82:4d:a1:e8:03:9a:46:d6:d7:07:0f: + 12:50:7e:95:5c:6c:17:f0:40:34:81:5b:74:90:8e:24:6a:5f: + 8e:77:ff:4d:67:c3:a9:1b:39:e2:ca:62:b6:89:ca:c6:86:f1: + 95:36:2b:cf:96:a5:6e:89:0e:e6:dc:88:78:f0:7d:09:e9:53: + 65:35:e9:72:a2:be:1c:5e:b8:a6:2b:57:f2:0d:2f:4b:31:8f: + f7:d9:ad:a3:58:12:bb:c9:5b:38:79:96:5b:c8:74:d2:e6:79: + 23:e6:bd:be:74:25:42:2c:fa:50:ea:9f:53:28:6d:35:f3:0e: + 9b:82:15:70 +-----BEGIN CERTIFICATE----- +MIIDOjCCAiICAwD6zzANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJjbjESMBAG +A1UECAwJR3VhbmdEb25nMQ8wDQYDVQQHDAZaaHVIYWkxDTALBgNVBAoMBGFwaTcx +DDAKBgNVBAsMA29wczEWMBQGA1UEAwwNY2EuYXBpc2l4LmRldjAeFw0yMDA2MjAx +MzE1MDBaFw0zMDA3MDgxMzE1MDBaMF0xCzAJBgNVBAYTAmNuMRIwEAYDVQQIDAlH +dWFuZ0RvbmcxDTALBgNVBAoMBGFwaTcxDzANBgNVBAcMBlpodUhhaTEaMBgGA1UE +AwwRY2xpZW50LmFwaXNpeC5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCfKI8uiEH/ifZikSnRa3/E2B4ohVWRwjo/IxyDEWomgR4tLk1pSJhP/4SC +LWuMQTFWTbSqt1IFYy4ZbVSHHyGoNPmJGrHRJCGE+sgpfzn0GjV4lXQPJD0k6GR1 +CX2Mo1TWdFqSJ/Hc5AQwcQFnPfoLAwsBy4yqrlmf96ZAUytl/7Zkjf4P7mJkJHtM +/WgSR0pGhjZTAGRf5DJWoO51ki3i3JI+15mOhmnnCpnksnGVPfl92q92Hz/4v3iq +E+UThPYRpcGbnddzMvPaCXiavg8B/u2LVbn4l0adamqQGepOAjD/1xraOVP2W22W +0PztDXJ4rLe+capNS4oGuSUfkIENAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHKn +HxUhuk/nL2Sg5UB84OoJe5XPgNBvVMKN0c/NAPKVIPninvUcG/mHeKexPzE0sMga +RNos75N2199EXydqUcsJ8jL0cNtQ2k5JQXXg0ntNC4tuCgIKAOnO879y5hSG36e5 +7wmAoVKnabgjej09zG1kkXvAmpgqoxeVCu7h7fK+AurLbsGCTaHoA5pG1tcHDxJQ +fpVcbBfwQDSBW3SQjiRqX453/01nw6kbOeLKYraJysaG8ZU2K8+WpW6JDubciHjw +fQnpU2U16XKivhxeuKYrV/INL0sxj/fZraNYErvJWzh5llvIdNLmeSPmvb50JUIs ++lDqn1MobTXzDpuCFXA= +-----END CERTIFICATE----- diff --git a/t/certs/mtls_client.key b/t/certs/mtls_client.key new file mode 100644 index 0000000..d939c62 --- /dev/null +++ b/t/certs/mtls_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnyiPLohB/4n2YpEp0Wt/xNgeKIVVkcI6PyMcgxFqJoEeLS5N +aUiYT/+Egi1rjEExVk20qrdSBWMuGW1Uhx8hqDT5iRqx0SQhhPrIKX859Bo1eJV0 +DyQ9JOhkdQl9jKNU1nRakifx3OQEMHEBZz36CwMLAcuMqq5Zn/emQFMrZf+2ZI3+ +D+5iZCR7TP1oEkdKRoY2UwBkX+QyVqDudZIt4tySPteZjoZp5wqZ5LJxlT35fdqv +dh8/+L94qhPlE4T2EaXBm53XczLz2gl4mr4PAf7ti1W5+JdGnWpqkBnqTgIw/9ca +2jlT9lttltD87Q1yeKy3vnGqTUuKBrklH5CBDQIDAQABAoIBAHDe5bPdQ9jCcW3z +fpGax/DER5b6//UvpfkSoGy/E+Wcmdb2yEVLC2FoVwOuzF+Z+DA5SU/sVAmoDZBQ +vapZxJeygejeeo5ULkVNSFhNdr8LOzJ54uW+EHK1MFDj2xq61jaEK5sNIvRA7Eui +SJl8FXBrxwmN3gNJRBwzF770fImHUfZt0YU3rWKw5Qin7QnlUzW2KPUltnSEq/xB +kIzyWpuj7iAm9wTjH9Vy06sWCmxj1lzTTXlanjPb1jOTaOhbQMpyaAzRgQN8PZiE +YKCarzVj7BJr7/vZYpnQtQDY12UL5n33BEqMP0VNHVqv+ZO3bktfvlwBru5ZJ7Cf +URLsSc0CgYEAyz7FzV7cZYgjfUFD67MIS1HtVk7SX0UiYCsrGy8zA19tkhe3XVpc +CZSwkjzjdEk0zEwiNAtawrDlR1m2kverbhhCHqXUOHwEpujMBjeJCNUVEh3OABr8 +vf2WJ6D1IRh8FA5CYLZP7aZ41fcxAnvIPAEThemLQL3C4H5H5NG2WFsCgYEAyHhP +onpS/Eo/OXKYFLR/mvjizRVSomz1lVVL+GWMUYQsmgsPyBJgyAOX3Pqt9catgxhM +DbEr7EWTxth3YeVzamiJPNVK0HvCax9gQ0KkOmtbrfN54zBHOJ+ieYhsieZLMgjx +iu7Ieo6LDGV39HkvekzutZpypiCpKlMaFlCFiLcCgYEAmAgRsEj4Nh665VPvuZzH +ZIgZMAlwBgHR7/v6l7AbybcVYEXLTNJtrGEEH6/aOL8V9ogwwZuIvb/TEidCkfcf +zg/pTcGf2My0MiJLk47xO6EgzNdso9mMG5ZYPraBBsuo7NupvWxCp7NyCiOJDqGH +K5NmhjInjzsjTghIQRq5+qcCgYEAxnm/NjjvslL8F69p/I3cDJ2/RpaG0sMXvbrO +VWaMryQyWGz9OfNgGIbeMu2Jj90dar6ChcfUmb8lGOi2AZl/VGmc/jqaMKFnElHl +J5JyMFicUzPMiG8DBH+gB71W4Iy+BBKwugHBQP2hkytewQ++PtKuP+RjADEz6vCN +0mv0WS8CgYBnbMRP8wIOLJPRMw/iL9BdMf606X4xbmNn9HWVp2mH9D3D51kDFvls +7y2vEaYkFv3XoYgVN9ZHDUbM/YTUozKjcAcvz0syLQb8wRwKeo+XSmo09+360r18 +zRugoE7bPl39WdGWaW3td0qf1r9z3sE2iWUTJPRQ3DYpsLOYIgyKmw== +-----END RSA PRIVATE KEY----- diff --git a/t/certs/mtls_server.crt b/t/certs/mtls_server.crt new file mode 100644 index 0000000..14f48ef --- /dev/null +++ b/t/certs/mtls_server.crt @@ -0,0 +1,69 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 64206 (0xface) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=cn, ST=GuangDong, L=ZhuHai, O=api7, OU=ops, CN=ca.apisix.dev + Validity + Not Before: Jun 20 13:14:34 2020 GMT + Not After : Jun 18 13:14:34 2030 GMT + Subject: C=cn, ST=GuangDong, O=api7, L=ZhuHai, CN=admin.apisix.dev + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9b:45:2a:e1:c9:6e:a7:af:af:bd:46:5c:5e:5f: + 72:66:02:78:69:16:fd:f9:69:8e:47:68:0f:8d:35: + 92:c4:14:40:5c:cf:57:3d:41:ea:13:7b:f4:de:c8: + ab:e8:62:56:1e:60:61:f6:38:65:5f:30:b5:91:25: + 79:07:12:45:ce:24:31:86:1f:2c:a6:cb:1d:8b:4b: + 9e:5f:1f:c7:b6:f3:e8:98:ee:b3:70:c7:9e:5d:10: + ce:29:e4:22:68:69:9e:df:ae:f6:bb:11:e8:b8:f1: + 07:bf:2d:d5:57:f2:e4:07:8a:da:d2:7b:8a:53:d1: + b4:f4:42:19:9a:14:98:01:3e:23:27:3a:0f:ad:d0: + 1d:c5:31:9a:ee:ae:df:7f:fb:2e:34:0b:51:ca:b4: + 8c:59:ae:86:5f:95:69:2b:4a:c6:2d:a5:ae:04:46: + 7a:93:09:15:72:0a:78:ef:98:7d:00:b5:b4:b2:f2: + e2:a9:2e:04:fb:de:84:ad:da:8e:a3:31:53:3a:d5: + 91:cd:77:f5:b8:ea:eb:14:aa:d9:62:d1:12:79:87: + 08:27:6d:c1:b9:e3:7d:f1:07:52:3c:a3:34:6a:c1: + 96:cf:a2:84:cc:14:50:49:40:0b:38:3c:3b:1e:df: + 57:6f:f2:05:35:92:9b:4f:b1:21:0b:f7:62:3a:2d: + 83:c7 + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 7a:1c:a3:d8:d4:97:5d:91:d2:c8:31:c4:40:ef:f1:38:ac:5c: + b9:74:66:81:94:4f:71:02:38:49:5a:0d:7b:10:17:73:a5:96: + 3e:de:0e:a4:75:8c:1b:c7:51:f9:f6:eb:9d:f4:bd:4c:1c:92: + 41:d0:16:c6:73:c1:f9:7c:b6:71:7d:16:53:13:fa:70:90:c0: + 95:e3:a3:51:30:96:02:f2:32:32:fe:a9:d1:ef:c5:7e:04:58: + ca:20:ef:d0:43:8c:52:8d:52:3a:71:ed:0f:87:4e:8b:c6:28: + 51:56:13:fd:71:81:10:cc:2f:2c:aa:8d:6a:93:d7:52:34:08: + 23:7b:2b:a7:a4:3e:6b:8f:c3:af:59:b9:1c:b8:d8:6c:a3:88: + c7:bd:b5:e1:eb:6b:6a:f2:7d:a3:89:c6:b0:21:f8:1b:9a:dc: + bf:ef:d6:21:91:7f:65:99:4d:f4:49:24:ab:46:09:a0:c9:a1: + 64:14:f4:56:73:ce:1b:22:dd:b7:1f:58:0f:29:ae:6a:6e:41: + 6e:b4:5c:90:97:4e:59:4e:cf:e3:a1:89:d1:5a:65:a3:68:2f: + b9:97:82:6f:4c:21:cb:f6:9b:7d:fd:d8:07:70:14:cd:10:fb: + bf:03:70:fa:51:7c:56:4c:1b:a5:87:d3:1b:18:5c:22:87:6f: + 04:08:59:53 +-----BEGIN CERTIFICATE----- +MIIDOTCCAiECAwD6zjANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJjbjESMBAG +A1UECAwJR3VhbmdEb25nMQ8wDQYDVQQHDAZaaHVIYWkxDTALBgNVBAoMBGFwaTcx +DDAKBgNVBAsMA29wczEWMBQGA1UEAwwNY2EuYXBpc2l4LmRldjAeFw0yMDA2MjAx +MzE0MzRaFw0zMDA2MTgxMzE0MzRaMFwxCzAJBgNVBAYTAmNuMRIwEAYDVQQIDAlH +dWFuZ0RvbmcxDTALBgNVBAoMBGFwaTcxDzANBgNVBAcMBlpodUhhaTEZMBcGA1UE +AwwQYWRtaW4uYXBpc2l4LmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAJtFKuHJbqevr71GXF5fcmYCeGkW/flpjkdoD401ksQUQFzPVz1B6hN79N7I +q+hiVh5gYfY4ZV8wtZEleQcSRc4kMYYfLKbLHYtLnl8fx7bz6Jjus3DHnl0Qzink +Imhpnt+u9rsR6LjxB78t1Vfy5AeK2tJ7ilPRtPRCGZoUmAE+Iyc6D63QHcUxmu6u +33/7LjQLUcq0jFmuhl+VaStKxi2lrgRGepMJFXIKeO+YfQC1tLLy4qkuBPvehK3a +jqMxUzrVkc139bjq6xSq2WLREnmHCCdtwbnjffEHUjyjNGrBls+ihMwUUElACzg8 +Ox7fV2/yBTWSm0+xIQv3Yjotg8cCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAehyj +2NSXXZHSyDHEQO/xOKxcuXRmgZRPcQI4SVoNexAXc6WWPt4OpHWMG8dR+fbrnfS9 +TBySQdAWxnPB+Xy2cX0WUxP6cJDAleOjUTCWAvIyMv6p0e/FfgRYyiDv0EOMUo1S +OnHtD4dOi8YoUVYT/XGBEMwvLKqNapPXUjQII3srp6Q+a4/Dr1m5HLjYbKOIx721 +4etravJ9o4nGsCH4G5rcv+/WIZF/ZZlN9Ekkq0YJoMmhZBT0VnPOGyLdtx9YDymu +am5BbrRckJdOWU7P46GJ0Vplo2gvuZeCb0why/abff3YB3AUzRD7vwNw+lF8Vkwb +pYfTGxhcIodvBAhZUw== +-----END CERTIFICATE----- diff --git a/t/certs/mtls_server.key b/t/certs/mtls_server.key new file mode 100644 index 0000000..5f2c75b --- /dev/null +++ b/t/certs/mtls_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAm0Uq4clup6+vvUZcXl9yZgJ4aRb9+WmOR2gPjTWSxBRAXM9X +PUHqE3v03sir6GJWHmBh9jhlXzC1kSV5BxJFziQxhh8spssdi0ueXx/HtvPomO6z +cMeeXRDOKeQiaGme3672uxHouPEHvy3VV/LkB4ra0nuKU9G09EIZmhSYAT4jJzoP +rdAdxTGa7q7ff/suNAtRyrSMWa6GX5VpK0rGLaWuBEZ6kwkVcgp475h9ALW0svLi +qS4E+96ErdqOozFTOtWRzXf1uOrrFKrZYtESeYcIJ23BueN98QdSPKM0asGWz6KE +zBRQSUALODw7Ht9Xb/IFNZKbT7EhC/diOi2DxwIDAQABAoIBAC3NJW0dAissw+ZN +Twn3lcNJj0NQqPJdlL6zj4LT/ssgPiwibVWAkA/XTNA62ZrfBxBG1h7PW/fMYoLC +TwUq+rRoMMOjhoRc/gYM9FaTBVKOeFpEb2IhQDGrt2TcCtpJ7beF4PolukRztRlL +59bdqy4eY5YbIx6+iWZT6UFuObiDqi7i4SLWEgK+/P4Uk8/SmhVqIWcj1m3SPK6I +YbzsgXiT64fNd7/O06ISKia1UzvUCtH7tbxWxCvsqw+PqQT+YuEmNY1pOQGYp0dU +4ndzvrP0Ajuu3xH7aYP/Kilkz69PPMLygwNey4HRIAuUqw/HBfTR0/ccRSuhrYxb +9QaOP0ECgYEAyuqLo/tjWrFiJnDbhK3z2qcydktFS58da2QitSRYlQ6AQXjZ3+v7 +buL1QV59aXzIGTZz3gjO+omdpfIagBI47YnWIUtj+NylNROWv+aZXQwgC7ayQWTg +eBu8L2YXBvAR9TgHhqj3Fl4YcuipVE3XFVjjvLjrbE1nssMmaJqi95kCgYEAw+O7 +Zdj/NedbI2GtnZv31YlLmrMdtmeAmU2x8eC5v30Kx3GCU9DdZzImsaYxxjfSL+6c +eP/DF8JHWIGo9GQPcMSijHsaNMIwgv6+5rx+Lp/zsjwRApJsVQeoff2ZdWjnFsi3 +rRHE8QZfWMqcnOsr4io7xfVd3t4tV22BBrnt8l8CgYEAncU3xcxUN9PryI+/Xq4S +CFQvvCJSQrX4neNByS31Yg/gUQex/5Tv7guxPZ5GTJqkylW4SU73/3y4gqp3SFTi +xm6Be2mu1XRZT6dnctXNMLeYwwLOHmJc1YZbD0+FX/ORQuTJlT4Sv+VxhQa5gb70 +GLkAeWAeTBrzId7yIir5wyECgYAw2iJqC+vZrZD1Ce8aV0M/ZbYNJo5KZxWTJeUy +xTCNqMl/Y7d036nXipJLy36uSE2K1p7/LgmhazoPwIY6LJoNLXy8PBcVATjH8m/5 +axis2AcWdBRp58pMilRi11PmC/tVm0jzSHMtCMHOivjzyVJwXMf7Xm3CnvX/z7dV +zhihUQKBgHWtWfNk/svgLp6w8T6aMgyAb9ud5pX/CbNZhGNRqhPhJkss1tFr6/Mv +bJiZoEP3C0sDdA1JRuMkXm5EE60xyhzCNmv5H0cQ3C2Y9Q9ly89ggwIXNiNfKWpP +VrdvXQ3NkP/RaDy83B9dN2Jb6lUpcNQnB5Q5yAlsYaYgsGBedcvc +-----END RSA PRIVATE KEY----- diff --git a/t/upstream_mtls.t b/t/upstream_mtls.t new file mode 100644 index 0000000..d396862 --- /dev/null +++ b/t/upstream_mtls.t @@ -0,0 +1,176 @@ +use t::APISIX_NGINX 'no_plan'; + +repeat_each(2); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->http_config) { + my $http_config = <<'_EOC_'; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name admin.apisix.dev; + ssl_certificate ../../certs/mtls_server.crt; + ssl_certificate_key ../../certs/mtls_server.key; + ssl_client_certificate ../../certs/mtls_ca.crt; + ssl_verify_client on; + + server_tokens off; + + location /foo { + return 200 'ok\n'; + } + } + +_EOC_ + + $block->set_value("http_config", $http_config); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: send client certificate +--- config + location /t { + access_by_lua_block { + local upstream = require("resty.apisix.upstream") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/certs/mtls_client.crt")) + local cert_data = f:read("*a") + f:close() + + local cert = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/certs/mtls_client.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + local ok, err = upstream.set_cert_and_key(cert, key) + if not ok then + ngx.say("set_cert_and_key failed: ", err) + end + } + + proxy_ssl_trusted_certificate ../../certs/mtls_ca.crt; + proxy_ssl_verify on; + proxy_ssl_name admin.apisix.dev; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/foo; + } + +--- response_body +ok + + + +=== TEST 2: send bad client certificate +--- config + location /t { + access_by_lua_block { + local upstream = require("resty.apisix.upstream") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/certs/mtls_client.crt")) + local cert_data = f:read("*a") + f:close() + + local cert = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/certs/mtls_client.key")) + local key_data = f:read("*a") + f:close() + + local ok, err = upstream.set_cert_and_key(cert, nil) + if not ok then + ngx.say("set_cert_and_key failed: ", err) + end + } + + proxy_ssl_trusted_certificate ../../certs/mtls_ca.crt; + proxy_ssl_verify on; + proxy_ssl_name admin.apisix.dev; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/foo; + } + +--- response_body +set_cert_and_key failed: both client certificate and private key should be given + + + +=== TEST 3: send wrong client certificate +--- config + location /t { + access_by_lua_block { + local upstream = require("resty.apisix.upstream") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/certs/apisix.crt")) + local cert_data = f:read("*a") + f:close() + + local cert = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/certs/apisix.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + local ok, err = upstream.set_cert_and_key(cert, key) + if not ok then + ngx.say("set_cert_and_key failed: ", err) + end + } + + proxy_ssl_trusted_certificate ../../certs/mtls_ca.crt; + proxy_ssl_verify on; + proxy_ssl_name admin.apisix.dev; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/foo; + } + +--- error_code: 400 +--- error_log +client SSL certificate verify error + + + +=== TEST 4: call set_cert_and_key repeatedly +--- config + location /t { + access_by_lua_block { + local upstream = require("resty.apisix.upstream") + local ssl = require("ngx.ssl") + + local f = assert(io.open("t/certs/mtls_client.crt")) + local cert_data = f:read("*a") + f:close() + + local cert = assert(ssl.parse_pem_cert(cert_data)) + + f = assert(io.open("t/certs/mtls_client.key")) + local key_data = f:read("*a") + f:close() + + local key = assert(ssl.parse_pem_priv_key(key_data)) + + for i = 1, 5 do + local ok, err = upstream.set_cert_and_key(cert, key) + if not ok then + ngx.say("set_cert_and_key failed: ", err) + end + end + } + + proxy_ssl_trusted_certificate ../../certs/mtls_ca.crt; + proxy_ssl_verify on; + proxy_ssl_name admin.apisix.dev; + proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/foo; + } + +--- response_body +ok