From: Remi Tricot-Le Breton Date: Mon, 25 Mar 2024 15:50:25 +0000 (+0100) Subject: MEDIUM: ssl: Add 'tune.ssl.ocsp-update.mode' global option X-Git-Tag: v3.0-dev7~71 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7359c0c7f4d367b4ee12944c35ea753e915edc19;p=thirdparty%2Fhaproxy.git MEDIUM: ssl: Add 'tune.ssl.ocsp-update.mode' global option 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). --- diff --git a/doc/configuration.txt b/doc/configuration.txt index adbdbfb2a1..3441af1ccd 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3959,6 +3959,15 @@ tune.ssl.ocsp-update.mindelay "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 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. diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index 2d8f8157e1..30e81e798b 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -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 }; diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 23b515d23e..0ba31d703f 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -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 ", 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 ", 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 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 }, }}; diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index 299ca925bf..add42b69eb 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -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; diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c index 1790c96ce6..4cc1fd88c8 100644 --- a/src/ssl_crtlist.c +++ b/src/ssl_crtlist.c @@ -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; diff --git a/src/ssl_ocsp.c b/src/ssl_ocsp.c index eb1d17fe65..1add6bd009 100644 --- a/src/ssl_ocsp.c +++ b/src/ssl_ocsp.c @@ -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; } diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 49565576c7..824bdaa721 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -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) {