Skip to content

Commit 447bbf8

Browse files
authored
feat(tls) new method resty.kong.tls.set_upstream_cert_and_key for dynamically overriding client certificate used while connecting to upstream
1 parent 9d9308f commit 447bbf8

File tree

6 files changed

+574
-5
lines changed

6 files changed

+574
-5
lines changed

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ Name
33
lua-kong-nginx-module - Nginx C module that exposes a Lua API to dynamically control Nginx
44

55

6+
Table of Contents
7+
=================
8+
9+
* [Name](#name)
10+
* [Description](#description)
11+
* [Install](#install)
12+
* [Methods](#methods)
13+
* [resty.kong.tls.request\_client\_certificate](#restykongtlsrequest_client_certificate)
14+
* [resty.kong.tls.disable\_session\_reuse](#restykongtlsdisable_session_reuse)
15+
* [resty.kong.tls.get\_full\_client\_certificate\_chain](#restykongtlsget_full_client_certificate_chain)
16+
* [resty.kong.tls.upstream\_cert\_and\_key](#restykongtlsupstream_cert_and_key)
17+
* [License](#license)
18+
619
Description
720
===========
821
Kong often needs to be able to change Nginx behavior at runtime. Traditionally this
@@ -44,6 +57,8 @@ in later phases.
4457
This function returns `true` when the call is successful. Otherwise it returns
4558
`nil` and a string describing the error.
4659

60+
[Back to TOC](#table-of-contents)
61+
4762
resty.kong.tls.disable\_session\_reuse
4863
--------------------------------------
4964
**syntax:** *succ, err = resty.kong.tls.disable\_session\_reuse()*
@@ -56,9 +71,10 @@ disabling session ticket and session ID for the current TLS connection.
5671
This function returns `true` when the call is successful. Otherwise it returns
5772
`nil` and a string describing the error.
5873

74+
[Back to TOC](#table-of-contents)
5975

6076
resty.kong.tls.get\_full\_client\_certificate\_chain
61-
-------------------------------------------
77+
----------------------------------------------------
6278
**syntax:** *pem_chain, err = resty.kong.tls.get\_full\_client\_certificate\_chain()*
6379

6480
**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua**
@@ -83,6 +99,31 @@ In this case caller should use
8399
associate this session with one of the previous handshakes to identify the connecting
84100
client.
85101

102+
[Back to TOC](#table-of-contents)
103+
104+
resty.kong.tls.set\_upstream\_cert\_and\_key
105+
--------------------------------------------
106+
**syntax:** *ok, err = resty.kong.tls.set\_upstream\_cert\_and\_key(chain, key)*
107+
108+
**context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua**
109+
110+
Overrides and enables sending client certificate while connecting to the
111+
upstream in the current request.
112+
113+
`chain` is the client certificate and intermediate chain (if any) returned by
114+
functions such as [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert).
115+
116+
`key` is the private key corresponding to the client certificate returned by
117+
functions such as [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key).
118+
119+
On success, this function returns `true` and future handshakes with upstream servers
120+
will always use the provided client certificate. Otherwise `nil` and a string describing the error
121+
will be returned.
122+
123+
This function can be called multiple times in the same request. Later calls override
124+
previous ones.
125+
126+
[Back to TOC](#table-of-contents)
86127

87128
License
88129
=======
@@ -102,3 +143,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102143
See the License for the specific language governing permissions and
103144
limitations under the License.
104145
```
146+
147+
[Back to TOC](#table-of-contents)
148+

config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
ngx_module_type=HTTP
22
ngx_module_name=ngx_http_lua_kong_module
33
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_lua_kong_module.c"
4+
ngx_module_incs="$ngx_addon_dir/src"
45

56
. auto/module
67

78
ngx_addon_name=$ngx_module_name
9+
10+
have=NGX_HTTP_LUA_KONG . auto/have

lualib/resty/kong/tls.lua

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const char *ngx_http_lua_kong_ffi_request_client_certificate(ngx_http_request_t
2626
int ngx_http_lua_kong_ffi_get_full_client_certificate_chain(
2727
ngx_http_request_t *r, char *buf, size_t *buf_len);
2828
const char *ngx_http_lua_kong_ffi_disable_session_reuse(ngx_http_request_t *r);
29+
int ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key(ngx_http_request_t *r,
30+
void *_chain, void *_key);
2931
]])
3032

3133

@@ -47,12 +49,33 @@ local NGX_DECLINED = ngx.DECLINED
4749
local NGX_ABORT = -6
4850

4951

52+
local get_request
53+
do
54+
local ok, exdata = pcall(require, "thread.exdata")
55+
if ok and exdata then
56+
function get_request()
57+
local r = exdata()
58+
if r ~= nil then
59+
return r
60+
end
61+
end
62+
63+
else
64+
local getfenv = getfenv
65+
66+
function get_request()
67+
return getfenv(0).__ngx_req
68+
end
69+
end
70+
end
71+
72+
5073
function _M.request_client_certificate(no_session_reuse)
5174
if get_phase() ~= 'ssl_cert' then
5275
error("API disabled in the current context")
5376
end
5477

55-
local r = getfenv(0).__ngx_req
78+
local r = get_request()
5679
-- no need to check if r is nil as phase check above
5780
-- already ensured it
5881

@@ -70,7 +93,7 @@ function _M.disable_session_reuse()
7093
error("API disabled in the current context")
7194
end
7295

73-
local r = getfenv(0).__ngx_req
96+
local r = get_request()
7497

7598
local errmsg = C.ngx_http_lua_kong_ffi_disable_session_reuse(r)
7699
if errmsg == nil then
@@ -95,7 +118,7 @@ do
95118
error("API disabled in the current context", 2)
96119
end
97120

98-
local r = getfenv(0).__ngx_req
121+
local r = get_request()
99122

100123
size_ptr[0] = DEFAULT_CERT_CHAIN_SIZE
101124

@@ -131,4 +154,37 @@ do
131154
end
132155

133156

157+
do
158+
local ALLOWED_PHASES = {
159+
['rewrite'] = true,
160+
['balancer'] = true,
161+
['access'] = true,
162+
}
163+
164+
function _M.set_upstream_cert_and_key(chain, key)
165+
if not ALLOWED_PHASES[get_phase()] then
166+
error("API disabled in the current context", 2)
167+
end
168+
169+
if not chain or not key then
170+
error("chain and key must not be nil", 2)
171+
end
172+
173+
local r = get_request()
174+
175+
local ret = C.ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key(
176+
r, chain, key)
177+
if ret == NGX_OK then
178+
return true
179+
end
180+
181+
if ret == NGX_ERROR then
182+
return nil, "error while setting upstream client cert and key"
183+
end
184+
185+
error("unknown return code: " .. tostring(ret))
186+
end
187+
end
188+
189+
134190
return _M

src/ngx_http_lua_kong_module.c

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <ngx_config.h>
1919
#include <ngx_core.h>
2020
#include <ngx_http.h>
21+
#include "ngx_http_lua_kong_module.h"
2122

2223

2324
#if (NGX_SSL)
@@ -201,7 +202,6 @@ ngx_http_lua_kong_ffi_request_client_certificate(ngx_http_request_t *r)
201202
#if (NGX_SSL)
202203
ngx_connection_t *c = r->connection;
203204
ngx_ssl_conn_t *sc;
204-
SSL_CTX *ctx;
205205

206206
if (c->ssl == NULL) {
207207
return "server does not have TLS enabled";
@@ -305,3 +305,170 @@ ngx_http_lua_kong_ffi_get_full_client_certificate_chain(ngx_http_request_t *r,
305305
return NGX_ABORT;
306306
#endif
307307
}
308+
309+
310+
#if (NGX_HTTP_SSL)
311+
312+
/*
313+
* called by ngx_http_upstream_ssl_init_connection right after
314+
* ngx_ssl_create_connection to override any parameters in the
315+
* ngx_ssl_conn_t before handshake occurs
316+
*
317+
* c->ssl is guaranteed to be present and valid
318+
*/
319+
320+
void
321+
ngx_http_lua_kong_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c)
322+
{
323+
ngx_ssl_conn_t *sc = c->ssl->connection;
324+
ngx_http_lua_kong_ctx_t *ctx;
325+
STACK_OF(X509) *chain;
326+
EVP_PKEY *pkey;
327+
X509 *x509;
328+
#ifdef OPENSSL_IS_BORINGSSL
329+
size_t i;
330+
#else
331+
int i;
332+
#endif
333+
334+
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_kong_module);
335+
if (ctx == NULL || ctx->upstream_client_certificate_chain == NULL) {
336+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
337+
"skip overriding upstream SSL configuration, "
338+
"certificate not set");
339+
return;
340+
}
341+
342+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
343+
"overriding upstream SSL configuration");
344+
345+
chain = ctx->upstream_client_certificate_chain;
346+
pkey = ctx->upstream_client_private_key;
347+
348+
if (sk_X509_num(chain) < 1) {
349+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
350+
"invalid client certificate chain provided while "
351+
"handshaking with upstream");
352+
goto failed;
353+
}
354+
355+
x509 = sk_X509_value(chain, 0);
356+
if (x509 == NULL) {
357+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "sk_X509_value() failed");
358+
goto failed;
359+
}
360+
361+
if (SSL_use_certificate(sc, x509) == 0) {
362+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_use_certificate() failed");
363+
goto failed;
364+
}
365+
366+
/* read rest of the chain */
367+
368+
for (i = 1; i < sk_X509_num(chain); i++) {
369+
x509 = sk_X509_value(chain, i);
370+
if (x509 == NULL) {
371+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "sk_X509_value() failed");
372+
goto failed;
373+
}
374+
375+
if (SSL_add1_chain_cert(sc, x509) == 0) {
376+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_add1_chain_cert() failed");
377+
goto failed;
378+
}
379+
}
380+
381+
if (SSL_use_PrivateKey(sc, pkey) == 0) {
382+
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_use_PrivateKey() failed");
383+
goto failed;
384+
}
385+
386+
return;
387+
388+
failed:
389+
390+
ERR_clear_error();
391+
}
392+
393+
394+
static X509 *
395+
ngx_http_lua_kong_x509_copy(X509 *in)
396+
{
397+
return X509_up_ref(in) == 0 ? NULL : in;
398+
}
399+
400+
401+
static void
402+
ngx_http_lua_kong_cleanup(void *data)
403+
{
404+
ngx_http_lua_kong_ctx_t *ctx = data;
405+
406+
if (ctx->upstream_client_certificate_chain != NULL) {
407+
sk_X509_pop_free(ctx->upstream_client_certificate_chain, X509_free);
408+
EVP_PKEY_free(ctx->upstream_client_private_key);
409+
}
410+
}
411+
412+
413+
int
414+
ngx_http_lua_kong_ffi_set_upstream_client_cert_and_key(ngx_http_request_t *r,
415+
void *_chain, void *_key)
416+
{
417+
STACK_OF(X509) *chain = _chain;
418+
EVP_PKEY *key = _key;
419+
STACK_OF(X509) *new_chain;
420+
ngx_http_lua_kong_ctx_t *ctx;
421+
ngx_pool_cleanup_t *cln;
422+
423+
if (chain == NULL || key == NULL) {
424+
return NGX_ERROR;
425+
}
426+
427+
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_kong_module);
428+
if (ctx == NULL) {
429+
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_kong_ctx_t));
430+
if (ctx == NULL) {
431+
return NGX_ERROR;
432+
}
433+
434+
cln = ngx_pool_cleanup_add(r->pool, 0);
435+
if (cln == NULL) {
436+
return NGX_ERROR;
437+
}
438+
439+
cln->data = ctx;
440+
cln->handler = ngx_http_lua_kong_cleanup;
441+
442+
ngx_http_set_ctx(r, ctx, ngx_http_lua_kong_module);
443+
444+
} else if (ctx->upstream_client_certificate_chain != NULL) {
445+
ngx_http_lua_kong_cleanup(ctx);
446+
447+
ctx->upstream_client_certificate_chain = NULL;
448+
ctx->upstream_client_private_key = NULL;
449+
}
450+
451+
if (EVP_PKEY_up_ref(key) == 0) {
452+
goto failed;
453+
}
454+
455+
new_chain = sk_X509_deep_copy(chain, ngx_http_lua_kong_x509_copy,
456+
X509_free);
457+
if (new_chain == NULL) {
458+
EVP_PKEY_free(key);
459+
goto failed;
460+
}
461+
462+
ctx->upstream_client_certificate_chain = new_chain;
463+
ctx->upstream_client_private_key = key;
464+
465+
return NGX_OK;
466+
467+
failed:
468+
469+
ERR_clear_error();
470+
471+
return NGX_ERROR;
472+
}
473+
474+
#endif

src/ngx_http_lua_kong_module.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef _NGX_HTTP_LUA_KONG_MODULE_H_INCLUDED_
2+
#define _NGX_HTTP_LUA_KONG_MODULE_H_INCLUDED_
3+
4+
5+
#include <ngx_config.h>
6+
#include <ngx_core.h>
7+
#include <ngx_http.h>
8+
9+
10+
typedef struct {
11+
STACK_OF(X509) *upstream_client_certificate_chain;
12+
EVP_PKEY *upstream_client_private_key;
13+
} ngx_http_lua_kong_ctx_t;
14+
15+
16+
void ngx_http_lua_kong_set_upstream_ssl(ngx_http_request_t *r,
17+
ngx_connection_t *c);
18+
19+
20+
#endif /* _NGX_HTTP_LUA_KONG_MODULE_H_INCLUDED_ */

0 commit comments

Comments
 (0)