]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: Add 'show ssl ocsp-updates' CLI command
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Tue, 28 Feb 2023 16:46:23 +0000 (17:46 +0100)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 2 Mar 2023 14:37:17 +0000 (15:37 +0100)
This command can be used to dump information about the entries contained
in the ocsp update tree. It will display one line per concerned OCSP
response and will contain the expected next update time as well as the
time of the last successful update, and the number of successful and
failed attempts.

doc/management.txt
src/ssl_ocsp.c

index 7586affe345afd5f1e1d0a1c463000c76f380e13..1fa7d6cb5fcca21c3fa45e715a08932b2ba7769a 100644 (file)
@@ -3468,6 +3468,37 @@ show ssl ocsp-response [<id>]
       Next Update: Oct 12 15:43:38 2048 GMT
       [...]
 
+show ssl ocsp-updates
+  Display information about the entries concerned by the OCSP update mechanism.
+  The command will output one line per OCSP response and will contain the
+  expected update time of the response as well as the time of the last
+  successful update and counters of successful and failed updates. It will also
+  give the status of the last update (successful or not) in numerical form as
+  well as text form. See below for a full list of possible errors. The lines
+  will be sorted by ascending 'Next Update' time. The lines will also contain a
+  path to the first frontend certificate that uses the OCSP response.
+  See "show ssl ocsp-response" command and "ocsp-update" option for more
+  information on the OCSP auto update.
+
+  The update error codes and error strings can be the following:
+
+  +----+-------------------------------------+
+  | ID | message                             |
+  +----+-------------------------------------+
+  |  0 | "Unknown"                           |
+  |  1 | "Update successful"                 |
+  |  2 | "HTTP error"                        |
+  |  3 | "Missing \"ocsp-response\" header"  |
+  |  4 | "OCSP response check failure"       |
+  |  5 | "Error during insertion"            |
+  +----+-------------------------------------+
+
+    Example :
+      $ echo "show ssl ocsp-updates" | socat /tmp/haproxy.sock -
+        OCSP Certid | Path | Next Update | Last Update | Successes | Failures | Last Update Status | Last Update Status (str)
+            303b300906052b0e03021a050004148a83e0060faff709ca7e9b95522a2e81635fda0a0414f652b0e435d5ea923851508f0adbe92d85de007a02021015 | /path_to_cert/cert.pem | 30/Jan/2023:00:08:09 +0000 | - | 0 | 1 | 2 | HTTP error
+            304b300906052b0e03021a0500041448dac9a0fb2bd32d4ff0de68d2f567b735f9b3c40414142eb317b75856cbae500940e61faf9d8b14c2c6021203e16a7aa01542f291237b454a627fdea9c1 | /path_to_cert/other_cert.pem | 30/Jan/2023:01:07:09 +0000 | 30/Jan/2023:00:07:09 +0000 | 1 | 0 | 1 | Update successful
+
 show ssl providers
   Display the names of the providers loaded by OpenSSL during init. Provider
   loading can indeed be configured via the OpenSSL configuration file and this
index e234c7ad6ead209d5582e0e4e39587d4ca81fdba..eaac5f7348c0dd7d448fdd56a8ab26278b104af3 100644 (file)
@@ -1724,10 +1724,166 @@ int ocsp_update_check_cfg_consistency(struct ckch_store *store, struct crtlist_e
        return err_code;
 }
 
+struct show_ocsp_updates_ctx {
+       struct certificate_ocsp *cur_ocsp;
+};
+
+/*
+ * Parsing function for 'show ssl ocsp-updates [nb]'.
+ */
+static int cli_parse_show_ocsp_updates(char **args, char *payload, struct appctx *appctx, void *private)
+{
+#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
+       struct show_ocsp_updates_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
+
+       HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
+
+       return 0;
+#else
+       return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
+#endif
+}
+
+/*
+ * Dump information about an ocsp response concerning ocsp auto update.
+ * It follows the following format :
+ * OCSP Certid | Path | Next Update | Last Update | Successes | Failures | Last Update Status | Last Update Status (str)
+ * Return 0 in case of success.
+ */
+static int dump_ocsp_update_info(struct certificate_ocsp *ocsp, struct buffer *out)
+{
+       struct tm tm = {};
+       char *ret;
+       int i;
+       time_t next_update;
+
+       /* Dump OCSP certid */
+       for (i = 0; i < ocsp->key_length; ++i) {
+               chunk_appendf(out, "%02x", ocsp->key_data[i]);
+       }
+
+       chunk_appendf(out, " | ");
+
+       /* Dump path */
+       chunk_appendf(out, "%s", ocsp->path);
+
+       chunk_appendf(out, " | ");
+
+       /* Dump next update time */
+       if (ocsp->next_update.key != 0) {
+               next_update = ocsp->next_update.key;
+               get_localtime(ocsp->next_update.key, &tm);
+       } else {
+               next_update = date.tv_sec;
+               get_localtime(date.tv_sec, &tm);
+       }
+       ret = localdate2str_log(b_orig(out)+b_data(out), next_update, &tm, b_size(out)-b_data(out));
+
+       if (ret == NULL)
+               return 1;
+
+       out->data = (ret - out->area);
+
+       chunk_appendf(out, " | ");
+
+       /* Dump last update time or "-" if no update occurred yet */
+       if (ocsp->last_update) {
+               get_localtime(ocsp->last_update, &tm);
+               ret = localdate2str_log(b_orig(out)+b_data(out), ocsp->last_update, &tm, b_size(out)-b_data(out));
+
+               if (ret == NULL)
+                       return 1;
+
+               out->data = (ret - out->area);
+       } else
+               chunk_appendf(out, "-");
+
+       chunk_appendf(out, " | ");
+
+       /* Number of successful updates */
+       chunk_appendf(out, "%d", ocsp->num_success);
+
+       chunk_appendf(out, " | ");
+
+       /* Number of failed updates */
+       chunk_appendf(out, "%d", ocsp->num_failure);
+
+       chunk_appendf(out, " | ");
+
+       /* Last update status */
+       chunk_appendf(out, "%d", ocsp->last_update_status);
+
+       chunk_appendf(out, " | ");
+
+       /* Last update status str */
+       if (ocsp->last_update_status >= OCSP_UPDT_ERR_LAST)
+               chunk_appendf(out, "-");
+       else
+               chunk_appendf(out, "%s", istptr(ocsp_update_errors[ocsp->last_update_status]));
+
+       chunk_appendf(out, "\n");
+
+       return 0;
+}
+
+static int cli_io_handler_show_ocsp_updates(struct appctx *appctx)
+{
+       struct show_ocsp_updates_ctx *ctx = appctx->svcctx;
+       struct eb64_node *node;
+       struct certificate_ocsp *ocsp = NULL;
+       struct buffer *trash = get_trash_chunk();
+
+       if (!ctx->cur_ocsp) {
+               node = eb64_first(&ocsp_update_tree);
+               chunk_appendf(trash, "OCSP Certid | Path | Next Update | Last Update | Successes | Failures | Last Update Status | Last Update Status (str)\n");
+
+               /* Look for an entry currently being updated */
+               ocsp = ssl_ocsp_task_ctx.cur_ocsp;
+               if (ocsp) {
+                       if (dump_ocsp_update_info(ocsp, trash))
+                               goto end;
+               }
+
+               if (applet_putchk(appctx, trash) == -1)
+                       goto yield;
+
+       } else {
+               node = &((struct certificate_ocsp*)ctx->cur_ocsp)->next_update;
+       }
+
+       while (node) {
+               ocsp = eb64_entry(node, struct certificate_ocsp, next_update);
+
+               chunk_reset(trash);
+               if (dump_ocsp_update_info(ocsp, trash))
+                       goto end;
+
+               if (applet_putchk(appctx, trash) == -1) {
+                       ctx->cur_ocsp = ocsp;
+                       goto yield;
+               }
+
+               node = eb64_next(node);
+       }
+
+end:
+       return 1;
+
+yield:
+       return 0; /* should come back */
+}
+
+static void cli_release_show_ocsp_updates(struct appctx *appctx)
+{
+       HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
+}
+
+
 static struct cli_kw_list cli_kws = {{ },{
        { { "set", "ssl", "ocsp-response", NULL }, "set ssl ocsp-response <resp|payload>    : update a certificate's OCSP Response from a base64-encode DER",      cli_parse_set_ocspresponse, NULL },
 
        { { "show", "ssl", "ocsp-response", NULL },"show ssl ocsp-response [id]             : display the IDs of the OCSP responses used in memory, or the details of a single OCSP response", cli_parse_show_ocspresponse, cli_io_handler_show_ocspresponse, NULL },
+       { { "show", "ssl", "ocsp-updates", NULL }, "show ssl ocsp-updates                   : display information about the next 'nb' ocsp responses that will be updated automatically", cli_parse_show_ocsp_updates, cli_io_handler_show_ocsp_updates, cli_release_show_ocsp_updates },
 #if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
        { { "update", "ssl", "ocsp-response", NULL }, "update ssl ocsp-response <certfile>  : send ocsp request and update stored ocsp response",                  cli_parse_update_ocsp_response, cli_io_handler_update_ocsp_response, cli_release_update_ocsp_response },
 #endif