From e04aba2fa39db0bcf42a4db3c6d51b341176dc56 Mon Sep 17 00:00:00 2001 From: Remi Tricot-Le Breton Date: Wed, 28 May 2025 11:24:38 +0200 Subject: [PATCH] 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 BoringSSL or 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. --- doc/configuration.txt | 30 ++++++++++++++++++++++++++++++ include/haproxy/server-t.h | 8 ++++++++ include/haproxy/ssl_sock-t.h | 2 ++ src/cfgparse-ssl.c | 21 +++++++++++++++++++++ src/server.c | 2 +- src/ssl_sock.c | 13 +++++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index c21bae921..8083edb88 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18007,6 +18007,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 BoringSSL and AWS-LC disable it. + See also "renegotiate". + no-send-proxy May be used in the following contexts: tcp, http @@ -18335,6 +18350,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 BoringSSL and 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 dc238ec25..ed39d2e0a 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; struct resolv_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */ struct list srv_rec_item; /* to attach server to a srv record item */ 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..deebbf503 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -1630,6 +1630,19 @@ 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 (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 +1688,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 +2095,9 @@ 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) { + global_ssl.renegotiate = (*args[i] == 'n') ? SSL_RENEGOTIATE_OFF : SSL_RENEGOTIATE_ON; + } 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 +2524,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 +2536,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 fc886c917..50ff2a32c 100644 --- a/src/server.c +++ b/src/server.c @@ -2718,7 +2718,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 c897bbf18..b4983dba3 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -148,6 +148,7 @@ struct global_ssl global_ssl = { #ifdef HAVE_ACME .acme_scheduler = 1, #endif + .renegotiate = SSL_RENEGOTIATE_DFLT, }; @@ -4532,6 +4533,12 @@ 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; + + 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; + SSL_CTX_set_options(ctx, options); #ifdef SSL_MODE_ASYNC @@ -5099,6 +5106,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