MINOR: ssl: ssl-load-extra-files configure loading of files
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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 22fa882..776fd70 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1306,6 +1306,46 @@
"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
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 6f92edf..99072a5 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -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,
@@ -172,6 +181,7 @@
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,
@@ -203,6 +213,7 @@
.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;
@@ -3442,7 +3453,7 @@
#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;
@@ -3459,7 +3470,7 @@
#endif
/* try to load an ocsp response file */
- {
+ if (global_ssl.extra_files & SSL_GF_OCSP) {
char fp[MAXPATHLEN+1];
struct stat st;
@@ -3473,7 +3484,7 @@
}
#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;
@@ -4320,7 +4331,7 @@
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;
@@ -4370,13 +4381,22 @@
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;
}
@@ -9940,6 +9960,69 @@
}
#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,
@@ -11407,6 +11490,7 @@
{ 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 },
}};