Skip to content

Commit d3d3d86

Browse files
committed
feature: add lua_ssl_key_log directive to log client connection SSL keys in the tcpsock:sslhandshake method. Keys are logged in the SSLKEYLOGFILE format compatible with Wireshark.
1 parent 3cedd2d commit d3d3d86

File tree

6 files changed

+244
-1
lines changed

6 files changed

+244
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ behavior.
166166
* [lua_ssl_certificate_key](https://github.com/openresty/lua-nginx-module#lua_ssl_certificate_key)
167167
* [lua_ssl_trusted_certificate](https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate)
168168
* [lua_ssl_verify_depth](https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth)
169+
* [lua_ssl_key_log](https://github.com/openresty/lua-nginx-module#lua_ssl_key_log)
169170
* [lua_ssl_conf_command](https://github.com/openresty/lua-nginx-module#lua_ssl_conf_command)
170171
* [lua_upstream_skip_openssl_default_verify](https://github.com/openresty/lua-nginx-module#lua_upstream_skip_openssl_default_verify)
171172
* [lua_check_client_abort](https://github.com/openresty/lua-nginx-module#lua_check_client_abort)

src/ngx_stream_lua_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ struct ngx_stream_lua_srv_conf_s {
257257
ngx_uint_t ssl_verify_depth;
258258
ngx_str_t ssl_trusted_certificate;
259259
ngx_str_t ssl_crl;
260+
ngx_str_t ssl_key_log;
260261
#if (nginx_version >= 1019004)
261262
ngx_array_t *ssl_conf_commands;
262263
#endif

src/ngx_stream_lua_module.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ static char *ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data);
5050
#if (NGX_STREAM_SSL)
5151
static ngx_int_t ngx_stream_lua_set_ssl(ngx_conf_t *cf,
5252
ngx_stream_lua_loc_conf_t *llcf);
53+
static void key_log_callback(const ngx_ssl_conn_t *ssl_conn,
54+
const char *line);
55+
static void ngx_stream_lua_ssl_cleanup_key_log(void *data);
56+
static ngx_int_t ngx_stream_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl,
57+
ngx_str_t *file);
5358
#if (nginx_version >= 1019004)
5459
static char *ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post,
5560
void *data);
@@ -476,6 +481,13 @@ static ngx_command_t ngx_stream_lua_cmds[] = {
476481
offsetof(ngx_stream_lua_srv_conf_t, ssl_crl),
477482
NULL },
478483

484+
{ ngx_string("lua_ssl_key_log"),
485+
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
486+
ngx_conf_set_str_slot,
487+
NGX_STREAM_SRV_CONF_OFFSET,
488+
offsetof(ngx_stream_lua_srv_conf_t, ssl_key_log),
489+
NULL },
490+
479491
#if (nginx_version >= 1019004)
480492
{ ngx_string("lua_ssl_conf_command"),
481493
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
@@ -1012,6 +1024,7 @@ ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
10121024
ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
10131025
prev->ssl_trusted_certificate, "");
10141026
ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
1027+
ngx_conf_merge_str_value(conf->ssl_key_log, prev->ssl_key_log, "");
10151028
#if (nginx_version >= 1019004)
10161029
ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands,
10171030
NULL);
@@ -1157,6 +1170,12 @@ ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf)
11571170
return NGX_ERROR;
11581171
}
11591172

1173+
if (ngx_stream_lua_ssl_key_log(cf, lscf->ssl, &lscf->ssl_key_log)
1174+
!= NGX_OK)
1175+
{
1176+
return NGX_ERROR;
1177+
}
1178+
11601179
#if (nginx_version >= 1019004)
11611180
if (ngx_ssl_conf_commands(cf, lscf->ssl, lscf->ssl_conf_commands)
11621181
!= NGX_OK)
@@ -1169,6 +1188,101 @@ ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf)
11691188
}
11701189

11711190

1191+
static void
1192+
key_log_callback(const ngx_ssl_conn_t *ssl_conn, const char *line)
1193+
{
1194+
ngx_stream_lua_ssl_key_log_t *ssl_key_log;
1195+
ngx_connection_t *c;
1196+
1197+
ssl_key_log = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
1198+
ngx_stream_lua_ssl_key_log_index);
1199+
if (ssl_key_log == NULL) {
1200+
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
1201+
ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0, "get ssl key log failed");
1202+
1203+
return;
1204+
}
1205+
1206+
(void) ngx_write_fd(ssl_key_log->fd, (void *) line, ngx_strlen(line));
1207+
(void) ngx_write_fd(ssl_key_log->fd, (void *) "\n", 1);
1208+
}
1209+
1210+
1211+
static void
1212+
ngx_stream_lua_ssl_cleanup_key_log(void *data)
1213+
{
1214+
ngx_stream_lua_ssl_key_log_t *ssl_key_log = data;
1215+
1216+
if (ngx_close_file(ssl_key_log->fd) == NGX_FILE_ERROR) {
1217+
ngx_ssl_error(NGX_LOG_ALERT, ssl_key_log->ssl->log, 0,
1218+
ngx_close_file_n "(\"%V\") failed", ssl_key_log->name);
1219+
}
1220+
}
1221+
1222+
1223+
static ngx_int_t
1224+
ngx_stream_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
1225+
{
1226+
ngx_fd_t fd;
1227+
ngx_stream_lua_ssl_key_log_t *ssl_key_log;
1228+
ngx_pool_cleanup_t *cln;
1229+
1230+
if (!file->len) {
1231+
return NGX_OK;
1232+
}
1233+
1234+
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
1235+
return NGX_ERROR;
1236+
}
1237+
1238+
if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) {
1239+
return NGX_ERROR;
1240+
}
1241+
1242+
/*
1243+
* append so that existing keylog file contents can be preserved
1244+
*/
1245+
fd = ngx_open_file(file->data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
1246+
NGX_FILE_DEFAULT_ACCESS);
1247+
if (fd == NGX_INVALID_FILE) {
1248+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_open_file_n
1249+
"(\"%V\") failed", file);
1250+
return NGX_ERROR;
1251+
}
1252+
1253+
ssl_key_log = ngx_palloc(cf->pool, sizeof(ngx_stream_lua_ssl_key_log_t));
1254+
if (ssl_key_log == NULL) {
1255+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "ngx_pcalloc() failed");
1256+
return NGX_ERROR;
1257+
}
1258+
1259+
ssl_key_log->ssl = ssl;
1260+
ssl_key_log->fd = fd;
1261+
ssl_key_log->name = *file;
1262+
1263+
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_stream_lua_ssl_key_log_index,
1264+
ssl_key_log) == 0)
1265+
{
1266+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
1267+
"SSL_CTX_set_ex_data() failed");
1268+
return NGX_ERROR;
1269+
}
1270+
1271+
cln = ngx_pool_cleanup_add(cf->pool, 0);
1272+
if (cln == NULL) {
1273+
ngx_stream_lua_ssl_cleanup_key_log(ssl_key_log);
1274+
return NGX_ERROR;
1275+
}
1276+
1277+
cln->handler = ngx_stream_lua_ssl_cleanup_key_log;
1278+
cln->data = ssl_key_log;
1279+
1280+
SSL_CTX_set_keylog_callback(ssl->ctx, key_log_callback);
1281+
1282+
return NGX_OK;
1283+
}
1284+
1285+
11721286
#if (nginx_version >= 1019004)
11731287
static char *
11741288
ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)

src/ngx_stream_lua_ssl.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323

2424
int ngx_stream_lua_ssl_ctx_index = -1;
25+
int ngx_stream_lua_ssl_key_log_index = -1;
2526

2627

2728
ngx_int_t
@@ -40,6 +41,19 @@ ngx_stream_lua_ssl_init(ngx_log_t *log)
4041
}
4142
}
4243

44+
if (ngx_stream_lua_ssl_key_log_index == -1) {
45+
ngx_stream_lua_ssl_key_log_index = SSL_get_ex_new_index(0, NULL,
46+
NULL,
47+
NULL,
48+
NULL);
49+
50+
if (ngx_stream_lua_ssl_key_log_index == -1) {
51+
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
52+
"lua: SSL_get_ex_new_index() for key log failed");
53+
return NGX_ERROR;
54+
}
55+
}
56+
4357
return NGX_OK;
4458
}
4559

src/ngx_stream_lua_ssl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,18 @@ typedef struct {
5353
} ngx_stream_lua_ssl_ctx_t;
5454

5555

56+
typedef struct {
57+
ngx_ssl_t *ssl;
58+
ngx_fd_t fd;
59+
ngx_str_t name;
60+
} ngx_stream_lua_ssl_key_log_t;
61+
62+
5663
ngx_int_t ngx_stream_lua_ssl_init(ngx_log_t *log);
5764

5865

5966
extern int ngx_stream_lua_ssl_ctx_index;
67+
extern int ngx_stream_lua_ssl_key_log_index;
6068

6169

6270
#endif

t/129-ssl-socket.t

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use File::Basename;
66

77
repeat_each(2);
88

9-
plan tests => repeat_each() * (blocks() * 7 + 2);
9+
plan tests => repeat_each() * (blocks() * 7 + 3);
1010

1111
my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
1212
my $openssl_version = eval { `$NginxBinary -V 2>&1` };
@@ -3012,3 +3012,108 @@ handshake rejected while SSL handshaking
30123012
[crit]
30133013
--- timeout: 5
30143014
--- skip_nginx: 7: < 1.25.4
3015+
3016+
3017+
3018+
=== TEST 37: lua_ssl_key_log directive
3019+
--- skip_openssl: 8: < 1.1.1
3020+
--- http_config
3021+
server {
3022+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
3023+
server_name test.com;
3024+
ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
3025+
ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;
3026+
ssl_protocols TLSv1.3;
3027+
3028+
location / {
3029+
content_by_lua_block {
3030+
ngx.exit(200)
3031+
}
3032+
}
3033+
}
3034+
--- stream_server_config
3035+
lua_ssl_protocols TLSv1.3;
3036+
lua_ssl_key_log sslkey.log;
3037+
3038+
content_by_lua_block {
3039+
local sock = ngx.socket.tcp()
3040+
sock:settimeout(2000)
3041+
3042+
do
3043+
local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
3044+
if not ok then
3045+
ngx.say("failed to connect: ", err)
3046+
return
3047+
end
3048+
3049+
ngx.say("connected: ", ok)
3050+
3051+
local session, err = sock:sslhandshake(nil, "test.com")
3052+
if not session then
3053+
ngx.say("failed to do SSL handshake: ", err)
3054+
return
3055+
end
3056+
3057+
ngx.say("ssl handshake: ", type(session))
3058+
3059+
local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n"
3060+
local bytes, err = sock:send(req)
3061+
if not bytes then
3062+
ngx.say("failed to send stream request: ", err)
3063+
return
3064+
end
3065+
3066+
ngx.say("sent stream request: ", bytes, " bytes.")
3067+
3068+
local line, err = sock:receive()
3069+
if not line then
3070+
ngx.say("failed to recieve response status line: ", err)
3071+
return
3072+
end
3073+
3074+
ngx.say("received: ", line)
3075+
3076+
local ok, err = sock:close()
3077+
ngx.say("close: ", ok, " ", err)
3078+
3079+
local f, err = io.open("$TEST_NGINX_SERVER_ROOT/conf/sslkey.log", "r")
3080+
if not f then
3081+
ngx.log(ngx.ERR, "failed to open sslkey.log: ", err)
3082+
return
3083+
end
3084+
3085+
local key_log = f:read("*a")
3086+
ngx.say(key_log)
3087+
f:close()
3088+
end -- do
3089+
collectgarbage()
3090+
}
3091+
3092+
--- stream_response_like
3093+
connected: 1
3094+
ssl handshake: userdata
3095+
sent stream request: 53 bytes.
3096+
received: HTTP/1.1 200 OK
3097+
close: 1 nil
3098+
SERVER_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+
3099+
EXPORTER_SECRET [0-9a-z\s]+
3100+
SERVER_TRAFFIC_SECRET_0 [0-9a-z\s]+
3101+
CLIENT_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+
3102+
CLIENT_TRAFFIC_SECRET_0 [0-9a-z\s]+
3103+
3104+
--- log_level: debug
3105+
--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/
3106+
--- grep_error_log_out eval
3107+
qr/^lua ssl save session: ([0-9A-F]+)
3108+
lua ssl free session: ([0-9A-F]+)
3109+
$/
3110+
--- error_log eval
3111+
[
3112+
'lua ssl server name: "test.com"',
3113+
qr/SSL: TLSv1.3, cipher: "(TLS_AES_256_GCM_SHA384 TLSv1.3|TLS_AES_128_GCM_SHA256 Kx=GENERIC Au=GENERIC Enc=AESGCM\(128\) Mac=AEAD)/,
3114+
]
3115+
--- no_error_log
3116+
SSL reused session
3117+
[error]
3118+
[alert]
3119+
--- timeout: 10

0 commit comments

Comments
 (0)