From 9e60d35aaf41b276fc3a65500dbefde37d8340aa Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Wed, 10 Jun 2026 11:29:18 +0200 Subject: [PATCH] MINOR: acme: introduce acme_challenge_ready() for reuse outside the CLI Extract the challenge-readiness logic from cli_acme_chall_ready_parse() into a new acme_challenge_ready(crt, dns) function so it can be called from other contexts such as Lua event handlers. It slightly changes the messages on the CLI. --- include/haproxy/acme.h | 1 + src/acme.c | 98 ++++++++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/include/haproxy/acme.h b/include/haproxy/acme.h index 763a4692a..1591ee876 100644 --- a/include/haproxy/acme.h +++ b/include/haproxy/acme.h @@ -5,6 +5,7 @@ #include int ckch_conf_acme_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err); +int acme_challenge_ready(const char *crt, const char *dns); EVP_PKEY *acme_gen_tmp_pkey(); X509 *acme_gen_tmp_x509(); diff --git a/src/acme.c b/src/acme.c index ccb54155f..f9beba643 100644 --- a/src/acme.c +++ b/src/acme.c @@ -3506,16 +3506,64 @@ err: return cli_dynerr(appctx, errmsg); } +/* + * Change the readiness of an ACME challenge per couple + + * Return: + * - -2 if the crt was not found + * - -1 if an non-ready couple crt+dns wasn't not found + * - 0 if the challenges are ready for the certificate + * - > 0 with the number of remaining challenge to enable + */ +int acme_challenge_ready(const char *crt, const char *dns) +{ + struct ebmb_node *node = NULL; + struct acme_ctx *ctx = NULL; + struct acme_auth *auth = NULL; + int found = 0; + int remain = 0; + + HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock); + node = ebst_lookup(&acme_tasks, crt); + if (!node) { + HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock); + return -2; + } + + ctx = ebmb_entry(node, struct acme_ctx, node); + if (ctx->cfg->cond_ready & ACME_RDY_CLI) + auth = ctx->auths; + while (auth) { + if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) { + if ((auth->ready & ACME_RDY_CLI) == 0) { + auth->ready |= ACME_RDY_CLI; + found++; + } + } + if ((auth->ready & ACME_RDY_CLI) == 0) + remain++; + auth = auth->next; + } + /* no remaining challenge to ready: wake the task only if it is + * currently suspended in ACME_CLI_WAIT, not in the middle of an + * HTTP exchange. + */ + if (!remain && ctx->state == ACME_CLI_WAIT) + task_wakeup(ctx->task, TASK_WOKEN_MSG); + + HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock); + + if (!found) + return -1; + + return remain; +} + static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private) { char *msg = NULL; const char *crt; const char *dns; - struct acme_ctx *ctx = NULL; - struct acme_auth *auth = NULL; - int found = 0; - int remain = 0; - struct ebmb_node *node = NULL; + int ret; if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) return 1; @@ -3528,39 +3576,13 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx crt = args[2]; dns = args[4]; - HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock); - node = ebst_lookup(&acme_tasks, crt); - if (node) { - ctx = ebmb_entry(node, struct acme_ctx, node); - if (ctx->cfg->cond_ready & ACME_RDY_CLI) - auth = ctx->auths; - while (auth) { - if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) { - if (!(auth->ready & ACME_RDY_CLI)) { - auth->ready |= ACME_RDY_CLI; - found++; - } else { - memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns); - } - } - if ((auth->ready & ACME_RDY_CLI) == 0) - remain++; - auth = auth->next; - } - } - HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock); - if (!found) { - if (!msg) - memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns); - goto err; - } else { - if (!remain) { - if (ctx) - task_wakeup(ctx->task, TASK_WOKEN_MSG); - return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! All challenges ready, starting challenges validation!", found, dns)); - } else { - return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! Remaining challenges to deploy: %d", found, dns, remain)); - } + ret = acme_challenge_ready(crt, dns); + if (ret < 0) { + return cli_dynerr(appctx, memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns)); + } else if (ret == 0) { + return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenges ready! All challenges ready, starting challenges validation!", crt)); + } else if (ret > 0) { + return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenge(s) ready! Remaining challenges to deploy: %d", crt, ret)); } err: