]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl/cli: "acme ps" shows the acme tasks
authorWilliam Lallemand <wlallemand@haproxy.com>
Wed, 30 Apr 2025 13:49:53 +0000 (15:49 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Wed, 30 Apr 2025 15:12:50 +0000 (17:12 +0200)
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
doc/management.txt
include/haproxy/acme-t.h
src/acme.c

index a5e6603c033fd38e536bebcd309ee17ab28ddefe..790c6076feab78e2a91fb6ce2842a8af8395ee1f 100644 (file)
@@ -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:
 
index 66fecc635e531512366402e83104a800c3d52c19..7850370698af8ff1e5bb7feb3de48e69f8b32c8e 100644 (file)
@@ -1641,10 +1641,19 @@ abort ssl crl-file <crlfile>
 
   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 <certificate>
   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 [@<ver>] <acl> <pattern>
   Add an entry into the acl <acl>. <acl> is the #<id> or the <name> returned by
index 8a3527df44f97845a521c92d36bac500bf741173..c614468c3fb28c50fe377ac497bb820619bb35bd 100644 (file)
@@ -79,5 +79,6 @@ struct acme_ctx {
        X509_REQ *req;
        struct ist finalize;
        struct ist certificate;
+       struct mt_list el;
 };
 #endif
index a1be20fa24ca229aab29287871f2333c78a90c69..6897952b2d77b8ca19589d763533fbfc43256227 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <import/ebsttree.h>
 #include <import/mjson.h>
+#include <import/mt_list.h>
 
 #include <haproxy/acme-t.h>
 
@@ -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 <certfile>                   : 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 }
 }};