From 563ca94ab83fd3368761ae4914f4ccb800020f16 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Wed, 30 Apr 2025 15:49:53 +0200 Subject: [PATCH] MINOR: ssl/cli: "acme ps" shows the acme tasks Implement a way to display the running acme tasks over the CLI. It currently only displays a "Running" status with the certificate name and the acme section from the configuration. The displayed running tasks are limited to the size of a buffer for now, it will require a backref list later to be called multiple times to resume the list. --- doc/configuration.txt | 2 +- doc/management.txt | 11 ++++++++++- include/haproxy/acme-t.h | 1 + src/acme.c | 42 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index a5e6603c0..790c6076f 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -5934,7 +5934,7 @@ it could block the event loop, blocking the traffic on the same thread. Meaning that the certificates and keys generated from HAProxy will need to be dumped from outside HAProxy using "dump ssl cert" on the stats socket. The generation is not scheduled and must be triggered using the CLI command -"acme renew". +"acme renew". See also "acme ps" in the management guide. The following keywords are usable in the ACME section: diff --git a/doc/management.txt b/doc/management.txt index 66fecc635..785037069 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1641,10 +1641,19 @@ abort ssl crl-file See also "set ssl crl-file" and "commit ssl crl-file". +acme ps + Show the running ACME tasks. See also "acme renew". + + Example: + $ echo "@1 acme ps" | socat /run/haproxy-master.sock - | column -t -s $'\t' + # certificate section state + foobar.pem.rsa LE1 Running + foobar.pem.ecdsa LE2 Running + acme renew Starts an ACME certificate generation task with the given certificate name. The certificate must be linked to an acme section, see section 3.13. of the - configuration manual. + configuration manual. See also "acme ps". add acl [@] Add an entry into the acl . is the # or the returned by diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h index 8a3527df4..c614468c3 100644 --- a/include/haproxy/acme-t.h +++ b/include/haproxy/acme-t.h @@ -79,5 +79,6 @@ struct acme_ctx { X509_REQ *req; struct ist finalize; struct ist certificate; + struct mt_list el; }; #endif diff --git a/src/acme.c b/src/acme.c index a1be20fa2..6897952b2 100644 --- a/src/acme.c +++ b/src/acme.c @@ -11,6 +11,7 @@ #include #include +#include #include @@ -35,6 +36,9 @@ #if defined(HAVE_ACME) + +struct mt_list acme_tasks = MT_LIST_HEAD_INIT(acme_tasks); + static struct acme_cfg *acme_cfgs = NULL; static struct acme_cfg *cur_acme = NULL; @@ -613,6 +617,8 @@ static void acme_ctx_destroy(struct acme_ctx *ctx) X509_REQ_free(ctx->req); + MT_LIST_DELETE(&ctx->el); + free(ctx); } @@ -1737,6 +1743,7 @@ struct task *acme_process(struct task *task, void *context, unsigned int state) enum acme_st st = ctx->state; enum http_st http_st = ctx->http_state; char *errmsg = NULL; + struct mt_list tmp = MT_LIST_LOCK_FULL(&ctx->el); switch (st) { case ACME_RESSOURCES: @@ -1935,6 +1942,7 @@ struct task *acme_process(struct task *task, void *context, unsigned int state) } + MT_LIST_UNLOCK_FULL(&ctx->el, tmp); ctx->retries = ACME_RETRY; ctx->http_state = http_st; ctx->state = st; @@ -1969,6 +1977,7 @@ retry: ha_free(&errmsg); + MT_LIST_UNLOCK_FULL(&ctx->el, tmp); return task; abort: @@ -1976,6 +1985,7 @@ abort: ha_free(&errmsg); end: + MT_LIST_UNLOCK_FULL(&ctx->el, tmp); acme_del_acme_ctx_map(ctx); acme_ctx_destroy(ctx); task_destroy(task); @@ -2172,6 +2182,9 @@ static int cli_acme_renew_parse(char **args, char *payload, struct appctx *appct ctx->cfg = cfg; task->context = ctx; + MT_LIST_INIT(&ctx->el); + MT_LIST_APPEND(&acme_tasks, &ctx->el); + task_wakeup(task, TASK_WOKEN_INIT); return 0; @@ -2186,9 +2199,38 @@ err: } +static int cli_acme_ps_io_handler(struct appctx *appctx) +{ + struct mt_list back; + struct acme_ctx *ctx; + + chunk_reset(&trash); + + chunk_appendf(&trash, "# certificate\tsection\tstate\n"); + if (applet_putchk(appctx, &trash) == -1) + return 1; + + MT_LIST_FOR_EACH_ENTRY_LOCKED(ctx, &acme_tasks, el, back) { + chunk_appendf(&trash, "%s\t%s\tRunning\n", ctx->store->path, ctx->cfg->name); + + /* TODO: handle backref list when list of task > buffer size */ + if (applet_putchk(appctx, &trash) == -1) + return 1; + } + + return 1; +} + +static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *private) +{ + return 0; +} + + static struct cli_kw_list cli_kws = {{ },{ { { "acme", "renew", NULL }, "acme renew : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 }, + { { "acme", "ps", NULL }, "acme ps : show running ACME tasks", cli_acme_ps, cli_acme_ps_io_handler, NULL, NULL, 0 }, { { NULL }, NULL, NULL, NULL } }};