From: Remi Tricot-Le Breton Date: Thu, 12 Jun 2025 13:08:28 +0000 (+0200) Subject: MINOR: ssl: Add "renegotiate" server option X-Git-Tag: v3.3-dev2~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=34fc73ba81f2819d9eacc8fb2676c84079691d9b;p=thirdparty%2Fhaproxy.git MINOR: ssl: Add "renegotiate" server option This "renegotiate" option can be set on SSL backends to allow secure renegotiation. It is mostly useful with SSL libraries that disable secure regotiation by default (such as AWS-LC). The "no-renegotiate" one can be used the other way around, to disable secure renegotation that could be allowed by default. Those two options can be set via "ssl-default-server-options" as well. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 5803f5648..a66a6dfa0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18033,6 +18033,21 @@ no-check-ssl It may also be used as "default-server" setting to reset any previous "default-server" "check-ssl" setting. +no-renegotiate + May be used in the following contexts: tcp, http, log + + This setting is only available when support for OpenSSL was built in. It + disables the renegotiation mechanisms, be it the legacy unsafe one or the + more recent "secure renegotation" one (RFC 5746 TLS Renegotiation Indication + Extension) for the given SSL backend. This option is also available on global + statement "ssl-default-server-options". + Renegotiation is not posible anymore in TLS 1.3. + If neither "renegotiate" nor "no-renegotiate" is specified, the SSL library's + default behavior is kept. + Note that for instance OpenSSL library enables secure renegotiation by + default while AWS-LC disable it. + See also "renegotiate". + no-send-proxy May be used in the following contexts: tcp, http @@ -18361,6 +18376,21 @@ redir Example : server srv1 192.168.1.1:80 redir http://image1.mydomain.com check +renegotiate + May be used in the following contexts: tcp, http, log + + This option enables the secure renegotiation mechanism (RFC 5746 TLS + Renegotiation Indication Extension) for a given SSL backend. It does not mean + that renegotiation requests will be sent by the SSL client, it only allows + backends to renegotiate when servers request it. It still requires that the + underlying SSL library actually supports renegotiation. + This option is also available on global statement "ssl-default-server-options". + Renegotiation is not posible anymore in TLS 1.3. + If neither "renegotiate" nor "no-renegotiate" is specified, the SSL library's + default behavior is kept. + Note that for instance OpenSSL library enables secure renegotiation by + default while AWS-LC disable it. + rise May be used in the following contexts: tcp, http, log diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 91ce6f37c..c1e261130 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -304,6 +304,13 @@ struct srv_pp_tlv_list { unsigned char type; }; +/* Renegotiate mode */ +enum renegotiate_mode { + SSL_RENEGOTIATE_DFLT = 0, /* Use the SSL library's default behavior */ + SSL_RENEGOTIATE_OFF, /* Disable secure renegotiation */ + SSL_RENEGOTIATE_ON /* Enable secure renegotiation */ +}; + struct proxy; struct server { /* mostly config or admin stuff, doesn't change often */ @@ -477,6 +484,7 @@ struct server { int npn_len; /* NPN protocol string length */ char *alpn_str; /* ALPN protocol string */ int alpn_len; /* ALPN protocol string length */ + int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */ } ssl_ctx; #ifdef USE_QUIC struct quic_transport_params quic_params; /* QUIC transport parameters */ diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index 44055492b..75d7eee19 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -320,6 +320,8 @@ struct global_ssl { #ifdef HAVE_ACME int acme_scheduler; #endif + + int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */ }; /* The order here matters for picking a default context, diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 44171b67d..611d29bcf 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -1630,6 +1630,25 @@ static int srv_parse_check_sni(char **args, int *cur_arg, struct proxy *px, stru } +/* parse the "renegotiate" server keyword */ +static int srv_parse_renegotiate(char **args, int *cur_arg, struct proxy *px, + struct server *newsrv, char **err) +{ + +#if !defined(OPENSSL_IS_AWSLC) && !defined(SSL_OP_NO_RENEGOTIATION) + memprintf(err, "'%s' not supported for your SSL library (%s), either SSL_OP_NO_RENEGOTIATION or SSL_set_renegotiate_mode() must be defined.", + args[0], OPENSSL_VERSION_TEXT); + return -1; +#endif + + if (strncmp(*args, "no-", 3) == 0) + newsrv->ssl_ctx.renegotiate = SSL_RENEGOTIATE_OFF; + else + newsrv->ssl_ctx.renegotiate = SSL_RENEGOTIATE_ON; + + return 0; +} + /* common function to init ssl_ctx */ static int ssl_sock_init_srv(struct server *s) { @@ -1675,6 +1694,9 @@ static int ssl_sock_init_srv(struct server *s) } #endif + if (global_ssl.renegotiate && !s->ssl_ctx.renegotiate) + s->ssl_ctx.renegotiate = global_ssl.renegotiate; + return 0; } @@ -2079,6 +2101,15 @@ static int ssl_parse_default_server_options(char **args, int section_type, struc return -1; } } + else if (strcmp(args[i], "renegotiate") == 0 || strcmp(args[i], "no-renegotiate") == 0) { +#if !defined(OPENSSL_IS_AWSLC) && !defined(SSL_OP_NO_RENEGOTIATION) + memprintf(err, "'%s' not supported for your SSL library (%s), either SSL_OP_NO_RENEGOTIATION or SSL_set_renegotiate_mode() must be defined.", + args[i], OPENSSL_VERSION_TEXT); + return -1; +#else + global_ssl.renegotiate = (*args[i] == 'n') ? SSL_RENEGOTIATE_OFF : SSL_RENEGOTIATE_ON; +#endif + } else if (parse_tls_method_options(args[i], &global_ssl.connect_default_sslmethods, err)) { memprintf(err, "unknown option '%s' on global statement '%s'.", args[i], args[0]); return -1; @@ -2505,6 +2536,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, { { "force-tlsv12", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv12 */ { "force-tlsv13", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv13 */ { "no-check-ssl", srv_parse_no_check_ssl, 0, 1, 0 }, /* disable SSL for health checks */ + { "no-renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Disable renegotiation */ { "no-send-proxy-v2-ssl", srv_parse_no_send_proxy_ssl, 0, 1, 0 }, /* do not send PROXY protocol header v2 with SSL info */ { "no-send-proxy-v2-ssl-cn", srv_parse_no_send_proxy_cn, 0, 1, 0 }, /* do not send PROXY protocol header v2 with CN */ { "no-ssl", srv_parse_no_ssl, 0, 1, 0 }, /* disable SSL processing */ @@ -2516,6 +2548,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, { { "no-tlsv13", srv_parse_tls_method_options, 0, 0, 1 }, /* disable TLSv13 */ { "no-tls-tickets", srv_parse_no_tls_tickets, 0, 1, 1 }, /* disable session resumption tickets */ { "npn", srv_parse_npn, 1, 1, 1 }, /* Set NPN supported protocols */ + { "renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Allow secure renegotiation */ { "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 1, 1 }, /* send PROXY protocol header v2 with SSL info */ { "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 1, 1 }, /* send PROXY protocol header v2 with CN */ { "sigalgs", srv_parse_sigalgs, 1, 1, 1 }, /* signature algorithms */ diff --git a/src/server.c b/src/server.c index 294ba7e99..496b00b4c 100644 --- a/src/server.c +++ b/src/server.c @@ -2719,7 +2719,7 @@ static void srv_ssl_settings_cpy(struct server *srv, const struct server *src) srv->ssl_ctx.client_crt = strdup(src->ssl_ctx.client_crt); srv->ssl_ctx.verify = src->ssl_ctx.verify; - + srv->ssl_ctx.renegotiate = src->ssl_ctx.renegotiate; if (src->ssl_ctx.verify_host != NULL) srv->ssl_ctx.verify_host = strdup(src->ssl_ctx.verify_host); diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 6c787d7b7..76d3c5d48 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -149,6 +149,7 @@ struct global_ssl global_ssl = { #ifdef HAVE_ACME .acme_scheduler = 1, #endif + .renegotiate = SSL_RENEGOTIATE_DFLT, }; @@ -4550,6 +4551,14 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLS_TICKETS) options |= SSL_OP_NO_TICKET; + +#ifdef SSL_OP_NO_RENEGOTIATION + if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_OFF) + options |= SSL_OP_NO_RENEGOTIATION; + else if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_ON) + options &= ~SSL_OP_NO_RENEGOTIATION; +#endif + SSL_CTX_set_options(ctx, options); #ifdef SSL_MODE_ASYNC @@ -5117,6 +5126,12 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx) goto err; SSL_set_connect_state(ctx->ssl); + +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) + if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_ON) + SSL_set_renegotiate_mode(ctx->ssl, ssl_renegotiate_freely); +#endif + HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &srv->ssl_ctx.lock); if (srv->ssl_ctx.reused_sess[tid].ptr) { /* let's recreate a session from (ptr,size) and assign