From: William Lallemand Date: Thu, 20 Feb 2025 10:31:32 +0000 (+0100) Subject: MINOR: acme/cli: add the 'acme renew' command X-Git-Tag: v3.2-dev11~99 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b8209cf697155f462129f372209afd0ec48b775a;p=thirdparty%2Fhaproxy.git MINOR: acme/cli: add the 'acme renew' command The "acme renew" command launch the ACME task for a given certificate. The CLI parser generates a new private key using the parameters from the acme section.. --- diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h index 685bef0da..aa6892a0f 100644 --- a/include/haproxy/acme-t.h +++ b/include/haproxy/acme-t.h @@ -4,6 +4,8 @@ #include +#define ACME_RETRY 3 + /* acme section configuration */ struct acme_cfg { char *filename; /* config filename */ @@ -26,4 +28,11 @@ struct acme_cfg { struct acme_cfg *next; }; +struct acme_ctx { + int retries; + struct acme_cfg *cfg; + struct ckch_store *store; + unsigned int state; +}; + #endif diff --git a/src/acme.c b/src/acme.c index 156caea75..5d91c1b1b 100644 --- a/src/acme.c +++ b/src/acme.c @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -471,6 +472,126 @@ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws_acme); REGISTER_CONFIG_SECTION("acme", cfg_parse_acme, cfg_postsection_acme); +struct task *acme_process(struct task *task, void *context, unsigned int state) +{ + + return task; +} + + +static int cli_acme_renew_parse(char **args, char *payload, struct appctx *appctx, void *private) +{ + char *err = NULL; + struct acme_cfg *cfg; + struct task *task; + struct acme_ctx *ctx = NULL; + struct ckch_store *store = NULL, *newstore = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + EVP_PKEY *pkey = NULL; + + if (!*args[1]) { + memprintf(&err, ": not enough parameters\n"); + goto err; + } + + if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) + return cli_err(appctx, "Can't update: operations on certificates are currently locked!\n"); + + if ((store = ckchs_lookup(args[2])) == NULL) { + memprintf(&err, "Can't find the certificate '%s'.\n", args[1]); + goto err; + } + + if (store->conf.acme.id == NULL) { + memprintf(&err, "No ACME configuration defined for file '%s'.\n", args[1]); + goto err; + } + + cfg = get_acme_cfg(store->conf.acme.id); + if (!cfg) { + memprintf(&err, "No ACME configuration found for file '%s'.\n", args[1]); + goto err; + } + + newstore = ckch_store_new(store->path); + if (!newstore) { + memprintf(&err, "Out of memory.\n"); + goto err; + } + + HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock); + + ctx = calloc(1, sizeof *ctx); + if (!ctx) { + memprintf(&err, "Out of memory.\n"); + goto err; + } + + /* set the number of remaining retries when facing an error */ + ctx->retries = ACME_RETRY; + + if ((pkey_ctx = EVP_PKEY_CTX_new_id(cfg->key.type, NULL)) == NULL) { + memprintf(&err, "%sCan't generate a private key.\n", err ? err : ""); + goto err; + } + + if (EVP_PKEY_keygen_init(pkey_ctx) <= 0) { + memprintf(&err, "%sCan't generate a private key.\n", err ? err : ""); + goto err; + } + + if (cfg->key.type == EVP_PKEY_EC) { + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx, cfg->key.curves) <= 0) { + memprintf(&err, "%sCan't set the curves on the new private key.\n", err ? err : ""); + goto err; + } + } else if (cfg->key.type == EVP_PKEY_RSA) { + if (EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_ctx, cfg->key.bits) <= 0) { + memprintf(&err, "%sCan't set the bits on the new private key.\n", err ? err : ""); + goto err; + } + } + + if (EVP_PKEY_keygen(pkey_ctx, &pkey) <= 0) { + memprintf(&err, "%sCan't generate a private key.\n", err ? err : ""); + goto err; + } + + EVP_PKEY_CTX_free(pkey_ctx); + + newstore->data->key = pkey; + ctx->store = newstore; + ctx->cfg = cfg; + + task = task_new_anywhere(); + if (!task) + goto err; + task->nice = 0; + task->process = acme_process; + task->context = ctx; + + task_wakeup(task, TASK_WOKEN_INIT); + + return 0; + +err: + HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock); + ckch_store_free(newstore); + EVP_PKEY_CTX_free(pkey_ctx); + free(ctx); + memprintf(&err, "%sCan't start the ACME client.\n", err ? err : ""); + return cli_dynerr(appctx, err); +} + + + +static struct cli_kw_list cli_kws = {{ },{ + { { "acme", "renew", NULL }, NULL, cli_acme_renew_parse, NULL, NULL, NULL, 0 }, + { { NULL }, NULL, NULL, NULL } +}}; + + +INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); /* * Local variables: