- tune.sndbuf.client
- tune.sndbuf.server
- tune.ssl.cachesize
+ - tune.ssl.keylog
- tune.ssl.lifetime
- tune.ssl.force-private-cache
- tune.ssl.maxrecord
this case, adding a first layer of hash-based load balancing before the SSL
layer might limit the impact of the lack of session sharing.
+tune.ssl.keylog { on | off }
+ This option activates the logging of the TLS keys. It should be used with
+ care as it will consume more memory per SSL session and could decrease
+ performances. This is disabled by default.
+
+ These sample fetches should be used to generate the SSLKEYLOGFILE that is
+ required to decipher traffic with wireshark.
+
+ https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+
+ The SSLKEYLOG is a series of lines which are formatted this way:
+
+ <Label> <space> <ClientRandom> <space> <Secret>
+
+ The ClientRandom is provided by the %[ssl_fc_client_random,hex] sample
+ fetch, the secret and the Label could be find in the array below. You need
+ to generate a SSLKEYLOGFILE with all the labels in this array.
+
+ The following sample fetches are hexadecimal strings and does not need to be
+ converted.
+
+ SSLKEYLOGFILE Label | Sample fetches for the Secrets
+ --------------------------------|-----------------------------------------
+ CLIENT_EARLY_TRAFFIC_SECRET | %[ssl_fc_client_early_traffic_secret]
+ CLIENT_HANDSHAKE_TRAFFIC_SECRET | %[ssl_fc_client_handshake_traffic_secret]
+ SERVER_HANDSHAKE_TRAFFIC_SECRET | %[ssl_fc_server_handshake_traffic_secret]
+ CLIENT_TRAFFIC_SECRET_0 | %[ssl_fc_client_traffic_secret_0]
+ SERVER_TRAFFIC_SECRET_0 | %[ssl_fc_server_traffic_secret_0]
+ EARLY_EXPORTER_SECRET | %[ssl_fc_exporter_secret]
+ EXPORTER_SECRET | %[ssl_fc_early_exporter_secret]
+
+ This is only available with OpenSSL 1.1.1, and useful with TLS1.3 session.
+
+ If you want to generate the content of a SSLKEYLOGFILE with TLS < 1.3, you
+ only need this line:
+
+ "CLIENT_RANDOM %[ssl_fc_client_random,hex] %[ssl_fc_session_key,hex]"
+
tune.ssl.lifetime <timeout>
Sets how long a cached SSL session may remain valid. This time is expressed
in seconds and defaults to 300 (5 min). It is important to understand that it
was made over an SSL/TLS transport layer. It is useful to to decrypt traffic
sent using ephemeral ciphers. This requires OpenSSL >= 1.1.0, or BoringSSL.
+ssl_fc_client_early_traffic_secret : string
+ Return the CLIENT_EARLY_TRAFFIC_SECRET as an hexadecimal string for the
+ front connection when the incoming connection was made over a TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
+ssl_fc_client_handshake_traffic_secret : string
+ Return the CLIENT_HANDSHAKE_TRAFFIC_SECRET as an hexadecimal string for the
+ front connection when the incoming connection was made over a TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
+ssl_fc_client_traffic_secret_0 : string
+ Return the CLIENT_TRAFFIC_SECRET_0 as an hexadecimal string for the
+ front connection when the incoming connection was made over a TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
+ssl_fc_exporter_secret : string
+ Return the EXPORTER_SECRET as an hexadecimal string for the
+ front connection when the incoming connection was made over a TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
+ssl_fc_early_exporter_secret : string
+ Return the EARLY_EXPORTER_SECRET as an hexadecimal string for the
+ front connection when the incoming connection was made over an TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
ssl_fc_has_crt : boolean
Returns true if a client certificate is present in an incoming connection over
SSL/TLS transport layer. Useful if 'verify' statement is set to 'optional'.
returns the TLS unique ID as defined in RFC5929 section 3. The unique id
can be encoded to base64 using the converter: "ssl_bc_unique_id,base64".
+ssl_fc_server_handshake_traffic_secret : string
+ Return the SERVER_HANDSHAKE_TRAFFIC_SECRET as an hexadecimal string for the
+ front connection when the incoming connection was made over a TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
+ssl_fc_server_traffic_secret_0 : string
+ Return the SERVER_TRAFFIC_SECRET_0 as an hexadecimal string for the
+ front connection when the incoming connection was made over an TLS 1.3
+ transport layer.
+ Require OpenSSL >= 1.1.1. This is one of the keys dumped by the OpenSSL
+ keylog callback to generate the SSLKEYLOGFILE. The SSL Key logging must be
+ activated with "tune.ssl.keylog on" in the global section. See also
+ "tune.ssl.keylog"
+
ssl_fc_server_random : binary
Returns the server random of the front connection when the incoming connection
was made over an SSL/TLS transport layer. It is useful to to decrypt traffic
char ciphersuite[0];
};
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+#define SSL_KEYLOG_MAX_SECRET_SIZE 129
+
+struct ssl_keylog {
+ /*
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+ */
+ char *client_random;
+
+ /* TLS 1.3 */
+ char *client_early_traffic_secret;
+ char *client_handshake_traffic_secret;
+ char *server_handshake_traffic_secret;
+ char *client_traffic_secret_0;
+ char *server_traffic_secret_0;
+ char *exporter_secret;
+ char *early_exporter_secret;
+};
+#endif
+
struct ssl_sock_ctx {
struct connection *conn;
SSL *ssl;
unsigned int default_dh_param; /* SSL maximum DH parameter size */
int ctx_cache; /* max number of entries in the ssl_ctx cache. */
int capture_cipherlist; /* Size of the cipherlist buffer. */
+ int keylog; /* activate keylog */
int extra_files; /* which files not defined in the configuration file are we looking for */
};
extern int nb_engines;
extern struct xprt_ops ssl_sock;
extern int ssl_capture_ptr_index;
+extern int ssl_keylog_index;
+extern struct pool_head *pool_head_ssl_keylog;
+extern struct pool_head *pool_head_ssl_keylog_str;
int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *, SSL_CTX *ctx, char **err);
int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf);
return 0;
}
+/* init the SSLKEYLOGFILE pool */
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+static int ssl_parse_global_keylog(char **args, int section_type, struct proxy *curpx,
+ struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+
+ if (too_many_args(1, args, err, NULL))
+ return -1;
+
+ if (strcmp(args[1], "on") == 0)
+ global_ssl.keylog = 1;
+ else if (strcmp(args[1], "off") == 0)
+ global_ssl.keylog = 0;
+ else {
+ memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
+ return -1;
+ }
+
+ if (pool_head_ssl_keylog) /* already configured */
+ return 0;
+
+ pool_head_ssl_keylog = create_pool("ssl-keylogfile", sizeof(struct ssl_keylog), MEM_F_SHARED);
+ if (!pool_head_ssl_keylog) {
+ memprintf(err, "Out of memory error.");
+ return -1;
+ }
+
+ pool_head_ssl_keylog_str = create_pool("ssl-keylogfile-str", sizeof(char) * SSL_KEYLOG_MAX_SECRET_SIZE, MEM_F_SHARED);
+ if (!pool_head_ssl_keylog_str) {
+ memprintf(err, "Out of memory error.");
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
/* parse "ssl.force-private-cache".
* Returns <0 on alert, >0 on warning, 0 on success.
*/
{ CFG_GLOBAL, "tune.ssl.maxrecord", ssl_parse_global_int },
{ CFG_GLOBAL, "tune.ssl.ssl-ctx-cache-size", ssl_parse_global_int },
{ CFG_GLOBAL, "tune.ssl.capture-cipherlist-size", ssl_parse_global_capture_cipherlist },
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ { CFG_GLOBAL, "tune.ssl.keylog", ssl_parse_global_keylog },
+#endif
{ CFG_GLOBAL, "ssl-default-bind-ciphers", ssl_parse_global_ciphers },
{ CFG_GLOBAL, "ssl-default-server-ciphers", ssl_parse_global_ciphers },
#if ((HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL) || defined(LIBRESSL_VERSION_NUMBER))
return 1;
}
+/* Dump the SSL keylog, it only works with "tune.ssl.keylog 1" */
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+static int smp_fetch_ssl_x_keylog(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+ struct connection *conn;
+ struct ssl_keylog *keylog;
+ SSL *ssl;
+ char *src = NULL;
+ const char *sfx;
+
+ conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
+
+ if (conn->flags & CO_FL_WAIT_XPRT) {
+ smp->flags |= SMP_F_MAY_CHANGE;
+ return 0;
+ }
+
+ ssl = ssl_sock_get_ssl_object(conn);
+ if (!ssl)
+ return 0;
+
+ keylog = SSL_get_ex_data(ssl, ssl_keylog_index);
+ if (!keylog)
+ return 0;
+
+ sfx = kw + strlen("ssl_xx_");
+
+ if (strcmp(sfx, "client_early_traffic_secret") == 0) {
+ src = keylog->client_early_traffic_secret;
+ } else if (strcmp(sfx, "client_handshake_traffic_secret") == 0) {
+ src = keylog->client_handshake_traffic_secret;
+ } else if (strcmp(sfx, "server_handshake_traffic_secret") == 0) {
+ src = keylog->server_handshake_traffic_secret;
+ } else if (strcmp(sfx, "client_traffic_secret_0") == 0) {
+ src = keylog->client_traffic_secret_0;
+ } else if (strcmp(sfx, "server_traffic_secret_0") == 0) {
+ src = keylog->server_traffic_secret_0;
+ } else if (strcmp(sfx, "exporter_secret") == 0) {
+ src = keylog->exporter_secret;
+ } else if (strcmp(sfx, "early_exporter_secret") == 0) {
+ src = keylog->early_exporter_secret;
+ }
+
+ if (!src || !*src)
+ return 0;
+
+ smp->data.u.str.area = src;
+ smp->data.type = SMP_T_STR;
+ smp->flags |= SMP_F_CONST;
+ smp->data.u.str.data = strlen(smp->data.u.str.area);
+ return 1;
+/* log-format "CLIENT_RANDOM %[ssl_fc_client_random,hex] %[ssl_fc_session_key,hex]" */
+
+}
+#endif
+
static int
smp_fetch_ssl_fc_cl_str(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
{ "ssl_fc_server_random", smp_fetch_ssl_fc_random, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_fc_session_key", smp_fetch_ssl_fc_session_key, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
#endif
+
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ { "ssl_fc_client_early_traffic_secret", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_client_handshake_traffic_secret", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_server_handshake_traffic_secret", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_client_traffic_secret_0", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_server_traffic_secret_0", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_exporter_secret", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_fc_early_exporter_secret", smp_fetch_ssl_x_keylog, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+#endif
+
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
{ "ssl_fc_sni", smp_fetch_ssl_fc_sni, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
#endif
.ctx_cache = DEFAULT_SSL_CTX_CACHE,
.capture_cipherlist = 0,
.extra_files = SSL_GF_ALL,
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ .keylog = 0
+#endif
};
static BIO_METHOD *ha_meth;
int ssl_capture_ptr_index = -1;
static int ssl_app_data_index = -1;
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+int ssl_keylog_index = -1;
+struct pool_head *pool_head_ssl_keylog = NULL;
+struct pool_head *pool_head_ssl_keylog_str = NULL;
+#endif
+
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
struct list tlskeys_reference = LIST_HEAD_INIT(tlskeys_reference);
#endif
int content_type, const void *buf, size_t len,
SSL *ssl);
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+static void ssl_init_keylog(struct connection *conn, int write_p, int version,
+ int content_type, const void *buf, size_t len,
+ SSL *ssl);
+#endif
+
/* List head of all registered SSL/TLS protocol message callbacks. */
struct list ssl_sock_msg_callbacks = LIST_HEAD_INIT(ssl_sock_msg_callbacks);
if (!ssl_sock_register_msg_callback(ssl_sock_parse_clienthello))
return ERR_ABORT;
}
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ if (global_ssl.keylog > 0) {
+ if (!ssl_sock_register_msg_callback(ssl_init_keylog))
+ return ERR_ABORT;
+ }
+#endif
+
return 0;
}
SSL_set_ex_data(ssl, ssl_capture_ptr_index, capture);
}
+
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+static void ssl_init_keylog(struct connection *conn, int write_p, int version,
+ int content_type, const void *buf, size_t len,
+ SSL *ssl)
+{
+ struct ssl_keylog *keylog;
+
+ if (SSL_get_ex_data(ssl, ssl_keylog_index))
+ return;
+
+ keylog = pool_alloc(pool_head_ssl_keylog);
+ if (!keylog)
+ return;
+
+ memset(keylog, 0, sizeof(*keylog));
+
+ if (!SSL_set_ex_data(ssl, ssl_keylog_index, keylog)) {
+ pool_free(pool_head_ssl_keylog, keylog);
+ return;
+ }
+}
+#endif
+
/* Callback is called for ssl protocol analyse */
void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
{
SSL_CTX_sess_set_remove_cb(ctx, sh_ssl_sess_remove_cb);
}
+/*
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+ *
+ * The format is:
+ * * <Label> <space> <ClientRandom> <space> <Secret>
+ * We only need to copy the secret as there is a sample fetch for the ClientRandom
+ */
+
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+void SSL_CTX_keylog(const SSL *ssl, const char *line)
+{
+ struct ssl_keylog *keylog;
+ char *lastarg = NULL;
+ char *dst = NULL;
+
+ keylog = SSL_get_ex_data(ssl, ssl_keylog_index);
+ if (!keylog)
+ return;
+
+ lastarg = strrchr(line, ' ');
+ if (lastarg == NULL || ++lastarg == NULL)
+ return;
+
+ dst = pool_alloc(pool_head_ssl_keylog_str);
+ if (!dst)
+ return;
+
+ strncpy(dst, lastarg, SSL_KEYLOG_MAX_SECRET_SIZE-1);
+ dst[SSL_KEYLOG_MAX_SECRET_SIZE-1] = '\0';
+
+ if (strncmp(line, "CLIENT_RANDOM ", strlen("CLIENT RANDOM ")) == 0) {
+ if (keylog->client_random)
+ goto error;
+ keylog->client_random = dst;
+
+ } else if (strncmp(line, "CLIENT_EARLY_TRAFFIC_SECRET ", strlen("CLIENT_EARLY_TRAFFIC_SECRET ")) == 0) {
+ if (keylog->client_early_traffic_secret)
+ goto error;
+ keylog->client_early_traffic_secret = dst;
+
+ } else if (strncmp(line, "CLIENT_HANDSHAKE_TRAFFIC_SECRET ", strlen("CLIENT_HANDSHAKE_TRAFFIC_SECRET ")) == 0) {
+ if(keylog->client_handshake_traffic_secret)
+ goto error;
+ keylog->client_handshake_traffic_secret = dst;
+
+ } else if (strncmp(line, "SERVER_HANDSHAKE_TRAFFIC_SECRET ", strlen("SERVER_HANDSHAKE_TRAFFIC_SECRET ")) == 0) {
+ if (keylog->server_handshake_traffic_secret)
+ goto error;
+ keylog->server_handshake_traffic_secret = dst;
+
+ } else if (strncmp(line, "CLIENT_TRAFFIC_SECRET_0 ", strlen("CLIENT_TRAFFIC_SECRET_0 ")) == 0) {
+ if (keylog->client_traffic_secret_0)
+ goto error;
+ keylog->client_traffic_secret_0 = dst;
+
+ } else if (strncmp(line, "SERVER_TRAFFIC_SECRET_0 ", strlen("SERVER_TRAFFIC_SECRET_0 ")) == 0) {
+ if (keylog->server_traffic_secret_0)
+ goto error;
+ keylog->server_traffic_secret_0 = dst;
+
+ } else if (strncmp(line, "EARLY_EXPORTER_SECRET ", strlen("EARLY_EXPORTER_SECRET ")) == 0) {
+ if (keylog->early_exporter_secret)
+ goto error;
+ keylog->early_exporter_secret = dst;
+
+ } else if (strncmp(line, "EXPORTER_SECRET ", strlen("EXPORTER_SECRET ")) == 0) {
+ if (keylog->exporter_secret)
+ goto error;
+ keylog->exporter_secret = dst;
+ } else {
+ goto error;
+ }
+
+ return;
+
+error:
+ pool_free(pool_head_ssl_keylog_str, dst);
+
+ return;
+}
+#endif
+
/*
* This function applies the SSL configuration on a SSL_CTX
* It returns an error code and fills the <err> buffer
#if HA_OPENSSL_VERSION_NUMBER >= 0x00907000L
SSL_CTX_set_msg_callback(ctx, ssl_sock_msgcbk);
#endif
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ SSL_CTX_set_keylog_callback(ctx, SSL_CTX_keylog);
+#endif
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
ssl_conf_cur = NULL;
pool_free(pool_head_ssl_capture, ptr);
}
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+static void ssl_sock_keylog_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
+{
+ struct ssl_keylog *keylog;
+
+ if (!ptr)
+ return;
+
+ keylog = ptr;
+
+ pool_free(pool_head_ssl_keylog_str, keylog->client_random);
+ pool_free(pool_head_ssl_keylog_str, keylog->client_early_traffic_secret);
+ pool_free(pool_head_ssl_keylog_str, keylog->client_handshake_traffic_secret);
+ pool_free(pool_head_ssl_keylog_str, keylog->server_handshake_traffic_secret);
+ pool_free(pool_head_ssl_keylog_str, keylog->client_traffic_secret_0);
+ pool_free(pool_head_ssl_keylog_str, keylog->server_traffic_secret_0);
+ pool_free(pool_head_ssl_keylog_str, keylog->exporter_secret);
+ pool_free(pool_head_ssl_keylog_str, keylog->early_exporter_secret);
+
+ pool_free(pool_head_ssl_keylog, ptr);
+}
+#endif
+
__attribute__((constructor))
static void __ssl_sock_init(void)
{
#endif
ssl_app_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
ssl_capture_ptr_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_capture_free_func);
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
+ ssl_keylog_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_keylog_free_func);
+#endif
#ifndef OPENSSL_NO_ENGINE
ENGINE_load_builtin_engines();
hap_register_post_check(ssl_check_async_engine_count);