Skip to content

Commit

Permalink
Added support for SSLKEYLOGFILE
Browse files Browse the repository at this point in the history
* 'admin-ssl_keylog_file' variable has been introduced to allow users to specify the path for the SSLKEYLOG file. The file path can be an absolute path or relative to the ProxySQL data directory.
* Assigning an empty path to the 'admin-ssl_keylog_file' variable signifies that the SSLKEYLOG file feature is disabled.
* In case an invalid path is provided for the SSLKEYLOG file, ProxySQL will automatically revert to the last valid path value.
* 'PROXYSQL FLUSH LOGS' command can be used to rotate SSLKEYLOG file.

# Conflicts:
#	lib/mysql_connection.cpp
  • Loading branch information
rahim-kanji committed Jun 2, 2023
1 parent 3efa344 commit 8f899e3
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 6 deletions.
1 change: 1 addition & 0 deletions include/proxysql.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "proxysql_debug.h"
#include "proxysql_macros.h"
#include "proxysql_coredump.h"
#include "proxysql_sslkeylog.h"
#include "jemalloc.h"

#ifndef NOJEM
Expand Down
1 change: 1 addition & 0 deletions include/proxysql_admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class ProxySQL_Admin {
#endif /* DEBUG */
int coredump_generation_interval_ms;
int coredump_generation_threshold;
char* ssl_keylog_file;
} variables;

unsigned long long last_p_memory_metrics_ts;
Expand Down
5 changes: 4 additions & 1 deletion include/proxysql_glovars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ class ProxySQL_GlobalVariables {
char * sqlite3_plugin;
char * web_interface_plugin;
char * ldap_auth_plugin;
SSL * get_SSL_ctx();
SSL_CTX *get_SSL_ctx();
SSL *get_SSL_new();
void get_SSL_pem_mem(char **key, char **cert);
std::shared_ptr<prometheus::Registry> prometheus_registry { nullptr };
struct {
Expand Down Expand Up @@ -126,6 +127,8 @@ class ProxySQL_GlobalVariables {
bool clickhouse_server;
#endif /* PROXYSQLCLICKHOUSE */
pthread_mutex_t ext_glomth_mutex;

bool ssl_keylog_enabled;
} global;
struct mysql {
char *server_version;
Expand Down
10 changes: 10 additions & 0 deletions include/proxysql_sslkeylog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __PROXYSQL_SSLKEYLOG_H
#define __PROXYSQL_SSLKEYLOG_H
#include "proxysql.h"

void proxysql_keylog_init();
bool proxysql_keylog_open(const char* keylog_file);
void proxysql_keylog_close(bool lock = true);
void proxysql_keylog_write_line_callback(const SSL *ssl, const char* line);

#endif // __PROXYSQL_SSLKEYLOG_H
2 changes: 1 addition & 1 deletion lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ MYCXXFLAGS=-std=c++11 $(MYCFLAGS) $(PSQLCH) $(ENABLE_EPOLL)
default: libproxysql.a
.PHONY: default

_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo
_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo
OBJ_CXX = $(patsubst %,$(ODIR)/%,$(_OBJ_CXX))
HEADERS = ../include/*.h ../include/*.hpp

Expand Down
7 changes: 6 additions & 1 deletion lib/MySQL_Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5360,11 +5360,16 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
client_myds->DSS=STATE_SSL_INIT;
client_myds->rbio_ssl = BIO_new(BIO_s_mem());
client_myds->wbio_ssl = BIO_new(BIO_s_mem());
client_myds->ssl = GloVars.get_SSL_ctx();
client_myds->ssl = GloVars.get_SSL_new();
SSL_set_fd(client_myds->ssl, client_myds->fd);
SSL_set_accept_state(client_myds->ssl);
SSL_set_bio(client_myds->ssl, client_myds->rbio_ssl, client_myds->wbio_ssl);
l_free(pkt->size,pkt->ptr);

SSL_CTX* ssl_ctx = GloVars.get_SSL_ctx();
if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) {
SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback);
}
return;
}

Expand Down
48 changes: 47 additions & 1 deletion lib/ProxySQL_Admin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include "ProxySQL_Statistics.hpp"
#include "MySQL_Logger.hpp"
#include "SQLite3_Server.h"

#include "Web_Interface.hpp"

#include <dirent.h>
Expand Down Expand Up @@ -684,6 +683,7 @@ static char * admin_variables_names[]= {
#endif /* DEBUG */
(char *)"coredump_generation_interval_ms",
(char *)"coredump_generation_threshold",
(char *)"ssl_keylog_file",
NULL
};

Expand Down Expand Up @@ -1803,6 +1803,18 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_
GloMyLogger->flush_log();
}
SPA->flush_error_log();
proxysql_keylog_close();
char* ssl_keylog_file = SPA->get_variable("ssl_keylog_file");
if (ssl_keylog_file != NULL) {
if (strlen(ssl_keylog_file) > 0) {
if (proxysql_keylog_open(ssl_keylog_file) == false) {
// re-opening file failed, setting ssl_keylog_enabled to false
GloVars.global.ssl_keylog_enabled = false;
proxy_warning("Cannot open file '%s' for writing.\n", ssl_keylog_file);
}
}
free(ssl_keylog_file);
}
SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL);
return false;
}
Expand Down Expand Up @@ -5933,6 +5945,7 @@ ProxySQL_Admin::ProxySQL_Admin() :
#endif /* DEBUG */
variables.coredump_generation_interval_ms = 30000;
variables.coredump_generation_threshold = 10;
variables.ssl_keylog_file = strdup("");
last_p_memory_metrics_ts = 0;
// create the scheduler
scheduler=new ProxySQL_External_Scheduler();
Expand Down Expand Up @@ -6436,6 +6449,9 @@ void ProxySQL_Admin::admin_shutdown() {
if (variables.telnet_stats_ifaces) {
free(variables.telnet_stats_ifaces);
}
if (variables.ssl_keylog_file) {
free(variables.ssl_keylog_file);
}
};

ProxySQL_Admin::~ProxySQL_Admin() {
Expand Down Expand Up @@ -8082,6 +8098,7 @@ char * ProxySQL_Admin::get_variable(char *name) {
sprintf(intbuf,"%d",variables.coredump_generation_threshold);
return strdup(intbuf);
}
if (!strcasecmp(name,"ssl_keylog_file")) return s_strdup(variables.ssl_keylog_file);
return NULL;
}

Expand Down Expand Up @@ -8775,6 +8792,35 @@ bool ProxySQL_Admin::set_variable(char *name, char *value, bool lock) { // this
return false;
}
}
if (!strcasecmp(name, "ssl_keylog_file")) {
if (strcmp(variables.ssl_keylog_file, value)) {
if (vallen == 0 || strcmp(value, "(null)") == 0) {
proxysql_keylog_close();
free(variables.ssl_keylog_file);
variables.ssl_keylog_file = strdup("");
GloVars.global.ssl_keylog_enabled = false;
} else {
char* sslkeylogfile = NULL;

if (value[0] == '/') { // absolute path
sslkeylogfile = strdup(value);
} else { // relative path
sslkeylogfile = (char*)malloc(strlen(GloVars.datadir) + strlen(value) + 1);
sprintf(sslkeylogfile, "%s/%s", GloVars.datadir, value);
}
if (proxysql_keylog_open(sslkeylogfile) == false) {
free(sslkeylogfile);
proxy_warning("Cannot open file '%s' for writing.\n", value);
return false;
}
//free(sslkeylogfile);
free(variables.ssl_keylog_file);
variables.ssl_keylog_file = sslkeylogfile; //strdup(value);
GloVars.global.ssl_keylog_enabled = true;
}
}
return true;
}
return false;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/ProxySQL_GloVars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() :
#ifdef PROXYSQLCLICKHOUSE
global.clickhouse_server=false;
#endif /* PROXYSQLCLICKHOUSE */
global.ssl_keylog_enabled = false;
opt=new ez::ezOptionParser();
opt->overview="High Performance Advanced Proxy for MySQL";
opt->syntax="proxysql [OPTIONS]";
Expand Down Expand Up @@ -296,6 +297,8 @@ void ProxySQL_GlobalVariables::process_opts_pre() {
init_debug_struct();
#endif
init_coredump_struct();

proxysql_keylog_init();
};

void ProxySQL_GlobalVariables::process_opts_post() {
Expand Down
10 changes: 9 additions & 1 deletion lib/mysql_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "MySQL_Data_Stream.h"
#include "query_processor.h"
#include "MySQL_Variables.h"

#include <atomic>

// some of the code that follows is from mariadb client library memory allocator
Expand Down Expand Up @@ -1167,6 +1166,15 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) {
//vio_blocking(mysql->net.vio, FALSE, 0);
//fcntl(mysql->net.vio->sd, F_SETFL, O_RDWR|O_NONBLOCK);
//}
if (mysql->options.use_ssl == 1) {
SSL_CTX* ssl_ctx = NULL;
P_MARIADB_TLS* matls = (P_MARIADB_TLS*)mysql->net.pvio->ctls;
if (matls != NULL) ssl_ctx = SSL_get_SSL_CTX((SSL*)matls->ssl);

if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) {
SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback);
}
}
MySQL_Monitor::update_dns_cache_from_mysql_conn(mysql);
break;
case ASYNC_CONNECT_FAILED:
Expand Down
96 changes: 96 additions & 0 deletions lib/proxysql_sslkeylog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "proxysql_sslkeylog.h"

// https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html

#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)

#define CLIENT_RANDOM_SIZE 32

/*
* The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
* secret size depends on the cipher suite's hash function which is 32 bytes
* for SHA-256 and 48 bytes for SHA-384.
*/
#define SECRET_MAXLEN 48

static pthread_rwlock_t keylog_file_rwlock;

/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
static FILE *keylog_file_fp = NULL;

FILE* proxysql_open_file(const char* file) {
FILE *file_tmp = fopen(file, "a+");
if (file_tmp) {
if (setvbuf(file_tmp, NULL, _IOLBF, 4096)) {
fclose(file_tmp);
file_tmp = NULL;
goto __exit;
}
}
__exit:
return file_tmp;
}

void proxysql_keylog_init() {
pthread_rwlock_init(&keylog_file_rwlock, nullptr);
keylog_file_fp = NULL;
}

bool proxysql_keylog_open(const char* keylog_file)
{
assert(keylog_file);
FILE* keylog_file_tmp = proxysql_open_file(keylog_file);
if (!keylog_file_tmp) return false;
pthread_rwlock_wrlock(&keylog_file_rwlock);
proxysql_keylog_close(false);
keylog_file_fp = keylog_file_tmp;
pthread_rwlock_unlock(&keylog_file_rwlock);
return true;
}

void proxysql_keylog_close(bool lock)
{
if (lock)
pthread_rwlock_wrlock(&keylog_file_rwlock);
if(keylog_file_fp) {
fclose(keylog_file_fp);
keylog_file_fp = NULL;
}
if (lock)
pthread_rwlock_unlock(&keylog_file_rwlock);
}

void proxysql_keylog_write_line_callback(const SSL *ssl, const char *line)
{
(void)ssl; // to fix warning

// checking keylog_file_fp without acquiring a lock is safe, as it is checked again after acquring lock
if (!keylog_file_fp) return;

/* The current maximum valid keylog line length LF and NUL is 195. */
size_t linelen;
char buf[256];

pthread_rwlock_rdlock(&keylog_file_rwlock);
if(!keylog_file_fp || !line) {
goto __exit;
}

linelen = strlen(line);
if(linelen == 0 || linelen > sizeof(buf) - 2) {
/* Empty line or too big to fit in a LF and NUL. */
goto __exit;
}

memcpy(buf, line, linelen);
if(line[linelen - 1] != '\n') {
buf[linelen++] = '\n';
}
buf[linelen] = '\0';

/* as we are using rwlock, using fputs as it's thread-safe*/
fputs(buf, keylog_file_fp);

__exit:
pthread_rwlock_unlock(&keylog_file_rwlock);
}
8 changes: 7 additions & 1 deletion src/proxysql_global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
#include "cpp.h"
//ProxySQL_GlobalVariables GloVars;

SSL * ProxySQL_GlobalVariables::get_SSL_ctx() {
SSL_CTX * ProxySQL_GlobalVariables::get_SSL_ctx() {
// take the mutex
std::lock_guard<std::mutex> lock(global.ssl_mutex);
return GloVars.global.ssl_ctx;
}

SSL * ProxySQL_GlobalVariables::get_SSL_new() {
// take the mutex
std::lock_guard<std::mutex> lock(global.ssl_mutex);
return SSL_new(GloVars.global.ssl_ctx);
Expand Down

0 comments on commit 8f899e3

Please sign in to comment.