]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: Add "renegotiate" server option
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Thu, 12 Jun 2025 13:08:28 +0000 (15:08 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Wed, 25 Jun 2025 13:23:48 +0000 (15:23 +0200)
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.

doc/configuration.txt
include/haproxy/server-t.h
include/haproxy/ssl_sock-t.h
src/cfgparse-ssl.c
src/server.c
src/ssl_sock.c

index 5803f56481291f11faff899ad6d7427fab27902a..a66a6dfa07ae6df46c19f6ce3bd8a4ce809cad57 100644 (file)
@@ -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 <prefix>
 
   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 <count>
   May be used in the following contexts: tcp, http, log
 
index 91ce6f37c77040d9ff0f1a3861801cccfb6ea111..c1e26113088c3d357e99d7b3d700f9f4f9da7016 100644 (file)
@@ -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 */
index 44055492b955c96626f9847f21aa97fa7a526ef5..75d7eee19470967e7409d2c7b9e11c249c906fe6 100644 (file)
@@ -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,
index 44171b67d20a92538a4ed317da3f6a623cec6f44..611d29bcf26147e8ab8254d989aff901d959c537 100644 (file)
@@ -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 */
index 294ba7e9926ecab21e21b492e69471db68819bdc..496b00b4c4792be3ba742bd6590f755658ff2539 100644 (file)
@@ -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);
index 6c787d7b790432ad515edda95da304e78a962cec..76d3c5d488685a7fd155c7cba0cbc3676c737ece 100644 (file)
@@ -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