From: William Lallemand Date: Tue, 27 Jan 2026 11:25:25 +0000 (+0100) Subject: MINOR: ssl: allow to disable certificate compression X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6995fe60c32726830786be229c0531ce5fe531ae;p=thirdparty%2Fhaproxy.git MINOR: ssl: allow to disable certificate compression This option allows to disable the certificate compression (RFC 8879) using OpenSSL >= 3.2.0. This feature is known to permit some denial of services by causing extra memory allocations of approximately 22MiB and extra CPU work per connection with OpenSSL versions affected by CVE-2025-66199. ( https://openssl-library.org/news/vulnerabilities/index.html#CVE-2025-66199 ) Setting this to "off" permits to mitigate the problem. Must be backported to every stable branches. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 989290601..8c798c06f 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1983,6 +1983,7 @@ The following keywords are supported in the "global" section : - tune.ssl.cachesize - tune.ssl.capture-buffer-size - tune.ssl.capture-cipherlist-size (deprecated) + - tune.ssl.certificate-compression - tune.ssl.default-dh-param - tune.ssl.force-private-cache - tune.ssl.hard-maxrecord @@ -5310,6 +5311,22 @@ tune.ssl.capture-cipherlist-size (deprecated) formats. If the value is 0 (default value) the capture is disabled, otherwise a buffer is allocated for each SSL/TLS connection. +tune.ssl.certificate-compression { auto | off } + This setting allows to configure the certificate compression support which is + an extension (RFC 8879) to TLS 1.3. + + When set to "auto" it uses the default value of the TLS library. + + With "off" it tries to explicitely disable the support of the feature. + HAProxy won't try to send compressed certificates anymore nor accept + compressed certificates. + + Configures both backend and frontend sides. + + This keyword is supported by OpenSSL >= 3.2.0. + + The default value is auto. + tune.ssl.default-dh-param Sets the maximum size of the Diffie-Hellman parameters used for generating the ephemeral/temporary Diffie-Hellman key in case of DHE key exchange. The diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index b1d660f30..af10facee 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -338,6 +338,8 @@ struct global_ssl { int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */ char **passphrase_cmd; int passphrase_cmd_args_cnt; + + unsigned int certificate_compression:1; /* allow to explicitely disable certificate compression */ }; /* The order here matters for picking a default context, diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index e5e539c08..3fa93966e 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -496,6 +496,36 @@ static int ssl_parse_global_keylog(char **args, int section_type, struct proxy * } #endif +/* Allow to explicitely disable certificate compression when set to "off" */ +#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION +static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (too_many_args(1, args, err, NULL)) + return -1; + + if (strcmp(args[1], "auto") == 0) + global_ssl.certificate_compression = 1; + else if (strcmp(args[1], "off") == 0) + global_ssl.certificate_compression = 0; + else { + memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); return -1; + } + + return 0; +} +#else +static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + memprintf(err, "'%s' is not supported by your TLS library. " + "It is known to work only with OpenSSL >= 3.2.0.", args[0]); + return -1; +} +#endif + /* parse "ssl.force-private-cache". * Returns <0 on alert, >0 on warning, 0 on success. */ @@ -2759,6 +2789,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "ssl-security-level", ssl_parse_security_level }, { CFG_GLOBAL, "ssl-skip-self-issued-ca", ssl_parse_skip_self_issued_ca }, { CFG_GLOBAL, "tune.ssl.cachesize", ssl_parse_global_int }, + { CFG_GLOBAL, "tune.ssl.certificate-compression", ssl_parse_certificate_compression }, { CFG_GLOBAL, "tune.ssl.default-dh-param", ssl_parse_global_default_dh }, { CFG_GLOBAL, "tune.ssl.force-private-cache", ssl_parse_global_private_cache }, { CFG_GLOBAL, "tune.ssl.lifetime", ssl_parse_global_lifetime }, diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 1fcec4ab4..27218e4a8 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -152,6 +152,9 @@ struct global_ssl global_ssl = { #endif #ifdef HAVE_ACME .acme_scheduler = 1, +#endif +#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION + .certificate_compression = 1, #endif .renegotiate = SSL_RENEGOTIATE_DFLT, .passphrase_cmd = NULL, @@ -4079,6 +4082,11 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) options |= SSL_OP_NO_RENEGOTIATION; #endif +#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION + if (global_ssl.certificate_compression == 0) + options |= SSL_OP_NO_RX_CERTIFICATE_COMPRESSION | SSL_OP_NO_TX_CERTIFICATE_COMPRESSION; +#endif + SSL_CTX_set_options(ctx, options); #ifdef SSL_MODE_ASYNC @@ -5132,6 +5140,11 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) options &= ~SSL_OP_NO_RENEGOTIATION; #endif +#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION + if (global_ssl.certificate_compression == 0) + options |= SSL_OP_NO_RX_CERTIFICATE_COMPRESSION | SSL_OP_NO_TX_CERTIFICATE_COMPRESSION; +#endif + SSL_CTX_set_options(ctx, options); #ifdef SSL_MODE_ASYNC