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
|
||||
- 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
|
||||
Dump all messages emitted during the startup of the current haproxy process,
|
||||
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/cli.h>
|
||||
#include <haproxy/errors.h>
|
||||
#include <haproxy/proxy.h>
|
||||
#include <haproxy/sc_strm.h>
|
||||
#include <haproxy/ssl_ckch.h>
|
||||
#include <haproxy/ssl_sock.h>
|
||||
@ -86,6 +87,15 @@ struct show_cert_ctx {
|
||||
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" */
|
||||
struct dump_cert_ctx {
|
||||
struct ckch_store *ckchs;
|
||||
@ -1535,6 +1545,177 @@ struct cert_exts cert_exts[] = {
|
||||
{ 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 */
|
||||
static void cli_release_show_cert(struct appctx *appctx)
|
||||
@ -4182,6 +4363,8 @@ void ckch_deinit()
|
||||
|
||||
/* register cli keywords */
|
||||
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 },
|
||||
{ { "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 },
|
||||
|
Loading…
Reference in New Issue
Block a user