or "ECDSA". You can also configure the "curves" for ECDSA and the number of
"bits" for RSA. By default EC384 keys are generated.
+map <map>
+ Configure the map which will be used to store token (key) and thumbprint
+ (value), which is useful to reply to a challenge when there are multiple
+ account used. The acme task will add entries before validating the challenge
+ and will remove the entries at the end of the task.
Example:
frontend in
bind *:80
bind *:443 ssl
- http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].${ACCOUNT_THUMBPRINT}\n" if { path_beg '/.well-known/acme-challenge/' }
+ http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[path,field(-1,/),map(virt@acme)]\n" if { path_beg '/.well-known/acme-challenge/' }
ssl-f-use crt "foo.example.com.pem.rsa" acme LE1 domains "foo.example.com.pem,bar.example.com"
ssl-f-use crt "foo.example.com.pem.ecdsa" acme LE2 domains "foo.example.com.pem,bar.example.com"
challenge HTTP-01
keytype RSA
bits 2048
+ map virt@acme
acme LE2
directory https://acme-staging-v02.api.letsencrypt.org/directory
challenge HTTP-01
keytype ECDSA
curves P-384
+ map virt@acme
4. Proxies
----------
#include <haproxy/jws.h>
#include <haproxy/list.h>
#include <haproxy/log.h>
+#include <haproxy/pattern.h>
#include <haproxy/ssl_ckch.h>
#include <haproxy/ssl_sock.h>
#include <haproxy/ssl_utils.h>
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
goto out;
}
+ } else if (strcmp(args[0], "map") == 0) {
+ /* save the map name for thumbprint + token storage */
+ 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;
+
+ cur_acme->map = strdup(args[1]);
+ if (!cur_acme->map) {
+ err_code |= ERR_ALERT | ERR_FATAL;
+ ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ goto out;
+ }
} else if (*args[0] != 0) {
ha_alert("parsing [%s:%d]: unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
{ CFG_ACME, "keytype", cfg_parse_acme_cfg_key },
{ CFG_ACME, "bits", cfg_parse_acme_cfg_key },
{ CFG_ACME, "curves", cfg_parse_acme_cfg_key },
+ { CFG_ACME, "map", cfg_parse_acme_kws },
{ 0, NULL, NULL },
}};
task_wakeup(task, TASK_WOKEN_MSG);
}
+/*
+ * Add a map entry with <challenge> as the key, and <thumprint> as value in the virt@acme map.
+ * Return 0 upon success or 1 otherwise.
+ */
+static int acme_add_challenge_map(const char *map, const char *challenge, const char *thumbprint, char **errmsg)
+{
+ int ret = 1;
+ struct pat_ref *ref;
+ struct pat_ref_elt *elt;
+
+ /* when no map configured, return without error */
+ if (!map)
+ return 0;
+
+ ref = pat_ref_lookup("virt@acme");
+ if (!ref) {
+ memprintf(errmsg, "Unknown map identifier 'virt@acme'.\n");
+ goto out;
+ }
+
+ HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
+ elt = pat_ref_load(ref, ref->curr_gen, challenge, thumbprint, -1, errmsg);
+ HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
+
+ if (elt == NULL)
+ goto out;
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+/*
+ * Remove the <challenge> from the virt@acme map
+ */
+static void acme_del_challenge_map(const char *map, const char *challenge)
+{
+ struct pat_ref *ref;
+
+ /* when no map configured, return without error */
+ if (!map)
+ return;
+
+ ref = pat_ref_lookup(map);
+ if (!ref)
+ goto out;
+
+ HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
+ pat_ref_delete(ref, challenge);
+ HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
+
+out:
+ return;
+}
+
+/*
+ * Remove all challenges from an acme_ctx from the virt@acme map
+ */
+static void acme_del_acme_ctx_map(const struct acme_ctx *ctx)
+{
+ struct acme_auth *auth;
+
+ /* when no map configured, return without error */
+ if (!ctx->cfg->map)
+ return;
+
+ auth = ctx->auths;
+ while (auth) {
+ acme_del_challenge_map(ctx->cfg->map, auth->token.ptr);
+ auth = auth->next;
+ }
+ return;
+}
int acme_http_req(struct task *task, struct acme_ctx *ctx, struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs, struct ist payload)
{
goto error;
}
+ if (acme_add_challenge_map(ctx->cfg->map, auth->token.ptr, ctx->cfg->account.thumbprint, errmsg) != 0) {
+ memprintf(errmsg, "couldn't add the token to virt@acme: %s", *errmsg);
+ goto error;
+ }
+
/* we only need one challenge, and iteration is only used to found the right one */
break;
}
ha_free(&errmsg);
end:
+ acme_del_acme_ctx_map(ctx);
acme_ctx_destroy(ctx);
task_destroy(task);
task = NULL;