"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>*
+ssl-load-extra-files <none|all|bundle|sctl|ocsp|issuer|key>*
This setting alters the way HAProxy will look for unspecified files during
the loading of the SSL certificates.
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.
+ bundles, sctl, ocsp, issuer, key.
"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
"issuer": Try to load "<basename>.issuer" if the issuer of the OCSP file is
not provided in the PEM file.
+ "key": If the private key was not provided by the PEM file, try to load a
+ file "<basename>.key" containing a private key.
+
The default behavior is "all".
Example:
file. Intermediate certificate can also be shared in a directory via
"issuers-chain-path" directive.
+ If the file does not contain a private key, HAProxy will try to load
+ the key at the same path suffixed by a ".key".
+
If the OpenSSL used supports Diffie-Hellman, parameters present in this file
are loaded.
#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_KEY 0x00000010 /* try to open the .key file to load a private key */
-#define SSL_GF_ALL (SSL_GF_BUNDLE|SSL_GF_SCTL|SSL_GF_OCSP|SSL_GF_OCSP_ISSUER)
+#define SSL_GF_ALL (SSL_GF_BUNDLE|SSL_GF_SCTL|SSL_GF_OCSP|SSL_GF_OCSP_ISSUER|SSL_GF_KEY)
/* ssl_methods versions */
enum {
/*
* Try to load a PEM file from a <path> or a buffer <buf>
- * The PEM must contain at least a Private Key and a Certificate,
- * It could contain a DH and a certificate chain.
+ * The PEM must contain at least a Certificate,
+ * It could contain a DH, a certificate chain and a PrivateKey.
*
* If it failed you should not attempt to use the ckch but free it.
*
/* Read Private Key */
key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
- if (key == NULL) {
- memprintf(err, "%sunable to load private key from file '%s'.\n",
- err && *err ? *err : "", path);
- goto end;
- }
+ /* no need to check for errors here, because the private key could be loaded later */
#ifndef OPENSSL_NO_DH
/* Seek back to beginning of file */
goto end;
}
- if (!X509_check_private_key(cert, key)) {
- memprintf(err, "%sinconsistencies between private key and certificate loaded from PEM file '%s'.\n",
- err && *err ? *err : "", path);
- goto end;
- }
-
/* Look for a Certificate Chain */
while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
if (chain == NULL)
return ret;
}
+/*
+ * Try to load a private key file from a <path> or a buffer <buf>
+ *
+ * If it failed you should not attempt to use the ckch but free it.
+ *
+ * Return 0 on success or != 0 on failure
+ */
+static int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
+{
+ BIO *in = NULL;
+ int ret = 1;
+ EVP_PKEY *key = NULL;
+
+ if (buf) {
+ /* reading from a buffer */
+ in = BIO_new_mem_buf(buf, -1);
+ if (in == NULL) {
+ memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
+ goto end;
+ }
+
+ } else {
+ /* reading from a file */
+ in = BIO_new(BIO_s_file());
+ if (in == NULL)
+ goto end;
+
+ if (BIO_read_filename(in, path) <= 0)
+ goto end;
+ }
+
+ /* Read Private Key */
+ key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
+ if (key == NULL) {
+ memprintf(err, "%sunable to load private key from file '%s'.\n",
+ err && *err ? *err : "", path);
+ goto end;
+ }
+
+ ret = 0;
+
+ SWAP(ckch->key, key);
+
+end:
+
+ ERR_clear_error();
+ if (in)
+ BIO_free(in);
+ if (key)
+ EVP_PKEY_free(key);
+
+ return ret;
+}
+
/*
* Try to load in a ckch every files related to a ckch.
* (PEM, sctl, ocsp, issuer etc.)
goto end;
}
+ /* try to load an external private key if it wasn't in the PEM */
+ if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
+ char fp[MAXPATHLEN+1];
+ struct stat st;
+
+ snprintf(fp, MAXPATHLEN+1, "%s.key", path);
+ if (stat(fp, &st) == 0) {
+ if (ssl_sock_load_key_into_ckch(fp, NULL, ckch, err)) {
+ memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
+ err && *err ? *err : "", fp);
+ goto end;
+ }
+ }
+ }
+
+ if (ckch->key == NULL) {
+ memprintf(err, "%sNo Private Key found in '%s' or '%s.key'.\n", err && *err ? *err : "", path, path);
+ goto end;
+ }
+
+ if (!X509_check_private_key(ckch->cert, ckch->key)) {
+ memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
+ err && *err ? *err : "", path);
+ goto end;
+ }
+
#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) {
} else if (!strcmp("issuer", args[i])){
gf |= SSL_GF_OCSP_ISSUER;
+ } else if (!strcmp("key", args[i])) {
+ gf |= SSL_GF_KEY;
+
} else if (!strcmp("none", args[i])) {
if (gf != SSL_GF_NONE)
goto err_alone;
enum {
CERT_TYPE_PEM = 0,
+ CERT_TYPE_KEY,
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
CERT_TYPE_OCSP,
#endif
/* add a parsing callback */
} cert_exts[CERT_TYPE_MAX+1] = {
[CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
+ [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
[CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
#endif
goto error;
}
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+ if (ckchs_transaction.new_ckchs->multi) {
+ int n;
+
+ for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
+ if (ckchs_transaction.new_ckchs->ckch[n].cert && !X509_check_private_key(ckchs_transaction.new_ckchs->ckch[n].cert, ckchs_transaction.new_ckchs->ckch[n].key)) {
+ memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
+ goto error;
+ }
+ }
+ } else
+#endif
+ {
+ if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
+ memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
+ goto error;
+ }
+ }
+
/* init the appctx structure */
appctx->st2 = SETCERT_ST_INIT;
appctx->ctx.ssl.next_ckchi = NULL;