MINOR: acme: publish ACME_NEWCERT event via event_hdl

Add a new EVENT_HDL_SUB_ACME_NEWCERT event type in the ACME family.
It is published after a new certificate has been successfully fetched
and installed. The event carries the certificate store name, allowing
subscribers to act on newly available certificates.

Lua subscribers using core.event_sub() receive the event data as an
AcmeEvent object with a crtname field containing the certificate store
name.
This commit is contained in:
William Lallemand 2026-06-11 15:47:42 +02:00
parent 960fa1c921
commit 81d7624e01
7 changed files with 108 additions and 1 deletions

View file

@ -1031,6 +1031,10 @@ Core class
Be careful when subscribing to this type since many events might be
generated.
**ACME** Family:
* **ACME_NEWCERT**: when a new certificate is successfully installed
.. Note::
Use **SERVER** in **event_types** to subscribe to all server events types
at once. Note that this should only be used for testing purposes since a
@ -1048,7 +1052,8 @@ Core class
* **event** (*string*): the event type (one of the **event_types** specified
when subscribing)
* **event_data**: specific to each event family (For **SERVER** family,
a :ref:`server_event_class` object)
a :ref:`server_event_class` object; for **ACME** family,
a :ref:`acme_event_class` object)
* **sub**: class to manage the subscription from within the event
(a :ref:`event_sub_class` object)
* **when**: timestamp corresponding to the date when the event was generated.
@ -4760,6 +4765,20 @@ ACME class
:returns: The number of remaining challenges (integer), or 0 when all
challenges are done and validation has been triggered.
.. _acme_event_class:
AcmeEvent class
===============
.. js:class:: AcmeEvent
This class is provided with **ACME_NEWCERT** events.
See :js:func:`core.event_sub()` for more info.
.. js:attribute:: AcmeEvent.crtname
Contains the certificate store name of the newly installed certificate.
External Lua libraries
======================

View file

@ -135,6 +135,15 @@ struct acme_ctx {
#define ACME_VERB_ADVANCED 4
#define ACME_VERB_COMPLETE 5
/* event data for EVENT_HDL_SUB_ACME_NEWCERT:
* published when a new certificate was successfully installed.
*/
struct event_hdl_cb_data_acme_newcert {
struct {
char *crtname; /* certificate store name (heap-allocated) */
} safe;
};
#endif /* ! HAVE_ACME */
#endif

View file

@ -9,5 +9,10 @@ int acme_challenge_ready(const char *crt, const char *dns);
EVP_PKEY *acme_gen_tmp_pkey();
X509 *acme_gen_tmp_x509();
#if defined(USE_LUA)
#include <haproxy/hlua-t.h>
#include <haproxy/event_hdl-t.h>
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data);
#endif /* USE_LUA */
#endif

View file

@ -290,6 +290,13 @@ struct event_hdl_sub {
#define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4)
#define EVENT_HDL_SUB_PAT_REF_CLEAR EVENT_HDL_SUB_TYPE(2,5)
/* ACME family, published in global subscription list.
* Provides event_hdl_cb_data_acme_newcert struct (defined in haproxy/acme-t.h).
*/
#define EVENT_HDL_SUB_ACME EVENT_HDL_SUB_FAMILY(3)
/* a new certificate was successfully installed */
#define EVENT_HDL_SUB_ACME_NEWCERT EVENT_HDL_SUB_TYPE(3,1)
/* --------------------------------------- */
/* Please reflect changes above in event_hdl_sub_type_map defined

View file

@ -19,6 +19,7 @@
#include <haproxy/acme-t.h>
#include <haproxy/acme_resolvers.h>
#include <haproxy/event_hdl.h>
#include <haproxy/base64.h>
#include <haproxy/intops.h>
#include <haproxy/cfgparse.h>
@ -1456,6 +1457,14 @@ error:
return ret;
}
/* mfree callback for EVENT_HDL_SUB_ACME_NEWCERT: frees the heap-allocated path */
static void acme_newcert_event_mfree(const void *data)
{
const struct event_hdl_cb_data_acme_newcert *e = data;
free(e->safe.crtname);
}
/*
* Update every certificate instances for the new store
*
@ -1509,6 +1518,15 @@ int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errm
if (dpapi)
sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3);
{
struct event_hdl_cb_data_acme_newcert cb_data = { };
cb_data.safe.crtname = strdup(ctx->store->path);
if (cb_data.safe.crtname)
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_NEWCERT,
EVENT_HDL_CB_DATA_DM(&cb_data, acme_newcert_event_mfree));
}
ctx->store = NULL;
ret = 0;
@ -3699,6 +3717,24 @@ static void __acme_init(void)
INITCALL0(STG_REGISTER, __acme_init);
#ifdef USE_LUA
#define CLASS_ACME_EVENT "AcmeEvent"
static int class_acme_event_ref;
/* Push a new AcmeEvent object for an ACME_NEWCERT event onto the Lua stack.
* The object exposes a <crtname> field with the certificate store name.
*/
static void hlua_fcn_new_acme_event_newcert(lua_State *L, const char *crtname)
{
lua_newtable(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
lua_setmetatable(L, -2);
lua_pushstring(L, crtname);
lua_setfield(L, -2, "crtname");
}
/*
* ACME.challenge_ready(crt, dns)
*
@ -3731,6 +3767,10 @@ __LJMP static int hlua_acme_challenge_ready(lua_State *L)
static int acme_hlua_init_state(lua_State *L, char **errmsg)
{
/* Register AcmeEvent class */
lua_newtable(L);
class_acme_event_ref = hlua_register_metatable(L, CLASS_ACME_EVENT);
lua_newtable(L);
hlua_class_function(L, "challenge_ready", hlua_acme_challenge_ready);
lua_setglobal(L, "ACME");
@ -3738,6 +3778,23 @@ static int acme_hlua_init_state(lua_State *L, char **errmsg)
}
REGISTER_HLUA_STATE_INIT(acme_hlua_init_state);
/* Push ACME event data as a Lua table for core.event_sub() handlers.
* Called from hlua_event_hdl_cb_push_args() when the event family is ACME.
*/
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data)
{
if (!lua_checkstack(hlua->T, 3))
WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));
if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_NEWCERT, event)) {
struct event_hdl_cb_data_acme_newcert *e_acme = data;
hlua->nargs += 1;
MAY_LJMP(hlua_fcn_new_acme_event_newcert(hlua->T, e_acme->safe.crtname));
}
}
#endif /* USE_LUA */
#endif /* ! HAVE_ACME */

View file

@ -48,6 +48,8 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = {
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
{"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR},
{"ACME", EVENT_HDL_SUB_ACME},
{"ACME_NEWCERT", EVENT_HDL_SUB_ACME_NEWCERT},
};
/* internal types (only used in this file) */

View file

@ -73,6 +73,9 @@
#include <haproxy/event_hdl.h>
#include <haproxy/check.h>
#include <haproxy/mailers.h>
#if defined(HAVE_ACME)
#include <haproxy/acme.h>
#endif /* HAVE_ACME */
/* Global LUA flags */
@ -10071,6 +10074,11 @@ __LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
lua_settable(hlua->T, -3);
}
}
#if defined(HAVE_ACME)
else if (event_hdl_sub_family_equal(EVENT_HDL_SUB_ACME, event)) {
MAY_LJMP(acme_hlua_event_push_args(hlua, event, data));
}
#endif /* HAVE_ACME */
/* sub mgmt */
hlua->nargs += 1;
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);