From 81d7624e018dae1c8762f2c023583ab0c7a11edb Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 11 Jun 2026 15:47:42 +0200 Subject: [PATCH] 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. --- doc/lua-api/index.rst | 21 ++++++++++++- include/haproxy/acme-t.h | 9 ++++++ include/haproxy/acme.h | 5 +++ include/haproxy/event_hdl-t.h | 7 +++++ src/acme.c | 57 +++++++++++++++++++++++++++++++++++ src/event_hdl.c | 2 ++ src/hlua.c | 8 +++++ 7 files changed, 108 insertions(+), 1 deletion(-) diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index 08568bcc3..ad6dd0b14 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -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 ====================== diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h index fb75399e5..87c2ce306 100644 --- a/include/haproxy/acme-t.h +++ b/include/haproxy/acme-t.h @@ -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 diff --git a/include/haproxy/acme.h b/include/haproxy/acme.h index 1591ee876..e147d131b 100644 --- a/include/haproxy/acme.h +++ b/include/haproxy/acme.h @@ -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 +#include +void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data); +#endif /* USE_LUA */ #endif diff --git a/include/haproxy/event_hdl-t.h b/include/haproxy/event_hdl-t.h index 85d13bea7..d5d8dc32f 100644 --- a/include/haproxy/event_hdl-t.h +++ b/include/haproxy/event_hdl-t.h @@ -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 diff --git a/src/acme.c b/src/acme.c index b9b022947..5aea3146b 100644 --- a/src/acme.c +++ b/src/acme.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -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 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 */ diff --git a/src/event_hdl.c b/src/event_hdl.c index 1929f00f7..6e6358e1a 100644 --- a/src/event_hdl.c +++ b/src/event_hdl.c @@ -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) */ diff --git a/src/hlua.c b/src/hlua.c index d2734d101..124c8c1c0 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -73,6 +73,9 @@ #include #include #include +#if defined(HAVE_ACME) +#include +#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);