From 582a1430b2354df6365266947504a90737e1daa0 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Wed, 5 Nov 2025 18:45:00 +0100 Subject: [PATCH] MEDIUM: acme: generate a key pair when no file are available When an acme keyword is associated to a crt and key, and the corresponding files does not exist, HAProxy would not start. This patch allows to configure acme without pre-generating a keypair before starting HAProxy. If the files does not exist, it tries to generate a unique keypair in memory, that will be used for every ACME certificates that don't have a file on the disk yet. --- src/ssl_ckch.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index 2d4dd0725..3135b70b9 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -4729,16 +4729,70 @@ static char *current_crtbase = NULL; static char *current_keybase = NULL; static int crtstore_load = 0; /* did we already load in this crt-store */ +static int ckch_conf_load_pem_or_generate(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err) +{ + char path[PATH_MAX]; + int err_code = 0; + struct stat sb; + + if (cli) + return 0; + err_code |= path_base(value, current_crtbase, path, err); + if (err_code & ERR_CODE) + goto out; + + errno = 0; + /* if ACME is enabled and the file does not exists, generate the PEM */ + if (s->conf.acme.id && (stat(path, &sb) == -1 && errno == ENOENT)) { + /* generate they key and the certificate */ + ha_notice("No certificate available for '%s', generating a temporary key pair before getting the ACME certificate\n", path); + s->data->key = acme_gen_tmp_pkey(); + s->data->cert = acme_gen_tmp_x509(); + if (!s->data->key || !s->data->cert) { + memprintf(err, "Couldn't generate a temporary keypair for '%s'\n", path); + err_code |= ERR_FATAL; + goto out; + } + + } else { + err_code |= ssl_sock_load_pem_into_ckch(path, buf, s->data, err); + } +out: + return err_code; +} +static int ckch_conf_load_key_or_generate(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err) +{ + char path[PATH_MAX]; + int err_code = 0; + struct stat sb; + + if (cli) + return 0; + err_code |= path_base(value, current_keybase, path, err); + if (err_code & ERR_CODE) + goto out; + + errno = 0; + /* if ACME is enabled and the file does not exists, and no key was previously loaded generate the key */ + if (s->conf.acme.id && + (stat(path, &sb) == -1 && errno == ENOENT) && + (!s->data->key)) { + s->data->key = acme_gen_tmp_pkey(); + } else { + err_code |= ssl_sock_load_key_into_ckch(path, buf, s->data, err); + } +out: + return err_code; +} + /* declare the ckch_conf_load_* wrapper functions */ -DECLARE_CKCH_CONF_LOAD(pem, current_crtbase, ssl_sock_load_pem_into_ckch); -DECLARE_CKCH_CONF_LOAD(key, current_keybase, ssl_sock_load_key_into_ckch); DECLARE_CKCH_CONF_LOAD(ocsp_response, current_crtbase, ssl_sock_load_ocsp_response_from_file); DECLARE_CKCH_CONF_LOAD(ocsp_issuer, current_crtbase, ssl_sock_load_issuer_file_into_ckch); DECLARE_CKCH_CONF_LOAD(sctl, current_crtbase, ssl_sock_load_sctl_from_file); struct ckch_conf_kws ckch_conf_kws[] = { { "alias", -1, PARSE_TYPE_NONE, NULL, }, - { "crt", offsetof(struct ckch_conf, crt), PARSE_TYPE_STR, ckch_conf_load_pem, }, + { "crt", offsetof(struct ckch_conf, crt), PARSE_TYPE_STR, ckch_conf_load_pem_or_generate }, { "key", offsetof(struct ckch_conf, key), PARSE_TYPE_STR, ckch_conf_load_key, }, #ifdef HAVE_SSL_OCSP { "ocsp", offsetof(struct ckch_conf, ocsp), PARSE_TYPE_STR, ckch_conf_load_ocsp_response, }, -- 2.47.3