Skip to content

Commit

Permalink
MINOR: ssl: ssl-load-extra-files configure loading of files
Browse files Browse the repository at this point in the history
This new setting in the global section alters the way HAProxy will look
for unspecified files (.ocsp, .sctl, .issuer, bundles) during the
loading of the SSL certificates.

By default, HAProxy discovers automatically a lot of files not specified
in the configuration, and you may want to disable this behavior if you
want to optimize the startup time.

This patch sets flags in global_ssl.extra_files and then check them
before trying to load an extra file.
  • Loading branch information
wlallemand committed Feb 3, 2020
1 parent 1c7c0d6 commit 3af48e7
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 9 deletions.
40 changes: 40 additions & 0 deletions doc/configuration.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,46 @@ ssl-dh-param-file <file>
"openssl dhparam <size>", where size should be at least 2048, as 1024-bit DH
parameters should not be considered secure anymore.

ssl-load-extra-files <none|all|bundle|sctl|ocsp|issuer>*
This setting alters the way HAProxy will look for unspecified files during
the loading of the SSL certificates.

By default, HAProxy discovers automatically a lot of files not specified in
the configuration, and you may want to disable this behavior if you want to
optimize the startup time.

"none": Only load the files specified in the configuration. Don't try to load
a certificate bundle if the file does not exist. In the case of a directory,
it won't try to bundle the certificates if they have the same basename.

"all": This is the default behavior, it will try to load everything,
bundles, sctl, ocsp, issuer.

"bundle": When a file specified in the configuration does not exist, HAProxy
will try to load a certificate bundle. This is done by looking for
<basename>.rsa, .ecdsa and .dsa. In the case of directories, HAProxy will
try to gather the files with the same basename in a multi-certificate bundle.
The bundles were introduced with OpenSSL 1.0.2 and were the only way back
then to load an ECDSA certificate and a RSA one, with the same SNI. Since
OpenSSL 1.1.1 it is not recommended anymore, you can specifiy both the ECDSA
and the RSA file on the bind line.

"sctl": Try to load "<basename>.sctl" for each crt keyword.

"ocsp": Try to load "<basename>.ocsp" for each crt keyword.

"issuer": Try to load "<basename>.issuer" if the issuer of the OCSP file is
not provided in the PEM file.

The default behavior is "all".

Example:
ssl-load-extra-files bundle sctl
ssl-load-extra-files sctl ocsp issuer
ssl-load-extra-files none

See also: "crt", section 5.1 about bind options.

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
Expand Down
102 changes: 93 additions & 9 deletions src/ssl_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@
#define MC_SSL_O_NO_TLSV12 0x0008 /* disable TLSv12 */
#define MC_SSL_O_NO_TLSV13 0x0010 /* disable TLSv13 */

/* file to guess during file loading */
#define SSL_GF_NONE 0x00000000 /* Don't guess any file, only open the files specified in the configuration files */
#define SSL_GF_BUNDLE 0x00000001 /* try to open the bundles */
#define SSL_GF_SCTL 0x00000002 /* try to open the .sctl file */
#define SSL_GF_OCSP 0x00000004 /* try to open the .ocsp file */
#define SSL_GF_OCSP_ISSUER 0x00000008 /* try to open the .issuer file if an OCSP file was loaded */

#define SSL_GF_ALL (SSL_GF_BUNDLE|SSL_GF_SCTL|SSL_GF_OCSP|SSL_GF_OCSP_ISSUER)

/* ssl_methods versions */
enum {
CONF_TLSV_NONE = 0,
Expand Down Expand Up @@ -172,6 +181,7 @@ static struct {
unsigned int default_dh_param; /* SSL maximum DH parameter size */
int ctx_cache; /* max number of entries in the ssl_ctx cache. */
int capture_cipherlist; /* Size of the cipherlist buffer. */
int extra_files; /* which files not defined in the configuration file are we looking for */
} global_ssl = {
#ifdef LISTEN_DEFAULT_CIPHERS
.listen_default_ciphers = LISTEN_DEFAULT_CIPHERS,
Expand Down Expand Up @@ -203,6 +213,7 @@ static struct {
.default_dh_param = SSL_DEFAULT_DH_PARAM,
.ctx_cache = DEFAULT_SSL_CTX_CACHE,
.capture_cipherlist = 0,
.extra_files = SSL_GF_ALL,
};

static BIO_METHOD *ha_meth;
Expand Down Expand Up @@ -3442,7 +3453,7 @@ static int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_c

#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
/* try to load the sctl file */
{
if (global_ssl.extra_files & SSL_GF_SCTL) {
char fp[MAXPATHLEN+1];
struct stat st;

Expand All @@ -3459,7 +3470,7 @@ static int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_c
#endif

/* try to load an ocsp response file */
{
if (global_ssl.extra_files & SSL_GF_OCSP) {
char fp[MAXPATHLEN+1];
struct stat st;

Expand All @@ -3473,7 +3484,7 @@ static int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_c
}

#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
if (ckch->ocsp_response) {
if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
/* if no issuer was found, try to load an issuer from the .issuer */
if (!ckch->ocsp_issuer) {
struct stat st;
Expand Down Expand Up @@ -4320,7 +4331,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
is_bundle = 0;
/* Check if current entry in directory is part of a multi-cert bundle */

if (end) {
if ((global_ssl.extra_files & SSL_GF_BUNDLE) && end) {
for (j = 0; j < SSL_SOCK_NUM_KEYTYPES; j++) {
if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
is_bundle = 1;
Expand Down Expand Up @@ -4370,13 +4381,22 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
free(de_list);
}
return cfgerr;
}

ckchs = ckchs_load_cert_file(path, 1, err);
if (!ckchs)
return ERR_ALERT | ERR_FATAL;
} else {
/* stat failed */

cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
if (global_ssl.extra_files & SSL_GF_BUNDLE) {
/* try to load a bundle if it is permitted */
ckchs = ckchs_load_cert_file(path, 1, err);
if (!ckchs)
return ERR_ALERT | ERR_FATAL;
cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
} else {
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
err && *err ? *err : "", fp, strerror(errno));
cfgerr |= ERR_ALERT | ERR_FATAL;
}
}

return cfgerr;
}
Expand Down Expand Up @@ -9941,6 +9961,69 @@ static int ssl_parse_global_default_dh(char **args, int section_type, struct pro
#endif


/*
* parse "ssl-load-extra-files".
* multiple arguments are allowed: "bundle", "sctl", "ocsp", "issuer", "all", "none"
*/
static int ssl_parse_global_extra_files(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
int i;
int gf = SSL_GF_NONE;

if (*(args[1]) == 0)
goto err_arg;

for (i = 1; *args[i]; i++) {

if (!strcmp("bundle", args[i])) {
gf |= SSL_GF_BUNDLE;

} else if (!strcmp("sctl", args[i])) {
gf |= SSL_GF_SCTL;

} else if (!strcmp("ocsp", args[i])){
gf |= SSL_GF_OCSP;

} else if (!strcmp("issuer", args[i])){
gf |= SSL_GF_OCSP_ISSUER;

} else if (!strcmp("none", args[i])) {
if (gf != SSL_GF_NONE)
goto err_alone;
gf = SSL_GF_NONE;
i++;
break;

} else if (!strcmp("all", args[i])) {
if (gf != SSL_GF_NONE)
goto err_alone;
gf = SSL_GF_ALL;
i++;
break;
} else {
goto err_arg;
}
}
/* break from loop but there are still arguments */
if (*args[i])
goto err_alone;

global_ssl.extra_files = gf;

return 0;

err_alone:
memprintf(err, "'%s' 'none' and 'all' can be only used alone", args[0]);
return -1;

err_arg:
memprintf(err, "'%s' expects one or multiple arguments (none, all, bundle, sctl, ocsp, issuer).", args[0]);
return -1;
}


/* This function is used with TLS ticket keys management. It permits to browse
* each reference. The variable <getnext> must contain the current node,
* <end> point to the root node.
Expand Down Expand Up @@ -11407,6 +11490,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "ssl-default-bind-ciphersuites", ssl_parse_global_ciphersuites },
{ CFG_GLOBAL, "ssl-default-server-ciphersuites", ssl_parse_global_ciphersuites },
#endif
{ CFG_GLOBAL, "ssl-load-extra-files", ssl_parse_global_extra_files },
{ 0, NULL, NULL },
}};

Expand Down

0 comments on commit 3af48e7

Please sign in to comment.