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.
This commit is contained in:
William Lallemand
2026-01-27 12:25:25 +01:00
parent 0ea601127e
commit 6995fe60c3
4 changed files with 63 additions and 0 deletions

View File

@@ -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 <number> (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 <number>
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

View File

@@ -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,

View File

@@ -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 },

View File

@@ -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