MINOR: acme: add private key configuration

This commit allows to configure the generated private keys, you can
configure the keytype (RSA/ECDSA), the number of bits or the curves.

Example:

    acme LE
        uri https://acme-staging-v02.api.letsencrypt.org/directory
        account account.key
        contact foobar@example.com
        challenge HTTP-01
        keytype ECDSA
        curves P-384
This commit is contained in:
William Lallemand 2025-04-02 18:52:24 +02:00
parent 2e8c350b95
commit bf6a39c4d1
2 changed files with 82 additions and 0 deletions

View File

@ -16,6 +16,12 @@ struct acme_cfg {
EVP_PKEY *pkey; /* account PKEY */
char *thumbprint; /* account PKEY JWS thumbprint */
} account;
struct {
int type; /* EVP_PKEY_EC or EVP_PKEY_RSA */
int bits; /* bits for RSA */
int curves; /* NID of curves */
} key;
char *challenge; /* HTTP-01, DNS-01, etc */
struct acme_cfg *next;
};

View File

@ -19,6 +19,7 @@
#include <haproxy/jws.h>
#include <haproxy/ssl_ckch.h>
#include <haproxy/ssl_sock.h>
#include <haproxy/ssl_utils.h>
#include <haproxy/tools.h>
static struct acme_cfg *acme_cfgs = NULL;
@ -59,6 +60,13 @@ struct acme_cfg *new_acme_cfg(const char *name)
ret->challenge = strdup("HTTP-01"); /* default value */
/* The default generated keys are EC-384 */
ret->key.type = EVP_PKEY_EC;
ret->key.curves = NID_secp384r1;
/* default to 4096 bits when using RSA */
ret->key.bits = 4096;
ret->next = acme_cfgs;
acme_cfgs = ret;
@ -267,6 +275,71 @@ out:
return err_code;
}
static int cfg_parse_acme_cfg_key(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx,
const char *file, int linenum, char **err)
{
int err_code = 0;
char *errmsg = NULL;
if (strcmp(args[0], "keytype") == 0) {
if (!*args[1]) {
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
if (strcmp(args[1], "RSA") == 0) {
cur_acme->key.type = EVP_PKEY_RSA;
} else if (strcmp(args[1], "ECDSA") == 0) {
cur_acme->key.type = EVP_PKEY_EC;
} else {
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires either 'RSA' or 'ECDSA' argument\n", file, linenum, args[0], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
} else if (strcmp(args[0], "bits") == 0) {
char *stop;
if (!*args[1]) {
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
cur_acme->key.bits = strtol(args[1], &stop, 10);
if (*stop != '\0') {
err_code |= ERR_ALERT | ERR_FATAL;
ha_alert("parsing [%s:%d] : cannot parse '%s' value '%s', an integer is expected.\n", file, linenum, args[0], args[1]);
goto out;
}
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
} else if (strcmp(args[0], "curves") == 0) {
if (!*args[1]) {
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
if ((cur_acme->key.curves = curves2nid(args[1])) == -1) {
ha_alert("parsing [%s:%d]: unsupported curves '%s'\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
out:
free(errmsg);
return err_code;
}
/* Initialize stuff once the section is parsed */
static int cfg_postsection_acme()
{
@ -388,6 +461,9 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, {
{ CFG_ACME, "contact", cfg_parse_acme_kws },
{ CFG_ACME, "account", cfg_parse_acme_kws },
{ CFG_ACME, "challenge", cfg_parse_acme_kws },
{ CFG_ACME, "keytype", cfg_parse_acme_cfg_key },
{ CFG_ACME, "bits", cfg_parse_acme_cfg_key },
{ CFG_ACME, "curves", cfg_parse_acme_cfg_key },
{ 0, NULL, NULL },
}};