mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 15:17:01 +02:00
MEDIUM: ssl/cli: "show ssl sni" list the loaded SNI in frontends
The "show ssl sni" command, allows one to dump the list of SNI in an haproxy process, or a designated frontend. It lists the SNI with the type, filename, and dates of expiration and activation
This commit is contained in:
parent
5454824e31
commit
5d1b30d6b8
@ -3761,6 +3761,41 @@ show ssl providers
|
|||||||
- fips
|
- fips
|
||||||
- base
|
- base
|
||||||
|
|
||||||
|
show ssl sni [-f <frontend>]
|
||||||
|
Dump every SNI configured for the designated frontend, or all frontends if no
|
||||||
|
frontend was specified. It allows to see what SNI are offered for a frontend,
|
||||||
|
and to identify if a SNI is defined multiple time by multiple certificates for
|
||||||
|
the same frontend.
|
||||||
|
|
||||||
|
Columns are separated by a single \t, allowing to parse it simply.
|
||||||
|
|
||||||
|
The frontend/bind column shows the frontend name followed by the bind line
|
||||||
|
position in the configuration (file:linenum).
|
||||||
|
|
||||||
|
The SNI column shows the SNI, it can be either a CN, a SAN or a positive
|
||||||
|
filter from a crt-list. Negative filters are not displayed.
|
||||||
|
|
||||||
|
The 'type' column shows the encryption algorithm type, it can be "rsa", "ecdsa" or "dsa".
|
||||||
|
|
||||||
|
The default certificates of a bind line, (which are either declared
|
||||||
|
explicitely by 'default-crt' or is implicitely the first certificate of a bind
|
||||||
|
line when no 'strict-sni' is used) shows the '*' character in the SNI column.
|
||||||
|
|
||||||
|
The 'filename' column can be either a filename from the configuration, or an
|
||||||
|
alias declarated in a crt-store.
|
||||||
|
|
||||||
|
The 'NotAfter' and 'NotBefore' columns are directly extracted from the X509
|
||||||
|
leaf certificate.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$ echo "@1 show ssl sni" | socat /var/run/haproxy-master.sock - | column -t -s $'\t'
|
||||||
|
# Frontend/Bind SNI Type Filename NotAfter NotBefore
|
||||||
|
li1/haproxy.cfg:10021 machine10 rsa machine10.pem.rsa Jun 13 13:37:21 2024 GMT May 14 13:37:21 2024 GMT
|
||||||
|
li1/haproxy.cfg:10021 machine10 ecdsa machine10.pem.ecdsa Jun 13 13:37:21 2024 GMT May 14 13:37:21 2024 GMT
|
||||||
|
li1/haproxy.cfg:10021 localhost rsa localhost.pem.rsa Jun 13 13:37:11 2024 GMT May 14 13:37:11 2024 GMT
|
||||||
|
li1/haproxy.cfg:10021 localhost ecdsa localhost.pem.ecdsa Jun 13 13:37:10 2024 GMT May 14 13:37:10 2024 GMT
|
||||||
|
li1/haproxy.cfg:10021 * rsa localhost.pem.rsa Jun 13 13:37:11 2024 GMT May 14 13:37:11 2024 GMT
|
||||||
|
|
||||||
show startup-logs
|
show startup-logs
|
||||||
Dump all messages emitted during the startup of the current haproxy process,
|
Dump all messages emitted during the startup of the current haproxy process,
|
||||||
each startup-logs buffer is unique to its haproxy worker.
|
each startup-logs buffer is unique to its haproxy worker.
|
||||||
|
183
src/ssl_ckch.c
183
src/ssl_ckch.c
@ -32,6 +32,7 @@
|
|||||||
#include <haproxy/channel.h>
|
#include <haproxy/channel.h>
|
||||||
#include <haproxy/cli.h>
|
#include <haproxy/cli.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/sc_strm.h>
|
#include <haproxy/sc_strm.h>
|
||||||
#include <haproxy/ssl_ckch.h>
|
#include <haproxy/ssl_ckch.h>
|
||||||
#include <haproxy/ssl_sock.h>
|
#include <haproxy/ssl_sock.h>
|
||||||
@ -86,6 +87,15 @@ struct show_cert_ctx {
|
|||||||
int transaction;
|
int transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* CLI context used by "show ssl sni" */
|
||||||
|
struct show_sni_ctx {
|
||||||
|
struct proxy *px;
|
||||||
|
struct bind_conf *bind;
|
||||||
|
struct ebmb_node *n;
|
||||||
|
int nodetype;
|
||||||
|
int onefrontend;
|
||||||
|
};
|
||||||
|
|
||||||
/* CLI context used by "dump ssl cert" */
|
/* CLI context used by "dump ssl cert" */
|
||||||
struct dump_cert_ctx {
|
struct dump_cert_ctx {
|
||||||
struct ckch_store *ckchs;
|
struct ckch_store *ckchs;
|
||||||
@ -1535,6 +1545,177 @@ struct cert_exts cert_exts[] = {
|
|||||||
{ NULL, CERT_TYPE_MAX, NULL },
|
{ NULL, CERT_TYPE_MAX, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* release function of the `show ssl sni' command */
|
||||||
|
static void cli_release_show_sni(struct appctx *appctx)
|
||||||
|
{
|
||||||
|
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IO handler of "show ssl sni [<frontend>]".
|
||||||
|
* It makes use of a show_sni_ctx context
|
||||||
|
*
|
||||||
|
* The fonction does loop over the frontend, the bind_conf and the sni_ctx.
|
||||||
|
*/
|
||||||
|
static int cli_io_handler_show_sni(struct appctx *appctx)
|
||||||
|
{
|
||||||
|
struct show_sni_ctx *ctx = appctx->svcctx;
|
||||||
|
struct buffer *trash = alloc_trash_chunk();
|
||||||
|
struct ebmb_node *n = NULL;
|
||||||
|
int type = 0;
|
||||||
|
struct bind_conf *bind = NULL;
|
||||||
|
struct proxy *px = NULL;
|
||||||
|
|
||||||
|
if (trash == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* ctx->bind is NULL only once we finished dumping a frontend or when starting
|
||||||
|
* so let's dump the header in these cases*/
|
||||||
|
if (ctx->bind == NULL && (ctx->onefrontend == 1 || (ctx->onefrontend == 0 && ctx->px == proxies_list)))
|
||||||
|
chunk_appendf(trash, "# Frontend/Bind\tSNI\tType\tFilename\tNotAfter\tNotBefore\n");
|
||||||
|
if (applet_putchk(appctx, trash) == -1)
|
||||||
|
goto yield;
|
||||||
|
|
||||||
|
for (px = ctx->px; px; px = px->next) {
|
||||||
|
|
||||||
|
/* only check the frontends which are not internal proxies */
|
||||||
|
if (!(px->cap & PR_CAP_FE) || (px->cap & PR_CAP_INT))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bind = ctx->bind;
|
||||||
|
/* if we didn't get a bind from the previous yield */
|
||||||
|
if (!bind)
|
||||||
|
bind = LIST_ELEM(px->conf.bind.n, typeof(bind), by_fe);
|
||||||
|
|
||||||
|
list_for_each_entry_from(bind, &px->conf.bind, by_fe) {
|
||||||
|
|
||||||
|
HA_RWLOCK_RDLOCK(SNI_LOCK, &bind->sni_lock);
|
||||||
|
|
||||||
|
/* do this twice: once for wildcards and once for standard SNI */
|
||||||
|
for (type = ctx->nodetype; type < 2; type++) {
|
||||||
|
|
||||||
|
n = ctx->n; /* get the node from previous yield */
|
||||||
|
|
||||||
|
if (!n) {
|
||||||
|
if (type == 0)
|
||||||
|
n = ebmb_first(&bind->sni_ctx);
|
||||||
|
else
|
||||||
|
n = ebmb_first(&bind->sni_w_ctx);
|
||||||
|
}
|
||||||
|
/* emty SNI tree, skip */
|
||||||
|
if (!n)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
struct sni_ctx *sni;
|
||||||
|
const char *name;
|
||||||
|
const char *certalg;
|
||||||
|
|
||||||
|
chunk_appendf(trash, "%s/%s:%d\t", bind->frontend->id, bind->file, bind->line);
|
||||||
|
|
||||||
|
sni = ebmb_entry(n, struct sni_ctx, name);
|
||||||
|
|
||||||
|
name = (char *)sni->name.key;
|
||||||
|
|
||||||
|
chunk_appendf(trash, "%s%s%s\t", sni->neg ? "!" : "", type ? "*" : "", name);
|
||||||
|
|
||||||
|
switch (sni->kinfo.sig) {
|
||||||
|
case TLSEXT_signature_ecdsa:
|
||||||
|
certalg = "ecdsa";
|
||||||
|
break;
|
||||||
|
case TLSEXT_signature_rsa:
|
||||||
|
certalg = "rsa";
|
||||||
|
break;
|
||||||
|
default: /* TLSEXT_signature_anonymous|dsa */
|
||||||
|
certalg = "dsa";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_appendf(trash, "%s\t", certalg);
|
||||||
|
|
||||||
|
/* we need to lock so the certificates in the ckch are not modified during the listing */
|
||||||
|
chunk_appendf(trash, "%s\t", sni->ckch_inst->ckch_store->path);
|
||||||
|
chunk_appendf(trash, "%s\t", x509_get_notafter(sni->ckch_inst->ckch_store->data->cert));
|
||||||
|
chunk_appendf(trash, "%s\n", x509_get_notbefore(sni->ckch_inst->ckch_store->data->cert));
|
||||||
|
|
||||||
|
if (applet_putchk(appctx, trash) == -1) {
|
||||||
|
HA_RWLOCK_RDUNLOCK(SNI_LOCK, &bind->sni_lock);
|
||||||
|
goto yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = ebmb_next(n);
|
||||||
|
}
|
||||||
|
ctx->n = NULL;
|
||||||
|
}
|
||||||
|
ctx->nodetype = 0;
|
||||||
|
HA_RWLOCK_RDUNLOCK(SNI_LOCK, &bind->sni_lock);
|
||||||
|
|
||||||
|
}
|
||||||
|
ctx->bind = NULL;
|
||||||
|
/* only want to display the specified frontend */
|
||||||
|
if (ctx->onefrontend)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx->px = NULL;
|
||||||
|
|
||||||
|
free_trash_chunk(trash);
|
||||||
|
return 1;
|
||||||
|
yield:
|
||||||
|
|
||||||
|
ctx->px = px;
|
||||||
|
ctx->bind = bind;
|
||||||
|
ctx->n = n;
|
||||||
|
ctx->nodetype = type;
|
||||||
|
|
||||||
|
free_trash_chunk(trash);
|
||||||
|
return 0; /* should come back */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* parsing function for 'show ssl sni [-f <frontend>]' */
|
||||||
|
static int cli_parse_show_sni(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
|
{
|
||||||
|
struct show_sni_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
|
||||||
|
ctx->px = proxies_list;
|
||||||
|
|
||||||
|
/* look for the right <frontend> to display */
|
||||||
|
if (*args[3]) {
|
||||||
|
struct proxy *px;
|
||||||
|
|
||||||
|
if (strcmp(args[3], "-f") != 0)
|
||||||
|
return cli_err(appctx, "'show ssl sni' only supports a '-f' option!\n");
|
||||||
|
|
||||||
|
if (*args[4] == '\0')
|
||||||
|
return cli_err(appctx, "'-f' requires a frontend name !\n");
|
||||||
|
|
||||||
|
for (px = proxies_list; px; px = px->next) {
|
||||||
|
|
||||||
|
/* only check the frontends */
|
||||||
|
if (!(px->cap & PR_CAP_FE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* skip the internal proxies */
|
||||||
|
if (px->cap & PR_CAP_INT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(px->id, args[3]) == 0) {
|
||||||
|
ctx->px = px;
|
||||||
|
ctx->onefrontend = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ctx->px == NULL)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
|
||||||
|
return cli_err(appctx, "Can't list SNIs\nOperations on certificates are currently locked!\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return cli_err(appctx, "Couldn't find the specified frontend!\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* release function of the `show ssl cert' command */
|
/* release function of the `show ssl cert' command */
|
||||||
static void cli_release_show_cert(struct appctx *appctx)
|
static void cli_release_show_cert(struct appctx *appctx)
|
||||||
@ -4182,6 +4363,8 @@ void ckch_deinit()
|
|||||||
|
|
||||||
/* register cli keywords */
|
/* register cli keywords */
|
||||||
static struct cli_kw_list cli_kws = {{ },{
|
static struct cli_kw_list cli_kws = {{ },{
|
||||||
|
{ { "show", "ssl", "sni", NULL }, "show ssl sni [-f <frontend>] : display the list of SNI and their corresponding filename", cli_parse_show_sni, cli_io_handler_show_sni, cli_release_show_sni },
|
||||||
|
|
||||||
{ { "new", "ssl", "cert", NULL }, "new ssl cert <certfile> : create a new certificate file to be used in a crt-list or a directory", cli_parse_new_cert, NULL, NULL },
|
{ { "new", "ssl", "cert", NULL }, "new ssl cert <certfile> : create a new certificate file to be used in a crt-list or a directory", cli_parse_new_cert, NULL, NULL },
|
||||||
{ { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
|
{ { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
|
||||||
{ { "commit", "ssl", "cert", NULL }, "commit ssl cert <certfile> : commit a certificate file", cli_parse_commit_cert, cli_io_handler_commit_cert, cli_release_commit_cert },
|
{ { "commit", "ssl", "cert", NULL }, "commit ssl cert <certfile> : commit a certificate file", cli_parse_commit_cert, cli_io_handler_commit_cert, cli_release_commit_cert },
|
||||||
|
Loading…
Reference in New Issue
Block a user