]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: acme: replace the previous ckch instance with new ones
authorWilliam Lallemand <wlallemand@haproxy.com>
Fri, 11 Apr 2025 21:54:52 +0000 (23:54 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Fri, 11 Apr 2025 23:39:03 +0000 (01:39 +0200)
This step is the latest to have a usable ACME certificate in haproxy.

It looks for the previous certificate, locks the "BIG CERTIFICATE LOCK",
copy every instance, deploys new ones, remove the previous one.
This is done in one step in a function which does not yield, so it could
be problematic if you have thousands of instances to handle.

It still lacks the rate limit which is mandatory to be used in
production, and more cleanup and deinit.

src/acme.c

index 5519ae25e7366640480a47dc634a43092b1ce2d9..df4b199cb4cadab0688e3e0aaa5e9200a9c9c82e 100644 (file)
@@ -579,12 +579,62 @@ error:
        return ret;
 }
 
+/*
+ * Update every certificate instances for the new store
+ *
+ * XXX: ideally this should be reentrant like in lua or the CLI.
+ */
+int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
+{
+       int ret = 1;
+       struct ckch_store *old_ckchs, *new_ckchs = NULL;
+       struct ckch_inst *ckchi;
+
+       new_ckchs = ctx->store;
+
+       if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
+               memprintf(errmsg, "couldn't get the certificate lock!");
+               goto error;
+
+       }
+
+       if ((old_ckchs = ckchs_lookup(new_ckchs->path)) == NULL) {
+               memprintf(errmsg, "couldn't find the previous certificate to update");
+               goto error;
+       }
+
+       ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
+
+       /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
+       list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
+               struct ckch_inst *new_inst;
+
+               if (ckch_inst_rebuild(new_ckchs, ckchi, &new_inst, errmsg)) {
+                       goto error;
+               }
+
+               /* link the new ckch_inst to the duplicate */
+               LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
+       }
+
+       /* insert everything and remove the previous objects */
+       ckch_store_replace(old_ckchs, new_ckchs);
+
+       ret = 0;
+
+error:
+       HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+       return ret;
+
+}
+
 int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
 {
        struct httpclient *hc;
        struct http_hdr *hdrs, *hdr;
        struct buffer *t1 = NULL, *t2 = NULL;
        int ret = 1;
+       EVP_PKEY *key;
 
        hc = ctx->hc;
        if (!hc)
@@ -617,9 +667,20 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
                goto error;
        }
 
+       /* loading a PEM would remove the key, save it for later */
+       key = ctx->store->data->key;
+       ctx->store->data->key = NULL;
+
+       /* XXX: might need a function dedicated to this, which does not read a private key */
        if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
                goto error;
 
+       /* restore the key */
+       ctx->store->data->key = key;
+
+       if (acme_update_certificate(task, ctx, errmsg) != 0)
+               goto error;
+
 out:
        ret = 0;