Skip to content

Commit 8a34bf5

Browse files
willmafhzhuizhuhaomeng
authored andcommitted
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 af6f8c0 commit 8a34bf5

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
@@ -164,6 +164,7 @@ behavior.
164164
* [lua_ssl_certificate_key](https://github.com/openresty/lua-nginx-module#lua_ssl_certificate_key)
165165
* [lua_ssl_trusted_certificate](https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate)
166166
* [lua_ssl_verify_depth](https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth)
167+
* [lua_ssl_key_log](https://github.com/openresty/lua-nginx-module#lua_ssl_key_log)
167168
* [lua_ssl_conf_command](https://github.com/openresty/lua-nginx-module#lua_ssl_conf_command)
168169
* [lua_check_client_abort](https://github.com/openresty/lua-nginx-module#lua_check_client_abort)
169170
* [lua_max_pending_timers](https://github.com/openresty/lua-nginx-module#lua_max_pending_timers)

‎src/ngx_stream_lua_common.h‎

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

‎src/ngx_stream_lua_module.c‎

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ static char *ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data);
4949
#if (NGX_STREAM_SSL)
5050
static ngx_int_t ngx_stream_lua_set_ssl(ngx_conf_t *cf,
5151
ngx_stream_lua_loc_conf_t *llcf);
52+
static void key_log_callback(const ngx_ssl_conn_t *ssl_conn,
53+
const char *line);
54+
static void ngx_stream_lua_ssl_cleanup_key_log(void *data);
55+
static ngx_int_t ngx_stream_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl,
56+
ngx_str_t *file);
5257
#if (nginx_version >= 1019004)
5358
static char *ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post,
5459
void *data);
@@ -453,6 +458,13 @@ static ngx_command_t ngx_stream_lua_cmds[] = {
453458
offsetof(ngx_stream_lua_srv_conf_t, ssl_crl),
454459
NULL },
455460

461+
{ ngx_string("lua_ssl_key_log"),
462+
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
463+
ngx_conf_set_str_slot,
464+
NGX_STREAM_SRV_CONF_OFFSET,
465+
offsetof(ngx_stream_lua_srv_conf_t, ssl_key_log),
466+
NULL },
467+
456468
#if (nginx_version >= 1019004)
457469
{ ngx_string("lua_ssl_conf_command"),
458470
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
@@ -984,6 +996,7 @@ ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
984996
ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
985997
prev->ssl_trusted_certificate, "");
986998
ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
999+
ngx_conf_merge_str_value(conf->ssl_key_log, prev->ssl_key_log, "");
9871000
#if (nginx_version >= 1019004)
9881001
ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands,
9891002
NULL);
@@ -1114,6 +1127,12 @@ ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf)
11141127
return NGX_ERROR;
11151128
}
11161129

1130+
if (ngx_stream_lua_ssl_key_log(cf, lscf->ssl, &lscf->ssl_key_log)
1131+
!= NGX_OK)
1132+
{
1133+
return NGX_ERROR;
1134+
}
1135+
11171136
#if (nginx_version >= 1019004)
11181137
if (ngx_ssl_conf_commands(cf, lscf->ssl, lscf->ssl_conf_commands)
11191138
!= NGX_OK)
@@ -1126,6 +1145,101 @@ ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf)
11261145
}
11271146

11281147

1148+
static void
1149+
key_log_callback(const ngx_ssl_conn_t *ssl_conn, const char *line)
1150+
{
1151+
ngx_stream_lua_ssl_key_log_t *ssl_key_log;
1152+
ngx_connection_t *c;
1153+
1154+
ssl_key_log = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
1155+
ngx_stream_lua_ssl_key_log_index);
1156+
if (ssl_key_log == NULL) {
1157+
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
1158+
ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0, "get ssl key log failed");
1159+
1160+
return;
1161+
}
1162+
1163+
(void) ngx_write_fd(ssl_key_log->fd, (void *) line, ngx_strlen(line));
1164+
(void) ngx_write_fd(ssl_key_log->fd, (void *) "\n", 1);
1165+
}
1166+
1167+
1168+
static void
1169+
ngx_stream_lua_ssl_cleanup_key_log(void *data)
1170+
{
1171+
ngx_stream_lua_ssl_key_log_t *ssl_key_log = data;
1172+
1173+
if (ngx_close_file(ssl_key_log->fd) == NGX_FILE_ERROR) {
1174+
ngx_ssl_error(NGX_LOG_ALERT, ssl_key_log->ssl->log, 0,
1175+
ngx_close_file_n "(\"%V\") failed", ssl_key_log->name);
1176+
}
1177+
}
1178+
1179+
1180+
static ngx_int_t
1181+
ngx_stream_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
1182+
{
1183+
ngx_fd_t fd;
1184+
ngx_stream_lua_ssl_key_log_t *ssl_key_log;
1185+
ngx_pool_cleanup_t *cln;
1186+
1187+
if (!file->len) {
1188+
return NGX_OK;
1189+
}
1190+
1191+
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
1192+
return NGX_ERROR;
1193+
}
1194+
1195+
if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) {
1196+
return NGX_ERROR;
1197+
}
1198+
1199+
/*
1200+
* append so that existing keylog file contents can be preserved
1201+
*/
1202+
fd = ngx_open_file(file->data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
1203+
NGX_FILE_DEFAULT_ACCESS);
1204+
if (fd == NGX_INVALID_FILE) {
1205+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_open_file_n
1206+
"(\"%V\") failed", file);
1207+
return NGX_ERROR;
1208+
}
1209+
1210+
ssl_key_log = ngx_palloc(cf->pool, sizeof(ngx_stream_lua_ssl_key_log_t));
1211+
if (ssl_key_log == NULL) {
1212+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "ngx_pcalloc() failed");
1213+
return NGX_ERROR;
1214+
}
1215+
1216+
ssl_key_log->ssl = ssl;
1217+
ssl_key_log->fd = fd;
1218+
ssl_key_log->name = *file;
1219+
1220+
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_stream_lua_ssl_key_log_index,
1221+
ssl_key_log) == 0)
1222+
{
1223+
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
1224+
"SSL_CTX_set_ex_data() failed");
1225+
return NGX_ERROR;
1226+
}
1227+
1228+
cln = ngx_pool_cleanup_add(cf->pool, 0);
1229+
if (cln == NULL) {
1230+
ngx_stream_lua_ssl_cleanup_key_log(ssl_key_log);
1231+
return NGX_ERROR;
1232+
}
1233+
1234+
cln->handler = ngx_stream_lua_ssl_cleanup_key_log;
1235+
cln->data = ssl_key_log;
1236+
1237+
SSL_CTX_set_keylog_callback(ssl->ctx, key_log_callback);
1238+
1239+
return NGX_OK;
1240+
}
1241+
1242+
11291243
#if (nginx_version >= 1019004)
11301244
static char *
11311245
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
@@ -49,10 +49,18 @@ typedef struct {
4949
} ngx_stream_lua_ssl_ctx_t;
5050

5151

52+
typedef struct {
53+
ngx_ssl_t *ssl;
54+
ngx_fd_t fd;
55+
ngx_str_t name;
56+
} ngx_stream_lua_ssl_key_log_t;
57+
58+
5259
ngx_int_t ngx_stream_lua_ssl_init(ngx_log_t *log);
5360

5461

5562
extern int ngx_stream_lua_ssl_ctx_index;
63+
extern int ngx_stream_lua_ssl_key_log_index;
5664

5765

5866
#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)