]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: Add 'tune.ssl.ocsp-update.mode' global option
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Mon, 25 Mar 2024 15:50:25 +0000 (16:50 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Wed, 27 Mar 2024 10:38:28 +0000 (11:38 +0100)
This option can be used to set a default ocsp-update mode for all
certificates of a given conf file. It allows to activate ocsp-update on
certificates without the need to create separate crt-lists. It can still
be superseded by the crt-list 'ocsp-update' option. It takes either "on"
or "off" as value and defaults to "off".
Since setting this new parameter to "on" would mean that we try to
enable ocsp-update on any certificate, and also certificates that don't
have an OCSP URI, the checks performed in ssl_sock_load_ocsp were
softened. We don't systematically raise an error when trying to enable
ocsp-update on a certificate that does not have an OCSP URI, be it via
the global option or the crt-list one. We will still raise an error when
a user tries to load a certificate that does have an OCSP URI but a
missing issuer certificate (if ocsp-update is enabled).

doc/configuration.txt
include/haproxy/ssl_sock-t.h
src/cfgparse-ssl.c
src/ssl_ckch.c
src/ssl_crtlist.c
src/ssl_ocsp.c
src/ssl_sock.c

index adbdbfb2a1f7c533b7d76c1c0d689315ade6fcf5..3441af1ccdb2adacd7dd1304b92da1049c76ec53 100644 (file)
@@ -3959,6 +3959,15 @@ tune.ssl.ocsp-update.mindelay <number>
   "tune.ssl.ocsp-update.maxdelay". See option "ocsp-update" for more
   information about the auto update mechanism.
 
+tune.ssl.ocsp-update.mode [ on | off ]
+  Sets the default ocsp-update mode for all certificates used in the
+  configuration. This global option can be superseded by the crt-list
+  "ocsp-update" option but an error will be raised if a given certificate has
+  two distinct configurations simultaneously. This option is set to "off" by
+  default.
+  See option "ocsp-update" for more information about the auto update
+  mechanism.
+
 tune.stick-counters <number>
   Sets the number of stick-counters that may be tracked at the same time by a
   connection or a request via "track-sc*" actions in "tcp-request" or
@@ -16047,11 +16056,15 @@ ocsp-update [ off | on ] (crt-list only)
   Its value defaults to 'off'.
   Please note that for now, this option can only be used in a crt-list line, it
   cannot be used directly on a bind line. It lies in this "Bind options"
-  section because it is still a frontend option. This limitation was set so
-  that the option applies to only one certificate at a time.
+  section because it is still a frontend option. For now, the only way to
+  enable OCSP auto update on a bind line certificate is via the global option
+  "tune.ocsp-update.mode".
   If a given certificate is used in multiple crt-lists with different values of
-  the 'ocsp-update' set, an error will be raised. Here is an example
-  configuration enabling it:
+  the 'ocsp-update' set, an error will be raised. Likewise, if a certificate
+  inherits from the global option on a bind line and has an incompatible
+  explicit 'ocsp-update' option set in a crt-list, the same error will be
+  raised.
+  Here is an example configuration enabling it:
 
   haproxy.cfg:
       frontend fe
@@ -16069,12 +16082,12 @@ ocsp-update [ off | on ] (crt-list only)
   hour limit. A minimum update interval of 5 minutes will still exist in order
   to avoid updating too often responses that have a really short expire time or
   even no 'Next Update' at all. Because of this hard limit, please note that
-  when auto update is set to 'on' or 'auto', any OCSP response loaded during
-  init will not be updated until at least 5 minutes, even if its expire time
-  ends before now+5m. This should not be too much of a hassle since an OCSP
-  response must be valid when it gets loaded during init (its expire time must
-  be in the future) so it is unlikely that this response expires in such a
-  short time after init.
+  when auto update is set to 'on', any OCSP response loaded during init will
+  not be updated until at least 5 minutes, even if its expire time ends before
+  now+5m. This should not be too much of a hassle since an OCSP response must
+  be valid when it gets loaded during init (its expire time must be in the
+  future) so it is unlikely that this response expires in such a short time
+  after init.
   On the other hand, if a certificate has an OCSP uri specified and no OCSP
   response, setting this option to 'on' for the given certificate will ensure
   that the OCSP response gets fetched automatically right after init.
index 2d8f8157e1b802d6291ab881999d156bd8dd5124..30e81e798b2d53f521c8fcef6aee03f74c83369e 100644 (file)
@@ -309,6 +309,7 @@ struct global_ssl {
        struct {
                unsigned int delay_max;
                unsigned int delay_min;
+               int mode; /* default mode used for ocsp auto-update (off, on) */
        } ocsp_update;
 #endif
 };
index 23b515d23e5533c15568378044c0c73ee26c0c4f..0ba31d703faf5109881f123e989f2e81e2038ce5 100644 (file)
@@ -2222,6 +2222,35 @@ static int ssl_parse_global_ocsp_mindelay(char **args, int section_type, struct
        return 0;
 }
 
+static int ssl_parse_global_ocsp_update_mode(char **args, int section_type, struct proxy *curpx,
+                                             const struct proxy *defpx, const char *file, int line,
+                                             char **err)
+{
+       int ret = 0;
+
+       if (!*args[1]) {
+               memprintf(err, "'%s' : expecting <on|off>", args[0]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       if (strcmp(args[1], "on") == 0)
+               global_ssl.ocsp_update.mode = SSL_SOCK_OCSP_UPDATE_ON;
+       else if (strcmp(args[1], "off") == 0)
+               global_ssl.ocsp_update.mode = SSL_SOCK_OCSP_UPDATE_OFF;
+       else {
+               memprintf(err, "'%s' : expecting <on|off>", args[0]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       if (global_ssl.ocsp_update.mode != SSL_SOCK_OCSP_UPDATE_OFF) {
+               /* We might need to create the main ocsp update task */
+               ret = ssl_create_ocsp_update_task(err);
+       }
+
+       return ret;
+}
+
+
 
 
 /* Note: must not be declared <const> as its list will be overwritten.
@@ -2411,6 +2440,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
 #ifndef OPENSSL_NO_OCSP
        { CFG_GLOBAL, "tune.ssl.ocsp-update.maxdelay", ssl_parse_global_ocsp_maxdelay },
        { CFG_GLOBAL, "tune.ssl.ocsp-update.mindelay", ssl_parse_global_ocsp_mindelay },
+       { CFG_GLOBAL, "tune.ssl.ocsp-update.mode", ssl_parse_global_ocsp_update_mode },
 #endif
        { 0, NULL, NULL },
 }};
index 299ca925bf7e0ebef87b058cb414246caa6fabb9..add42b69eb3353bb51595da3334cb2a656c8b159 100644 (file)
@@ -355,6 +355,8 @@ int ssl_sock_load_files_into_ckch(const char *path, struct ckch_data *data, char
                goto end;
        }
 
+       data->ocsp_update_mode = global_ssl.ocsp_update.mode;
+
        /* remove the ".crt" extension */
        if (global_ssl.extra_files_noext) {
                char *ext;
index 1790c96ce686a5e8db67d7bc84c4dcfeb81020ad..4cc1fd88c812630d2aee85fe87d19fb598753b68 100644 (file)
@@ -600,6 +600,8 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
                                entry->crtlist = newlist;
                                if (entry->ssl_conf)
                                        ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
+                               if (ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT)
+                                       ckchs->data->ocsp_update_mode = global_ssl.ocsp_update.mode;
                                ebpt_insert(&newlist->entries, &entry->node);
                                LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
                                LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
@@ -655,6 +657,8 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
 
                                        if (entry->ssl_conf)
                                                ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
+                                       if (ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT)
+                                               ckchs->data->ocsp_update_mode = global_ssl.ocsp_update.mode;
                                        ebpt_insert(&newlist->entries, &entry_dup->node);
                                        LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
                                        LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
@@ -685,6 +689,8 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
 
                        if (entry->ssl_conf)
                                ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
+                       if (ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT)
+                               ckchs->data->ocsp_update_mode = global_ssl.ocsp_update.mode;
                        ebpt_insert(&newlist->entries, &entry->node);
                        LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
                        LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
@@ -1364,6 +1370,8 @@ static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appc
 
        if (entry->ssl_conf)
                store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
+       if (store->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT)
+               store->data->ocsp_update_mode = global_ssl.ocsp_update.mode;
 
        /* check if it's possible to insert this new crtlist_entry */
        entry->node.key = store;
index eb1d17fe652cebcafb920c216adb234a0682cdf3..1add6bd009155e93d194993e69d784eaecc33a32 100644 (file)
@@ -1720,19 +1720,36 @@ static void cli_release_show_ocspresponse(struct appctx *appctx)
                ssl_sock_free_ocsp_instance(ctx->ocsp);
 }
 
-/* Check if the ckch_store and the entry does have the same configuration */
+/* Check if the ckch_store and the entry do have the same configuration. Also
+ * ensure that those options are compatible with the global ocsp-update mode. */
 int ocsp_update_check_cfg_consistency(struct ckch_store *store, struct crtlist_entry *entry, char *crt_path, char **err)
 {
        int err_code = ERR_NONE;
+       int incompat_found = 0;
 
-       if (store->data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_DFLT || entry->ssl_conf) {
-               if ((!entry->ssl_conf && store->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON)
-                   || (entry->ssl_conf && entry->ssl_conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_OFF &&
-                       store->data->ocsp_update_mode != entry->ssl_conf->ocsp_update)) {
-                       memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err : "", crt_path);
-                       err_code |= ERR_ALERT | ERR_FATAL;
-               }
+       switch(store->data->ocsp_update_mode) {
+       case SSL_SOCK_OCSP_UPDATE_DFLT:
+               if (entry && entry->ssl_conf && entry->ssl_conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_ON &&
+                   global_ssl.ocsp_update.mode != SSL_SOCK_OCSP_UPDATE_ON)
+                       incompat_found = 1;
+               break;
+       case SSL_SOCK_OCSP_UPDATE_OFF:
+               if ((entry && entry->ssl_conf && entry->ssl_conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_ON) ||
+                   ((!entry || !entry->ssl_conf) && global_ssl.ocsp_update.mode == SSL_SOCK_OCSP_UPDATE_ON))
+                       incompat_found = 1;
+               break;
+       case SSL_SOCK_OCSP_UPDATE_ON:
+               if ((entry && entry->ssl_conf && entry->ssl_conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_ON) ||
+                   ((!entry || !entry->ssl_conf) && global_ssl.ocsp_update.mode != SSL_SOCK_OCSP_UPDATE_ON))
+                       incompat_found = 1;
+               break;
        }
+
+       if (incompat_found) {
+               memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err : "", crt_path);
+               err_code |= ERR_ALERT | ERR_FATAL;
+       }
+
        return err_code;
 }
 
index 49565576c76bb078a3081409dd6c3f02b6bbe805..824bdaa721ba35d69860cbcf4e9a2cb5a720e162 100644 (file)
@@ -140,6 +140,7 @@ struct global_ssl global_ssl = {
 #ifndef OPENSSL_NO_OCSP
        .ocsp_update.delay_max = SSL_OCSP_UPDATE_DELAY_MAX,
        .ocsp_update.delay_min = SSL_OCSP_UPDATE_DELAY_MIN,
+       .ocsp_update.mode = SSL_SOCK_OCSP_UPDATE_DFLT,
 #endif
 };
 
@@ -1125,24 +1126,28 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data *
        char *err = NULL;
        size_t path_len;
        int inc_refcount_store = 0;
+       int enable_auto_update = (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON ||
+                                 (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT &&
+                                  global_ssl.ocsp_update.mode == SSL_SOCK_OCSP_UPDATE_ON));
 
        x = data->cert;
        if (!x)
                goto out;
 
        ssl_ocsp_get_uri_from_cert(x, ocsp_uri, &err);
-       /* We should have an "OCSP URI" field in order for auto update to work. */
-       if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON && b_data(ocsp_uri) == 0)
-               goto out;
-
-       /* In case of ocsp update mode set to 'on', this function might be
-        * called with no known ocsp response. If no ocsp uri can be found in
-        * the certificate, nothing needs to be done here. */
        if (!data->ocsp_response && !data->ocsp_cid) {
-               if (data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_ON || b_data(ocsp_uri) == 0) {
+               /* In case of ocsp update mode set to 'on', this function might
+                * be called with no known ocsp response. If no ocsp uri can be
+                * found in the certificate, nothing needs to be done here. */
+               if (!enable_auto_update || b_data(ocsp_uri) == 0) {
                        ret = 0;
                        goto out;
                }
+       } else {
+               /* If we have an OCSP response provided and the ocsp auto update
+                * enabled, we must raise an error if no OCSP URI was found. */
+               if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON && b_data(ocsp_uri) == 0)
+                       goto out;
        }
 
        issuer = data->ocsp_issuer;
@@ -1284,7 +1289,7 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data *
                 */
                memcpy(iocsp->path, path, path_len + 1);
 
-               if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) {
+               if (enable_auto_update) {
                        ssl_ocsp_update_insert(iocsp);
                        /* If we are during init the update task is not
                         * scheduled yet so a wakeup won't do anything.
@@ -1296,7 +1301,7 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data *
                        if (ocsp_update_task)
                                task_wakeup(ocsp_update_task, TASK_WOKEN_MSG);
                }
-       } else if (iocsp->uri && data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) {
+       } else if (iocsp->uri && enable_auto_update) {
                /* This unlikely case can happen if a series of "del ssl
                 * crt-list" / "add ssl crt-list" commands are made on the CLI.
                 * In such a case, the OCSP response tree entry will be created
@@ -3843,12 +3848,11 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default,
                /* we found the ckchs in the tree, we can use it directly */
                 cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, is_default, &ckch_inst, err);
 
-                /* This certificate has an 'ocsp-update' already set in a
-                 * previous crt-list so we must raise an error. */
-                if (ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) {
-                        memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err: "", path);
-                        cfgerr |= ERR_ALERT | ERR_FATAL;
-                }
+                /* The ckch_store might have been created through a crt-list
+                 * line so we must check that the ocsp-update modes are still
+                 * compatible between the global mode and the explicit one from
+                 * the crt-list. */
+                cfgerr |= ocsp_update_check_cfg_consistency(ckchs, NULL, path, err);
 
                 found++;
        } else if (stat(path, &buf) == 0) {