This implements configuration for custom mac alg in EAB.
I don't think there are any reasons to allow that TBH,
but it is something that exists in the spec.
Depends on the EAB impl.
No backport needed
---
doc/configuration.txt | 5 +++++
include/haproxy/acme-t.h | 2 ++
src/acme.c | 46 ++++++++++++++++++++++++++++++++++------
3 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d2b91eed2..56798d9d9 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -32653,6 +32653,11 @@ eab-key-id <filename>
EAB credentials are used only during the initial ACME account creation, and
can be removed afterwards.
+eab-mac-alg [ HS256 | HS384 | HS512 ]
+ Configure MAC algorithm used for EAB signing. Default is HS256. EAB MAC key
+ must be large enough to support specified MAC algorithm. Not all CAs support
+ algorithms other than HS256.
+
12.9. Healthchecks
------------------
diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h
index 87f3bfffa..fb75399e5 100644
--- a/include/haproxy/acme-t.h
+++ b/include/haproxy/acme-t.h
@@ -5,6 +5,7 @@
#include <haproxy/acme_resolvers-t.h>
#include <haproxy/istbuf.h>
#include <haproxy/buf-t.h>
+#include <haproxy/jwt-t.h>
#include <haproxy/openssl-compat.h>
#if defined(HAVE_ACME)
@@ -46,6 +47,7 @@ struct acme_cfg {
char *mac_key_file; /* base64url encoded EAB hmac key
filename */
char *kid; /* EAB key id */
struct buffer mac_key; /* raw EAB hmac key */
+ enum jwt_alg mac_alg; /* MAC algorithm for EAB signature */
} eab;
char *challenge; /* HTTP-01, DNS-01, etc */
char *profile; /* ACME profile */
diff --git a/src/acme.c b/src/acme.c
index e7f7d1a77..6fc789a9b 100644
--- a/src/acme.c
+++ b/src/acme.c
@@ -214,6 +214,9 @@ struct acme_cfg *new_acme_cfg(const char *name)
/* default to 2048 bits when using RSA */
ret->key.bits = 2048;
+ /* HS256 is the only sane choice for HMAC */
+ ret->eab.mac_alg = JWS_ALG_HS256;
+
ret->next = acme_cfgs;
acme_cfgs = ret;
@@ -450,6 +453,26 @@ static int cfg_parse_acme_kws(char **args, int
section_type, struct proxy *curpx
ha_alert("parsing [%s:%d]: out of memory.\n", file,
linenum);
goto out;
}
+ } else if (strcmp(args[0], "eab-mac-alg") == 0) {
+ if (!*args[1]) {
+ ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section
requires an argument\n", file, linenum, args[0], cursection);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ if (alertif_too_many_args(1, file, linenum, args, &err_code))
+ goto out;
+
+ if (strcmp(args[1], "HS256") == 0) {
+ cur_acme->eab.mac_alg = JWS_ALG_HS256;
+ } else if (strcmp(args[1], "HS384") == 0) {
+ cur_acme->eab.mac_alg = JWS_ALG_HS384;
+ } else if (strcmp(args[1], "HS512") == 0) {
+ cur_acme->eab.mac_alg = JWS_ALG_HS512;
+ } else {
+ ha_alert("parsing [%s:%d]: keyword '%s' in '%s' must be
one of the following: HS256, HS384, HS512\n", file, linenum, args[0],
cursection);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
} else if (strcmp(args[0], "challenge") == 0) {
if ((!*args[1]) ||
((strcasecmp("http-01", args[1]) != 0) &&
@@ -882,6 +905,7 @@ static int cfg_postsection_acme()
if (rv >= 1) {
struct buffer *dec_mac = get_trash_chunk();
+ int alg_bytes = 0;
rv = base64dec(trash.area, trash.data, dec_mac->area,
dec_mac->size);
if (rv < 0) {
@@ -891,9 +915,19 @@ static int cfg_postsection_acme()
}
dec_mac->data = rv;
- if (rv < 32) {
- ha_alert("acme: section '%s': EAB mac key from '%s' is
only %d bytes long, but at least 32 bytes is required for the specified mac
type.\n",
- cur_acme->name, cur_acme->eab.kid_file, rv);
+ switch (cur_acme->eab.mac_alg) {
+ case JWS_ALG_HS256: alg_bytes = 32; break;
+ case JWS_ALG_HS384: alg_bytes = 48; break;
+ case JWS_ALG_HS512: alg_bytes = 64; break;
+ default:
+ ha_alert("acme: invalid mac alg.\n");
+ err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
+ goto out;
+ }
+
+ if (rv < alg_bytes) {
+ ha_alert("acme: section '%s': EAB mac key from '%s' is
only %d bytes long, but at least %d bytes is required for the specified mac
type.\n",
+ cur_acme->name, cur_acme->eab.kid_file, rv,
alg_bytes);
err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
goto out;
}
@@ -1112,6 +1146,7 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, {
{ CFG_ACME, "dns-timeout", cfg_parse_acme_kws },
{ CFG_ACME, "eab-key-id", cfg_parse_acme_kws },
{ CFG_ACME, "eab-mac-key", cfg_parse_acme_kws },
+ { CFG_ACME, "eab-mac-alg", cfg_parse_acme_kws },
{ CFG_ACME, "acme-vars", cfg_parse_acme_vars_provider },
{ CFG_ACME, "provider-name", cfg_parse_acme_vars_provider },
{ CFG_GLOBAL, "acme.scheduler", cfg_parse_global_acme_sched },
@@ -1371,13 +1406,12 @@ int acme_jws_payload(struct buffer *req, struct ist
nonce, struct ist url, EVP_P
return ret;
}
-int acme_jws_eab_payload(struct ist url, EVP_PKEY *acc_key, struct buffer
mac_key, char *kid, struct buffer *output, char **errmsg)
+int acme_jws_eab_payload(struct ist url, EVP_PKEY *acc_key, struct buffer
mac_key, enum jwt_alg alg, char *kid, struct buffer *output, char **errmsg)
{
struct buffer *b64payload = NULL;
struct buffer *b64prot = NULL;
struct buffer *b64sign = NULL;
struct buffer *jwk = NULL;
- enum jwt_alg alg = JWS_ALG_HS256;
int ret = 1;
b64payload = alloc_trash_chunk();
@@ -2370,7 +2404,7 @@ int acme_req_account(struct task *task, struct acme_ctx
*ctx, int newaccount, ch
if (newaccount) {
chunk_appendf(req_in, "{");
if (ctx->cfg->eab.mac_key.data > 0 && ctx->cfg->eab.kid !=
NULL) {
- if (acme_jws_eab_payload(ctx->resources.newAccount,
ctx->cfg->account.pkey, ctx->cfg->eab.mac_key, ctx->cfg->eab.kid, eab_req_out,
errmsg) != 0)
+ if (acme_jws_eab_payload(ctx->resources.newAccount,
ctx->cfg->account.pkey, ctx->cfg->eab.mac_key, ctx->cfg->eab.mac_alg,
ctx->cfg->eab.kid, eab_req_out, errmsg) != 0)
goto out;
chunk_appendf(req_in, "\"externalAccountBinding\":
%.*s,", (int)eab_req_out->data, eab_req_out->area);
}
--
2.53.0