MEDIUM: ssl: allow to change the OpenSSL security level from global section

The new "ssl-security-level" option allows one to change the OpenSSL
security level without having to change the openssl.cnf global file of
your distribution. This directives applies on every SSL_CTX context.

People sometimes change their security level directly in the ciphers
directive, however there are some cases when the security level change
is not applied in the right order (for example when applying a DH
param).

Before this patch, it was to possible to trick by using a specific
openssl.cnf file and start haproxy this way:

    OPENSSL_CONF=./openssl.cnf ./haproxy -f bug-2468.cfg

Values for the security level can be found there:

https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html

This was discussed in github issue #2468.
This commit is contained in:
William Lallemand 2024-03-12 16:22:34 +01:00
parent 7e9e4a8f50
commit 501d9fdb86
7 changed files with 80 additions and 0 deletions

View File

@ -1311,6 +1311,7 @@ The following keywords are supported in the "global" section :
- ssl-propquery
- ssl-provider
- ssl-provider-path
- ssl-security-level
- ssl-server-verify
- ssl-skip-self-issued-ca
- stats
@ -2588,6 +2589,17 @@ ssl-load-extra-files <none|all|bundle|sctl|ocsp|issuer|key>*
See also: "crt", section 5.1 about bind options and section 5.2 about server
options.
ssl-security-level <number>
This directive allows to chose the OpenSSL security level as described in
https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html
The security level will be applied to every SSL contextes in HAProxy.
Only a value between 0 and 5 is supported.
The default value depends on your OpenSSL version, distribution and how was
compiled the library.
This directive requires at least OpenSSL 1.1.1.
ssl-server-verify [none|required]
The default behavior for SSL verify on servers side. If specified to 'none',
servers certificates are not verified. The default is 'required' except if

View File

@ -118,6 +118,16 @@
#define HAVE_SSL_0RTT_QUIC
#endif
#if defined(SSL_CTX_set_security_level) || HA_OPENSSL_VERSION_NUMBER >= 0x1010100fL
#define HAVE_SSL_SET_SECURITY_LEVEL
#endif
#if !defined(HAVE_SSL_SET_SECURITY_LEVEL)
/* define a nope function for set_security_level */
#define SSL_CTX_set_security_level(ctx, level) ({})
#endif
#if (HA_OPENSSL_VERSION_NUMBER >= 0x3000000fL)
#define HAVE_OSSL_PARAM
#define MAC_CTX EVP_MAC_CTX
@ -309,6 +319,11 @@ static inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
#endif
#if (HA_OPENSSL_VERSION_NUMBER < 0x10101000L)
#endif
#if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL)
#if defined(SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB)
#define SSL_CTX_set_tlsext_ticket_key_evp_cb SSL_CTX_set_tlsext_ticket_key_cb

View File

@ -303,6 +303,7 @@ struct global_ssl {
int keylog; /* activate keylog */
int extra_files; /* which files not defined in the configuration file are we looking for */
int extra_files_noext; /* whether we remove the extension when looking up a extra file */
int security_level; /* configure the openssl security level */
#ifndef OPENSSL_NO_OCSP
struct {

View File

@ -2120,6 +2120,39 @@ static int ssl_parse_global_ca_crt_base(char **args, int section_type, struct pr
return 0;
}
/* parse the "ssl-security-level" keyword in global section. */
static int ssl_parse_security_level(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int linenum,
char **err)
{
#ifndef HAVE_SSL_SET_SECURITY_LEVEL
memprintf(err, "global statement '%s' requires at least OpenSSL 1.1.1.", args[0]);
return -1;
#else
char *endptr;
if (!*args[1]) {
ha_alert("parsing [%s:%d] : '%s' : missing value\n", file, linenum, args[0]);
return -1;
}
global_ssl.security_level = strtol(args[1], &endptr, 10);
if (*endptr != '\0') {
ha_alert("parsing [%s:%d] : '%s' : expects an integer argument, found '%s'\n",
file, linenum, args[0], args[1]);
return -1;
}
if (global_ssl.security_level < 0 || global_ssl.security_level > 5) {
ha_alert("parsing [%s:%d] : '%s' : expects a value between 0 and 5\n",
file, linenum, args[0]);
return -1;
}
#endif
return 0;
}
/* parse the "ssl-skip-self-issued-ca" keyword in global section. */
static int ssl_parse_skip_self_issued_ca(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
@ -2343,6 +2376,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "ssl-provider", ssl_parse_global_ssl_provider },
{ CFG_GLOBAL, "ssl-provider-path", ssl_parse_global_ssl_provider_path },
#endif
{ 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 },
#ifndef OPENSSL_NO_DH

View File

@ -441,6 +441,8 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
ctx = SSL_CTX_new(TLS_server_method());
bind_conf->initial_ctx = ctx;
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
SSL_CTX_set_options(ctx, options);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);

View File

@ -201,6 +201,10 @@ static SSL_CTX *ssl_sock_do_create_cert(const char *servername, struct bind_conf
/* Create and set the new SSL_CTX */
if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
goto mkcert_error;
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ssl_ctx, global_ssl.security_level);
if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey))
goto mkcert_error;
if (!SSL_CTX_use_certificate(ssl_ctx, newcrt))

View File

@ -136,6 +136,7 @@ struct global_ssl global_ssl = {
#ifdef HAVE_SSL_KEYLOG
.keylog = 0,
#endif
.security_level = -1,
#ifndef OPENSSL_NO_OCSP
.ocsp_update.delay_max = SSL_OCSP_UPDATE_DELAY_MAX,
.ocsp_update.delay_min = SSL_OCSP_UPDATE_DELAY_MIN,
@ -3458,6 +3459,9 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct
goto error;
}
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
errcode |= ssl_sock_put_ckch_into_ctx(path, data, ctx, err);
if (errcode & ERR_CODE)
goto error;
@ -3612,6 +3616,9 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
goto error;
}
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
errcode |= ssl_sock_put_srv_ckch_into_ctx(path, data, ctx, err);
if (errcode & ERR_CODE)
goto error;
@ -3959,6 +3966,9 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf)
ctx = SSL_CTX_new(SSLv23_server_method());
bind_conf->initial_ctx = ctx;
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
if (conf_ssl_methods->flags && (conf_ssl_methods->min || conf_ssl_methods->max))
ha_warning("Proxy '%s': no-sslv3/no-tlsv1x are ignored for bind '%s' at [%s:%d]. "
"Use only 'ssl-min-ver' and 'ssl-max-ver' to fix.\n",
@ -4955,6 +4965,8 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
cfgerr++;
return cfgerr;
}
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
srv->ssl_ctx.ctx = ctx;
}