Skip to content

Commit

Permalink
Adding TLS session key logging capability
Browse files Browse the repository at this point in the history
Adding the ability to log TLS session keys to a log file for packet
capture decryption purposes.

This adds the following reloadable configuration:
proxy.config.ssl.keylog_file

Since this can work for QUIC as well, this also deprecates:
proxy.config.quic.client.keylog_file
  • Loading branch information
bneradt committed Sep 28, 2021
1 parent f4274a8 commit bfbce0f
Show file tree
Hide file tree
Showing 23 changed files with 399 additions and 61 deletions.
15 changes: 15 additions & 0 deletions build/crypto.m4
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,21 @@ AC_DEFUN([TS_CHECK_CRYPTO_OCSP], [
AC_SUBST(use_tls_ocsp)
])

dnl
dnl Since OpenSSL 1.1.1
dnl
AC_DEFUN([TS_CHECK_CRYPTO_KEYLOGGING], [
_keylogging_saved_LIBS=$LIBS
TS_ADDTO(LIBS, [$OPENSSL_LIBS])
AC_CHECK_FUNCS(SSL_CTX_set_keylog_callback, [enable_tls_keylogging=yes], [enable_tls_keylogging=no])
LIBS=$_keylogging_saved_LIBS
AC_MSG_CHECKING(whether to enable TLS keylogging support)
AC_MSG_RESULT([$enable_tls_keylogging])
TS_ARG_ENABLE_VAR([has], [tls-keylogging])
AC_SUBST(has_tls_keylogging)
])

dnl
dnl Since OpenSSL 1.1.1
dnl
Expand Down
3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,9 @@ TS_CHECK_CRYPTO_OCSP
# Check for SSL_CTX_set_ciphersuites call
TS_CHECK_CRYPTO_SET_CIPHERSUITES

# Check for TOLS keylogging support.
TS_CHECK_CRYPTO_KEYLOGGING

# Check for openssl early data support
TS_CHECK_EARLY_DATA

Expand Down
18 changes: 12 additions & 6 deletions doc/admin-guide/files/records.config.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3775,6 +3775,18 @@ SSL Termination

See :ref:`admin-performance-timeouts` for more discussion on |TS| timeouts.

.. ts:cv:: CONFIG proxy.config.ssl.keylog_file STRING NULL
:reloadable:

If configured, TLS session keys for TLS connections will be logged to the
specified file. This file is formatted in such a way that it can be
conveniently imported into tools such as Wireshark to decrypt packet
captures. This should only be used for debugging purposes since the data in
the keylog file can be used to decrypt the otherwise encrypted traffic. A
NULL value for this disables the feature.

This feature is disabled by default.

Client-Related Configuration
----------------------------

Expand Down Expand Up @@ -4234,12 +4246,6 @@ removed in the future without prior notice.
If specified, TLS session data will be stored to the file, and will be used
for resuming a session.

.. ts:cv:: CONFIG proxy.config.quic.client.keylog_file STRING ""
:reloadable:

Only available for :program:`traffic_quic`.
If specified, key information will be stored to the file.

.. ts:cv:: CONFIG proxy.config.quic.no_activity_timeout_in INT 30000
:reloadable:

Expand Down
1 change: 1 addition & 0 deletions include/tscore/ink_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#define TS_USE_TLS13 @use_tls13@
#define TS_USE_QUIC @use_quic@
#define TS_USE_TLS_SET_CIPHERSUITES @use_tls_set_ciphersuites@
#define TS_HAS_TLS_KEYLOGGING @has_tls_keylogging@
#define TS_USE_LINUX_NATIVE_AIO @use_linux_native_aio@
#define TS_USE_REMOTE_UNWINDING @use_remote_unwinding@
#define TS_USE_TLS_OCSP @use_tls_ocsp@
Expand Down
2 changes: 2 additions & 0 deletions iocore/net/P_SSLConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ struct SSLConfigParams : public ConfigInfo {
char *server_groups_list;
char *client_groups_list;

char *keylog_file;

static uint32_t server_max_early_data;
static uint32_t server_recv_max_early_data;
static bool server_allow_early_data_params;
Expand Down
104 changes: 103 additions & 1 deletion iocore/net/P_SSLUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
#include "records/I_RecCore.h"
#include "P_SSLCertLookup.h"

#include <set>
#include <map>
#include <mutex>
#include <set>
#include <shared_mutex>

struct SSLConfigParams;
class SSLNetVConnection;
Expand All @@ -60,6 +62,105 @@ struct SSLLoadingContext {
explicit SSLLoadingContext(SSL_CTX *c, SSLCertContextType ctx_type) : ctx(c), ctx_type(ctx_type) {}
};

/** A class for handling TLS secrets logging. */
class TLSKeyLogger
{
public:
TLSKeyLogger(const TLSKeyLogger &) = delete;
TLSKeyLogger &operator=(const TLSKeyLogger &) = delete;

~TLSKeyLogger()
{
std::unique_lock lock{_mutex};
close_keylog_file();
}

/** A callback for TLS secret key logging.
*
* This is the callback registered with OpenSSL's SSL_CTX_set_keylog_callback
* to log TLS secrets if the user enabled that feature. For more information
* about this callback, see OpenSSL's documentation of
* SSL_CTX_set_keylog_callback.
*
* @param[in] ssl The SSL object associated with the connection.
* @param[in] line The line to place in the keylog file.
*/
static void
ssl_keylog_cb(const SSL *ssl, const char *line)
{
instance().log(line);
}

/** Return whether TLS key logging is enabled.
*
* @return True if TLS session key logging is enabled, false otherwise.
*/
static bool
is_enabled()
{
return instance()._fd >= 0;
}

/** Enable keylogging.
*
* @param[in] keylog_file The path to the file to log TLS secrets to.
*/
static void
enable_keylogging(const char *keylog_file)
{
instance().enable_keylogging_internal(keylog_file);
}

/** Disable TLS secrets logging. */
static void
disable_keylogging()
{
instance().disable_keylogging_internal();
}

private:
TLSKeyLogger() = default;

/** Return the TLSKeyLogger singleton.
*
* We use a getter rather than a class static singleton member so that the
* construction of the singleton delayed until after TLS configuration is
* processed.
*/
static TLSKeyLogger &
instance()
{
static TLSKeyLogger instance;
return instance;
}

/** Close the file descriptor for the key log file.
*
* @note This assumes that a unique lock has been acquired for _mutex.
*/
void close_keylog_file();

/** A TLS secret line to log to the keylog file.
*
* @param[in] line A line to log to the keylog file.
*/
void log(const char *line);

/** Enable TLS keylogging in the instance singleton. */
void enable_keylogging_internal(const char *keylog_file);

/** Disable TLS keylogging in the instance singleton. */
void disable_keylogging_internal();

private:
/** A file descriptor for the log file receiving the TLS secrets. */
int _fd = -1;

/** A mutex to coordinate dynamically changing TLS logging config changes and
* logging to the TLS log file. */
std::shared_mutex _mutex;
};

/**
@brief Load SSL certificates from ssl_multicert.config and setup SSLCertLookup for SSLCertificateConfig
*/
Expand Down Expand Up @@ -122,6 +223,7 @@ class SSLMultiCertConfigLoader
virtual bool _set_info_callback(SSL_CTX *ctx);
virtual bool _set_npn_callback(SSL_CTX *ctx);
virtual bool _set_alpn_callback(SSL_CTX *ctx);
virtual bool _set_keylog_callback(SSL_CTX *ctx);
};

// Create a new SSL server context fully configured (cert and keys are optional).
Expand Down
4 changes: 2 additions & 2 deletions iocore/net/QUICNetVConnection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2323,8 +2323,8 @@ QUICNetVConnection::_setup_handshake_protocol(const shared_SSL_CTX &ctx)
{
// Initialize handshake protocol specific stuff
// For QUICv1 TLS is the only option
QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx.get(), this->direction(), this->options,
this->_quic_config->client_session_file(), this->_quic_config->client_keylog_file());
QUICTLS *tls =
new QUICTLS(this->_pp_key_info, ctx.get(), this->direction(), this->options, this->_quic_config->client_session_file());
SSL_set_ex_data(tls->ssl_handle(), QUIC::ssl_quic_qc_index, static_cast<QUICConnection *>(this));
TLSBasicSupport::bind(tls->ssl_handle(), this);
TLSSessionResumptionSupport::bind(tls->ssl_handle(), this);
Expand Down
2 changes: 1 addition & 1 deletion iocore/net/QUICPacketHandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ QUICPacketHandlerIn::_send_invalid_token_error(const uint8_t *initial_packet, ui
QUICPacketFactory pf(ppki);
QUICPacketHeaderProtector php(ppki);
QUICCertConfig::scoped_config server_cert;
QUICTLS tls(ppki, server_cert->ssl_default.get(), NET_VCONNECTION_IN, {}, "", "");
QUICTLS tls(ppki, server_cert->ssl_default.get(), NET_VCONNECTION_IN, {}, "");
tls.initialize_key_materials(dcid_in_initial, version_in_initial);

// Create INITIAL packet
Expand Down
1 change: 1 addition & 0 deletions iocore/net/SSLClientCoordinator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ SSLClientCoordinator::startup()
sslClientUpdate->attach("proxy.config.ssl.client.cert.filename");
sslClientUpdate->attach("proxy.config.ssl.client.private_key.path");
sslClientUpdate->attach("proxy.config.ssl.client.private_key.filename");
sslClientUpdate->attach("proxy.config.ssl.keylog_file");
SSLConfig::startup();
sslClientUpdate->attach("proxy.config.ssl.servername.filename");
SNIConfig::startup();
Expand Down
7 changes: 7 additions & 0 deletions iocore/net/SSLClientUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "P_Net.h"
#include "P_SSLClientUtils.h"
#include "P_SSLConfig.h"
#include "P_SSLNetVConnection.h"
#include "YamlSNIConfig.h"
#include "SSLDiags.h"
Expand Down Expand Up @@ -236,6 +237,12 @@ SSLInitClientContext(const SSLConfigParams *params)
SSL_CTX_sess_set_new_cb(client_ctx, ssl_new_session_callback);
}

#if TS_HAS_TLS_KEYLOGGING
if (unlikely(TLSKeyLogger::is_enabled())) {
SSL_CTX_set_keylog_callback(client_ctx, TLSKeyLogger::ssl_keylog_cb);
}
#endif

return client_ctx;

fail:
Expand Down
10 changes: 10 additions & 0 deletions iocore/net/SSLConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "P_SSLSNI.h"
#include "P_SSLCertLookup.h"
#include "P_SSLSNI.h"
#include "P_SSLUtils.h"
#include "SSLDiags.h"
#include "SSLSessionCache.h"
#include "SSLSessionTicket.h"
Expand Down Expand Up @@ -114,6 +115,7 @@ SSLConfigParams::reset()
client_tls13_cipher_suites = nullptr;
server_groups_list = nullptr;
client_groups_list = nullptr;
keylog_file = nullptr;
client_ctx = nullptr;
clientCertLevel = client_verify_depth = verify_depth = 0;
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
Expand Down Expand Up @@ -152,6 +154,7 @@ SSLConfigParams::cleanup()
client_tls13_cipher_suites = static_cast<char *>(ats_free_null(client_tls13_cipher_suites));
server_groups_list = static_cast<char *>(ats_free_null(server_groups_list));
client_groups_list = static_cast<char *>(ats_free_null(client_groups_list));
keylog_file = static_cast<char *>(ats_free_null(keylog_file));

cleanupCTXTable();
reset();
Expand Down Expand Up @@ -423,6 +426,13 @@ SSLConfigParams::initialize()

REC_ReadConfigStringAlloc(client_groups_list, "proxy.config.ssl.client.groups_list");

REC_ReadConfigStringAlloc(keylog_file, "proxy.config.ssl.keylog_file");
if (keylog_file == nullptr) {
TLSKeyLogger::disable_keylogging();
} else {
TLSKeyLogger::enable_keylogging(keylog_file);
}

REC_ReadConfigInt32(ssl_allow_client_renegotiation, "proxy.config.ssl.allow_client_renegotiation");

REC_ReadConfigInt32(ssl_misc_max_iobuffer_size_index, "proxy.config.ssl.misc.io.max_buffer_index");
Expand Down
Loading

0 comments on commit bfbce0f

Please sign in to comment.