mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-30 10:41:01 +02:00
MINOR: acme: allow IP SAN in certificate request
Implement IP in both requestOrder and CSR so a certificate with SAN IPs can be generated.
This commit is contained in:
parent
0d14bb7473
commit
95c400d08e
@ -32193,6 +32193,19 @@ domains <string>
|
||||
|
||||
load crt "example.com.pem" acme LE domains "bar.example.com,foo.example.com"
|
||||
|
||||
ips <string>
|
||||
Configure the list of IP addresses that will be included as IP SANs in the
|
||||
ACME certificate. IP addresses are separated by commas in the list.
|
||||
|
||||
Generating a certificate with IPs might require the use of the "shortlived"
|
||||
profile.
|
||||
|
||||
See also Section 12.8 ("ACME"), "acme" and "domains" in this section.
|
||||
|
||||
Example:
|
||||
|
||||
load crt "server.pem" acme LE ips "192.0.2.1,2001:db8::1"
|
||||
|
||||
key <filename>
|
||||
This argument is optional. Load a private key in PEM format. If a private key
|
||||
was already defined in "crt", it will overwrite it.
|
||||
|
||||
@ -72,6 +72,7 @@ struct ckch_conf {
|
||||
struct {
|
||||
char *id;
|
||||
char **domains;
|
||||
char **ips;
|
||||
} acme;
|
||||
struct {
|
||||
struct {
|
||||
|
||||
31
src/acme.c
31
src/acme.c
@ -2029,7 +2029,9 @@ int acme_req_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
{ IST_NULL, IST_NULL }
|
||||
};
|
||||
int ret = 1;
|
||||
int first = 1;
|
||||
char **san = ctx->store->conf.acme.domains;
|
||||
char **ip = ctx->store->conf.acme.ips;
|
||||
|
||||
if ((req_in = alloc_trash_chunk()) == NULL)
|
||||
goto error;
|
||||
@ -2038,12 +2040,16 @@ int acme_req_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||
|
||||
chunk_printf(req_in, "{ \"identifiers\": [ ");
|
||||
|
||||
if (!san)
|
||||
if (!san && !ip)
|
||||
goto error;
|
||||
|
||||
for (; san && *san; san++) {
|
||||
// fprintf(stderr, "%s:%d %s\n", __FUNCTION__, __LINE__, *san);
|
||||
chunk_appendf(req_in, "%s{ \"type\": \"dns\", \"value\": \"%s\" }", (*san == *ctx->store->conf.acme.domains) ? "" : ",", *san);
|
||||
chunk_appendf(req_in, "%s{ \"type\": \"dns\", \"value\": \"%s\" }", first ? "" : ",", *san);
|
||||
first = 0;
|
||||
}
|
||||
for (; ip && *ip; ip++) {
|
||||
chunk_appendf(req_in, "%s{ \"type\": \"ip\", \"value\": \"%s\" }", first ? "" : ",", *ip);
|
||||
first = 0;
|
||||
}
|
||||
|
||||
chunk_appendf(req_in, " ]");
|
||||
@ -3038,7 +3044,7 @@ end:
|
||||
/*
|
||||
* Generate a X509_REQ using a PKEY and a list of SAN finished by a NULL entry
|
||||
*/
|
||||
X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
||||
X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san, char **ips)
|
||||
{
|
||||
struct buffer *san_trash = NULL;
|
||||
X509_REQ *x = NULL;
|
||||
@ -3060,9 +3066,9 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
||||
if ((nm = X509_NAME_new()) == NULL)
|
||||
goto error;
|
||||
|
||||
/* common name is the first SAN in the list */
|
||||
/* common name is the first domain, or the first IP if no domain */
|
||||
if (!X509_NAME_add_entry_by_txt(nm, "CN", MBSTRING_ASC,
|
||||
(unsigned char *)san[0], -1, -1, 0))
|
||||
(unsigned char *)(san ? san[0] : ips[0]), -1, -1, 0))
|
||||
goto error;
|
||||
/* assign the CN to the REQ */
|
||||
if (!X509_REQ_set_subject_name(x, nm))
|
||||
@ -3072,8 +3078,11 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
||||
if ((exts = sk_X509_EXTENSION_new_null()) == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; san[i]; i++) {
|
||||
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
||||
for (i = 0; san && san[i]; i++) {
|
||||
chunk_appendf(san_trash, "%sDNS:%s", san_trash->data ? "," : "", san[i]);
|
||||
}
|
||||
for (i = 0; ips && ips[i]; i++) {
|
||||
chunk_appendf(san_trash, "%sIP:%s", san_trash->data ? "," : "", ips[i]);
|
||||
}
|
||||
if ((str_san = my_strndup(san_trash->area, san_trash->data)) == NULL)
|
||||
goto error;
|
||||
@ -3159,8 +3168,8 @@ static int acme_start_task(struct ckch_store *store, char **errmsg)
|
||||
struct ckch_store *newstore = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if (!store->conf.acme.domains) {
|
||||
memprintf(errmsg, "No 'domains' were configured for certificate. ");
|
||||
if (!store->conf.acme.domains && !store->conf.acme.ips) {
|
||||
memprintf(errmsg, "No 'domains' or 'ips' were configured for certificate. ");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -3211,7 +3220,7 @@ static int acme_start_task(struct ckch_store *store, char **errmsg)
|
||||
pkey = NULL;
|
||||
}
|
||||
|
||||
ctx->req = acme_x509_req(newstore->data->key, store->conf.acme.domains);
|
||||
ctx->req = acme_x509_req(newstore->data->key, store->conf.acme.domains, store->conf.acme.ips);
|
||||
if (!ctx->req) {
|
||||
memprintf(errmsg, "%sCan't generate a CSR.", *errmsg ? *errmsg : "");
|
||||
goto err;
|
||||
|
||||
@ -1112,6 +1112,26 @@ struct ckch_store *ckchs_dup(const struct ckch_store *src)
|
||||
dst->conf.acme.domains = r;
|
||||
}
|
||||
|
||||
if (src->conf.acme.ips) {
|
||||
r = NULL;
|
||||
n = 0;
|
||||
|
||||
/* copy the array of IP strings */
|
||||
|
||||
while (src->conf.acme.ips[n]) {
|
||||
r = realloc(r, sizeof(char *) * (n + 2));
|
||||
if (!r)
|
||||
goto error;
|
||||
|
||||
r[n] = strdup(src->conf.acme.ips[n]);
|
||||
if (!r[n])
|
||||
goto error;
|
||||
n++;
|
||||
}
|
||||
r[n] = 0;
|
||||
dst->conf.acme.ips = r;
|
||||
}
|
||||
|
||||
return dst;
|
||||
|
||||
error:
|
||||
@ -4904,6 +4924,7 @@ struct ckch_conf_kws ckch_conf_kws[] = {
|
||||
{ "acme", offsetof(struct ckch_conf, acme.id), PARSE_TYPE_STR, ckch_conf_acme_init, },
|
||||
#endif
|
||||
{ "domains", offsetof(struct ckch_conf, acme.domains), PARSE_TYPE_ARRAY_SUBSTR, NULL, },
|
||||
{ "ips", offsetof(struct ckch_conf, acme.ips), PARSE_TYPE_ARRAY_SUBSTR, NULL, },
|
||||
{ "generate-dummy", offsetof(struct ckch_conf, gencrt.on), PARSE_TYPE_ONOFF, NULL, },
|
||||
{ "keytype", offsetof(struct ckch_conf, gencrt.key.type), PARSE_TYPE_STR, NULL, },
|
||||
{ "bits", offsetof(struct ckch_conf, gencrt.key.bits), PARSE_TYPE_INT, NULL, },
|
||||
@ -5276,6 +5297,14 @@ void ckch_conf_clean(struct ckch_conf *conf)
|
||||
}
|
||||
ha_free(&conf->acme.domains);
|
||||
|
||||
r = conf->acme.ips;
|
||||
while (r && *r) {
|
||||
char *prev = *r;
|
||||
r++;
|
||||
free(prev);
|
||||
}
|
||||
ha_free(&conf->acme.ips);
|
||||
|
||||
}
|
||||
|
||||
static char current_crtstore_name[PATH_MAX] = {};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user