You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2022/08/25 14:00:13 UTC

svn commit: r1903677 - in /httpd/httpd/trunk: changes-entries/ docs/manual/mod/ modules/md/ test/modules/md/

Author: icing
Date: Thu Aug 25 14:00:13 2022
New Revision: 1903677

URL: http://svn.apache.org/viewvc?rev=1903677&view=rev
Log:
mod_md v2.4.19 from github sync

  *) mod_md: a new directive `MDStoreLocks` can be used on cluster
     setups with a shared file system for `MDStoreDir` to order
     activation of renewed certificates when several cluster nodes are
     restarted at the same time. Store locks are not enabled by default.

     Restored curl_easy cleanup behaviour from v2.4.14 and refactored
     the use of curl_multi for OCSP requests to work with that.
     Fixes <https://github.com/icing/mod_md/issues/293>.


Added:
    httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
    httpd/httpd/trunk/test/modules/md/test_820_locks.py
Modified:
    httpd/httpd/trunk/docs/manual/mod/mod_md.xml
    httpd/httpd/trunk/modules/md/md_curl.c
    httpd/httpd/trunk/modules/md/md_http.c
    httpd/httpd/trunk/modules/md/md_http.h
    httpd/httpd/trunk/modules/md/md_log.h
    httpd/httpd/trunk/modules/md/md_reg.c
    httpd/httpd/trunk/modules/md/md_reg.h
    httpd/httpd/trunk/modules/md/md_store.c
    httpd/httpd/trunk/modules/md/md_store.h
    httpd/httpd/trunk/modules/md/md_store_fs.c
    httpd/httpd/trunk/modules/md/md_version.h
    httpd/httpd/trunk/modules/md/mod_md.c
    httpd/httpd/trunk/modules/md/mod_md_config.c
    httpd/httpd/trunk/modules/md/mod_md_config.h
    httpd/httpd/trunk/test/modules/md/conftest.py

Added: httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt?rev=1903677&view=auto
==============================================================================
--- httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt (added)
+++ httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt Thu Aug 25 14:00:13 2022
@@ -0,0 +1,8 @@
+  *) mod_md: a new directive `MDStoreLocks` can be used on cluster
+     setups with a shared file system for `MDStoreDir` to order
+     activation of renewed certificates when several cluster nodes are
+     restarted at the same time. Store locks are not enabled by default.
+
+     Restored curl_easy cleanup behaviour from v2.4.14 and refactored
+     the use of curl_multi for OCSP requests to work with that.
+     Fixes <https://github.com/icing/mod_md/issues/293>.

Modified: httpd/httpd/trunk/docs/manual/mod/mod_md.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_md.xml?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_md.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/mod_md.xml Thu Aug 25 14:00:13 2022
@@ -1405,7 +1405,7 @@ MDMessageCmd /etc/apache/md-message
         </usage>
     </directivesynopsis>
 
-        <directivesynopsis>
+    <directivesynopsis>
         <name>MDRetryFailover</name>
         <description></description>
         <syntax>MDRetryFailover <var>number</var></syntax>
@@ -1423,5 +1423,39 @@ MDMessageCmd /etc/apache/md-message
             </p>
         </usage>
     </directivesynopsis>
+
+    <directivesynopsis>
+        <name>MDStoreLocks</name>
+        <description></description>
+        <syntax>MDStoreLocks on|off|<var>duration</var></syntax>
+        <default>MDStoreLocks off</default>
+        <contextlist>
+            <context>server config</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.55 and later</compatibility>
+        <usage>
+            <p>
+                Enable this to use a lock file on server startup when
+                <directive>MDStoreDir</directive> is synchronized with the server
+                configuration and renewed certificates are activated.
+            </p><p>
+                Locking is intended for setups in a cluster that have a shared
+                file system for MDStoreDir. It will protect the activation of
+                renewed certificates when cluster nodes are restarted/reloaded
+                at the same time. Under the condition that the shared file
+                 system does support file locking.
+            </p><p>
+                The default duration to obtain the lock is 5 seconds. If the log
+                cannot be obtained, an error is logged and the server startup will
+                continue. This may result in a cluster node to still use the
+                previous certificate afterwards.
+            </p><p>
+                A higher timeout will reduce that likelihood, but may delay server
+                startups/reloads in case the locks are not properly handled in
+                the underlying file system. A lock should only be held by a
+                httpd instance for a short duration.
+            </p>
+        </usage>
+    </directivesynopsis>
 
 </modulesynopsis>

Modified: httpd/httpd/trunk/modules/md/md_curl.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_curl.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_curl.c (original)
+++ httpd/httpd/trunk/modules/md/md_curl.c Thu Aug 25 14:00:13 2022
@@ -253,18 +253,17 @@ static apr_status_t internals_setup(md_h
             rv = APR_EGENERAL;
             goto leave;
         }
+        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
+        curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
+        curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
+        curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
+        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
+        curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
     }
     else {
         md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, req->pool, "reusing curl instance from http");
     }
 
-    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
-    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
-    curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
-    curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
-
     internals = apr_pcalloc(req->pool, sizeof(*internals));
     internals->curl = curl;
         
@@ -442,33 +441,41 @@ static void add_to_curlm(md_http_request
 {
     md_curl_internals_t *internals = req->internals;
     
-    if (curlm && internals && internals->curlm == NULL) {
-        curl_multi_add_handle(curlm, internals->curl);
+    assert(curlm);
+    assert(internals);
+    if (internals->curlm == NULL) {
         internals->curlm = curlm;
     }
+    assert(internals->curlm == curlm);
+    curl_multi_add_handle(curlm, internals->curl);
 }
 
-static void remove_from_curlm(md_http_request_t *req, CURLM *curlm)
+static void remove_from_curlm_and_destroy(md_http_request_t *req, CURLM *curlm)
 {
     md_curl_internals_t *internals = req->internals;
 
-    if (curlm && internals && internals->curlm == curlm) {
-        curl_multi_remove_handle(curlm, internals->curl);
-        internals->curlm = NULL;
-    }
+    assert(curlm);
+    assert(internals);
+    assert(internals->curlm == curlm);
+    curl_multi_remove_handle(curlm, internals->curl);
+    internals->curlm = NULL;
+    md_http_req_destroy(req);
 }
     
 static apr_status_t md_curl_multi_perform(md_http_t *http, apr_pool_t *p,
                                           md_http_next_req *nextreq, void *baton)
 {
+    md_http_t *sub_http;
     md_http_request_t *req;
     CURLM *curlm = NULL;
     CURLMcode mc;
     struct CURLMsg *curlmsg;
+    apr_array_header_t *http_spares;
     apr_array_header_t *requests;
     int i, running, numfds, slowdown, msgcount;
     apr_status_t rv;
     
+    http_spares = apr_array_make(p, 10, sizeof(md_http_t*));
     requests = apr_array_make(p, 10, sizeof(md_http_request_t*));
     curlm = curl_multi_init();
     if (!curlm) {
@@ -481,35 +488,46 @@ static apr_status_t md_curl_multi_perfor
     while(1) {
         while (1) {
             /* fetch as many requests as nextreq gives us */
-            rv = nextreq(&req, baton, http, requests->nelts);
-            
-            if (APR_SUCCESS == rv) {
-                if (APR_SUCCESS != (rv = internals_setup(req))) {
-                    if (req->cb.on_status) req->cb.on_status(req, rv, req->cb.on_status_data);
-                    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+            if (http_spares->nelts > 0) {
+                sub_http = *(md_http_t **)(apr_array_pop(http_spares));
+            }
+            else {
+                rv = md_http_clone(&sub_http, p, http);
+                if (APR_SUCCESS != rv) {
+                    md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p,
                                   "multi_perform[%d reqs]: setup failed", requests->nelts);
+                    goto leave;
                 }
-                else {
-                    APR_ARRAY_PUSH(requests, md_http_request_t*) = req;
-                    add_to_curlm(req, curlm);
-                    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
-                                  "multi_perform[%d reqs]: added request", requests->nelts);
-                }
-                continue;
             }
-            else if (APR_STATUS_IS_ENOENT(rv)) {
-                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, 
+
+            rv = nextreq(&req, baton, sub_http, requests->nelts);
+            if (APR_STATUS_IS_ENOENT(rv)) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p,
                               "multi_perform[%d reqs]: no more requests", requests->nelts);
                 if (!requests->nelts) {
                     goto leave;
                 }
                 break;
             }
-            else {
-                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+            else if (APR_SUCCESS != rv) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p,
                               "multi_perform[%d reqs]: nextreq() failed", requests->nelts);
+                APR_ARRAY_PUSH(http_spares, md_http_t*) = sub_http;
                 goto leave;
             }
+
+            if (APR_SUCCESS != (rv = internals_setup(req))) {
+                if (req->cb.on_status) req->cb.on_status(req, rv, req->cb.on_status_data);
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p,
+                              "multi_perform[%d reqs]: setup failed", requests->nelts);
+                APR_ARRAY_PUSH(http_spares, md_http_t*) = sub_http;
+                goto leave;
+            }
+
+            APR_ARRAY_PUSH(requests, md_http_request_t*) = req;
+            add_to_curlm(req, curlm);
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p,
+                          "multi_perform[%d reqs]: added request", requests->nelts);
         }
     
         mc = curl_multi_perform(curlm, &running);
@@ -544,9 +562,10 @@ static apr_status_t md_curl_multi_perfor
                                   requests->nelts, req->id);
                     update_status(req);
                     fire_status(req, curl_status(curlmsg->data.result));
-                    remove_from_curlm(req, curlm);
                     md_array_remove(requests, req);
-                    md_http_req_destroy(req);
+                    sub_http = req->http;
+                    APR_ARRAY_PUSH(http_spares, md_http_t*) = sub_http;
+                    remove_from_curlm_and_destroy(req, curlm);
                 }
                 else {
                     md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
@@ -563,8 +582,9 @@ leave:
     for (i = 0; i < requests->nelts; ++i) {
         req = APR_ARRAY_IDX(requests, i, md_http_request_t*);
         fire_status(req, APR_SUCCESS);
-        remove_from_curlm(req, curlm);
-        md_http_req_destroy(req);
+        sub_http = req->http;
+        APR_ARRAY_PUSH(http_spares, md_http_t*) = sub_http;
+        remove_from_curlm_and_destroy(req, curlm);
     }
     if (curlm) curl_multi_cleanup(curlm);
     return rv;
@@ -585,7 +605,19 @@ static void md_curl_req_cleanup(md_http_
     md_curl_internals_t *internals = req->internals;
     if (internals) {
         if (internals->curl) {
-            curl_easy_cleanup(internals->curl);
+            CURL *curl = md_http_get_impl_data(req->http);
+            if (curl == internals->curl) {
+                /* NOP: we have this curl at the md_http_t already */
+            }
+            else if (!curl) {
+                /* no curl at the md_http_t yet, install this one */
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, req->pool, "register curl instance at http");
+                md_http_set_impl_data(req->http, internals->curl);
+            }
+            else {
+                /* There already is a curl at the md_http_t and it's not this one. */
+                curl_easy_cleanup(internals->curl);
+            }
         }
         if (internals->req_hdrs) curl_slist_free_all(internals->req_hdrs);
         req->internals = NULL;

Modified: httpd/httpd/trunk/modules/md/md_http.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_http.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_http.c (original)
+++ httpd/httpd/trunk/modules/md/md_http.c Thu Aug 25 14:00:13 2022
@@ -92,6 +92,25 @@ apr_status_t md_http_create(md_http_t **
     return APR_SUCCESS;
 }
 
+apr_status_t md_http_clone(md_http_t **phttp,
+                           apr_pool_t *p, md_http_t *source_http)
+{
+    apr_status_t rv;
+
+    rv = md_http_create(phttp, p, source_http->user_agent, source_http->proxy_url);
+    if (APR_SUCCESS == rv) {
+        (*phttp)->resp_limit = source_http->resp_limit;
+        (*phttp)->timeout = source_http->timeout;
+        if (source_http->unix_socket_path) {
+            (*phttp)->unix_socket_path = apr_pstrdup(p, source_http->unix_socket_path);
+        }
+        if (source_http->ca_file) {
+            (*phttp)->ca_file = apr_pstrdup(p, source_http->ca_file);
+        }
+    }
+    return rv;
+}
+
 void md_http_set_impl_data(md_http_t *http, void *data)
 {
     http->impl_data = data;
@@ -183,7 +202,6 @@ static apr_status_t req_set_body_data(md
         bbody = apr_brigade_create(req->pool, req->http->bucket_alloc);
         rv = apr_brigade_write(bbody, NULL, NULL, body->data, body->len);
         if (rv != APR_SUCCESS) {
-            md_http_req_destroy(req);
             return rv;
         }
     }
@@ -315,10 +333,16 @@ apr_status_t md_http_POSTd_create(md_htt
     apr_status_t rv;
     
     rv = req_create(&req, http, "POST", url, headers);
+    if (APR_SUCCESS != rv) goto cleanup;
+    rv = req_set_body_data(req, content_type, body);
+cleanup:
     if (APR_SUCCESS == rv) {
-        rv = req_set_body_data(req, content_type, body);
+        *preq = req;
+    }
+    else {
+        *preq = NULL;
+        if (req) md_http_req_destroy(req);
     }
-    *preq = (APR_SUCCESS == rv)? req : NULL;
     return rv;
 }
 

Modified: httpd/httpd/trunk/modules/md/md_http.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_http.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_http.h (original)
+++ httpd/httpd/trunk/modules/md/md_http.h Thu Aug 25 14:00:13 2022
@@ -88,6 +88,13 @@ apr_status_t md_http_create(md_http_t **
 void md_http_set_response_limit(md_http_t *http, apr_off_t resp_limit);
 
 /**
+ * Clone a http instance, inheriting all settings from source_http.
+ * The cloned instance is not tied in any way to the source.
+ */
+apr_status_t md_http_clone(md_http_t **phttp,
+                           apr_pool_t *p, md_http_t *source_http);
+
+/**
  * Set the timeout for the complete request. This needs to take everything from
  * DNS looksups, to conntects, to transfer of all data into account and should
  * be sufficiently large.

Modified: httpd/httpd/trunk/modules/md/md_log.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_log.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_log.h (original)
+++ httpd/httpd/trunk/modules/md/md_log.h Thu Aug 25 14:00:13 2022
@@ -38,6 +38,10 @@ typedef enum {
 
 #define MD_LOG_MARK     __FILE__,__LINE__
 
+#ifndef APLOGNO
+#define APLOGNO(n)              "AH" #n ": "
+#endif
+
 const char *md_log_level_name(md_log_level_t level);
 
 int md_log_is_level(apr_pool_t *p, md_log_level_t level);

Modified: httpd/httpd/trunk/modules/md/md_reg.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.c (original)
+++ httpd/httpd/trunk/modules/md/md_reg.c Thu Aug 25 14:00:13 2022
@@ -55,6 +55,8 @@ struct md_reg_t {
     void *notify_ctx;
     apr_time_t min_delay;
     int retry_failover;
+    int use_store_locks;
+    apr_time_t lock_wait_timeout;
 };
 
 /**************************************************************************************************/
@@ -83,7 +85,8 @@ static apr_status_t load_props(md_reg_t
 
 apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *store,
                            const char *proxy_url, const char *ca_file,
-                           apr_time_t min_delay, int retry_failover)
+                           apr_time_t min_delay, int retry_failover,
+                           int use_store_locks, apr_time_t lock_wait_timeout)
 {
     md_reg_t *reg;
     apr_status_t rv;
@@ -100,6 +103,8 @@ apr_status_t md_reg_create(md_reg_t **pr
                     apr_pstrdup(p, ca_file) : NULL;
     reg->min_delay = min_delay;
     reg->retry_failover = retry_failover;
+    reg->use_store_locks = use_store_locks;
+    reg->lock_wait_timeout = lock_wait_timeout;
 
     md_timeslice_create(&reg->renew_window, p, MD_TIME_LIFE_NORM, MD_TIME_RENEW_WINDOW_DEF); 
     md_timeslice_create(&reg->warn_window, p, MD_TIME_LIFE_NORM, MD_TIME_WARN_WINDOW_DEF); 
@@ -1235,6 +1240,52 @@ apr_status_t md_reg_load_staging(md_reg_
     return md_util_pool_vdo(run_load_staging, reg, p, md, env, result, NULL);
 }
 
+apr_status_t md_reg_load_stagings(md_reg_t *reg, apr_array_header_t *mds,
+                                  apr_table_t *env, apr_pool_t *p)
+{
+    apr_status_t rv = APR_SUCCESS;
+    md_t *md;
+    md_result_t *result;
+    int i;
+
+    for (i = 0; i < mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(mds, i, md_t *);
+        result = md_result_md_make(p, md->name);
+        rv = md_reg_load_staging(reg, md, env, result, p);
+        if (APR_SUCCESS == rv) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, p, APLOGNO(10068)
+                          "%s: staged set activated", md->name);
+        }
+        else if (!APR_STATUS_IS_ENOENT(rv)) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, APLOGNO(10069)
+                          "%s: error loading staged set", md->name);
+        }
+    }
+
+    return rv;
+}
+
+apr_status_t md_reg_lock_global(md_reg_t *reg, apr_pool_t *p)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (reg->use_store_locks) {
+        rv = md_store_lock_global(reg->store, p, reg->lock_wait_timeout);
+        if (APR_SUCCESS != rv) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
+                          "unable to acquire global store lock");
+        }
+    }
+    return rv;
+}
+
+void md_reg_unlock_global(md_reg_t *reg, apr_pool_t *p)
+{
+    if (reg->use_store_locks) {
+        md_store_unlock_global(reg->store, p);
+    }
+}
+
 apr_status_t md_reg_freeze_domains(md_reg_t *reg, apr_array_header_t *mds)
 {
     apr_status_t rv = APR_SUCCESS;

Modified: httpd/httpd/trunk/modules/md/md_reg.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.h (original)
+++ httpd/httpd/trunk/modules/md/md_reg.h Thu Aug 25 14:00:13 2022
@@ -34,10 +34,18 @@ typedef struct md_reg_t md_reg_t;
 
 /**
  * Create the MD registry, using the pool and store.
+ * @param preg on APR_SUCCESS, the create md_reg_t
+ * @param pm memory pool to use for creation
+ * @param store the store to base on
+ * @param proxy_url optional URL of a proxy to use for requests
+ * @param ca_file  optioinal CA trust anchor file to use
+ * @param min_delay minimum delay between renewal attempts for a domain
+ * @param retry_failover numer of failed renewals attempt to fail over to alternate ACME ca
  */
 apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, md_store_t *store,
                            const char *proxy_url, const char *ca_file,
-                           apr_time_t min_delay, int retry_failover);
+                           apr_time_t min_delay, int retry_failover,
+                           int use_store_locks, apr_time_t lock_wait_timeout);
 
 md_store_t *md_reg_store_get(md_reg_t *reg);
 
@@ -270,9 +278,36 @@ apr_status_t md_reg_renew(md_reg_t *reg,
 apr_status_t md_reg_load_staging(md_reg_t *reg, const md_t *md, struct apr_table_t *env, 
                                  struct md_result_t *result, apr_pool_t *p);
 
+/**
+ * Check given MDomains for new data in staging areas and, if it exists, load
+ * the new credentials. On encountering errors, leave the credentails as
+ * they are.
+ */
+apr_status_t md_reg_load_stagings(md_reg_t *reg, apr_array_header_t *mds,
+                                  apr_table_t *env, apr_pool_t *p);
+
 void md_reg_set_renew_window_default(md_reg_t *reg, md_timeslice_t *renew_window);
 void md_reg_set_warn_window_default(md_reg_t *reg, md_timeslice_t *warn_window);
 
 struct md_job_t *md_reg_job_make(md_reg_t *reg, const char *mdomain, apr_pool_t *p);
 
+/**
+ * Acquire a cooperative, global lock on registry modifications. Will
+ * do nothing if locking is not configured.
+ *
+ * This will only prevent other children/processes/cluster nodes from
+ * doing the same and does not protect individual store functions from
+ * being called without it.
+ * @param reg the registy
+ * @param p memory pool to use
+ * @param max_wait maximum time to wait in order to acquire
+ * @return APR_SUCCESS when lock was obtained
+ */
+apr_status_t md_reg_lock_global(md_reg_t *reg, apr_pool_t *p);
+
+/**
+ * Realease the global registry lock. Will do nothing if there is no lock.
+ */
+void md_reg_unlock_global(md_reg_t *reg, apr_pool_t *p);
+
 #endif /* mod_md_md_reg_h */

Modified: httpd/httpd/trunk/modules/md/md_store.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_store.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_store.c (original)
+++ httpd/httpd/trunk/modules/md/md_store.c Thu Aug 25 14:00:13 2022
@@ -374,3 +374,12 @@ apr_status_t md_store_md_iter(md_store_m
     return md_store_iter(insp_md, &ctx, store, p, group, pattern, MD_FN_MD, MD_SV_JSON);
 }
 
+apr_status_t md_store_lock_global(md_store_t *store, apr_pool_t *p, apr_time_t max_wait)
+{
+    return store->lock_global(store, p, max_wait);
+}
+
+void md_store_unlock_global(md_store_t *store, apr_pool_t *p)
+{
+    store->unlock_global(store, p);
+}

Modified: httpd/httpd/trunk/modules/md/md_store.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_store.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_store.h (original)
+++ httpd/httpd/trunk/modules/md/md_store.h Thu Aug 25 14:00:13 2022
@@ -204,7 +204,23 @@ apr_status_t md_store_iter_names(md_stor
 apr_time_t md_store_get_modified(md_store_t *store, md_store_group_t group,  
                                  const char *name, const char *aspect, apr_pool_t *p);
 
+/**
+ * Acquire a cooperative, global lock on store modifications.
 
+ * This will only prevent other children/processes/cluster nodes from
+ * doing the same and does not protect individual store functions from
+ * being called without it.
+ * @param store the store
+ * @param p memory pool to use
+ * @param max_wait maximum time to wait in order to acquire
+ * @return APR_SUCCESS when lock was obtained
+ */
+apr_status_t md_store_lock_global(md_store_t *store, apr_pool_t *p, apr_time_t max_wait);
+
+/**
+ * Realease the global store lock. Will do nothing if there is no lock.
+ */
+void md_store_unlock_global(md_store_t *store, apr_pool_t *p);
 
 /**************************************************************************************************/
 /* Storage handling utils */
@@ -303,6 +319,8 @@ typedef apr_time_t md_store_get_modified
 typedef apr_status_t md_store_remove_nms_cb(md_store_t *store, apr_pool_t *p, 
                                             apr_time_t modified, md_store_group_t group, 
                                             const char *name, const char *aspect);
+typedef apr_status_t md_store_lock_global_cb(md_store_t *store, apr_pool_t *p, apr_time_t max_wait);
+typedef void md_store_unlock_global_cb(md_store_t *store, apr_pool_t *p);
 
 struct md_store_t {
     md_store_save_cb *save;
@@ -317,6 +335,8 @@ struct md_store_t {
     md_store_is_newer_cb *is_newer;
     md_store_get_modified_cb *get_modified;
     md_store_remove_nms_cb *remove_nms;
+    md_store_lock_global_cb *lock_global;
+    md_store_unlock_global_cb *unlock_global;
 };
 
 

Modified: httpd/httpd/trunk/modules/md/md_store_fs.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_store_fs.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_store_fs.c (original)
+++ httpd/httpd/trunk/modules/md/md_store_fs.c Thu Aug 25 14:00:13 2022
@@ -39,6 +39,7 @@
 /* file system based implementation of md_store_t */
 
 #define MD_STORE_VERSION        3
+#define MD_FS_LOCK_NAME         "store.lock"
 
 typedef struct {
     apr_fileperms_t dir;
@@ -60,6 +61,8 @@ struct md_store_fs_t {
     
     int port_80;
     int port_443;
+
+    apr_file_t *global_lock;
 };
 
 #define FS_STORE(store)     (md_store_fs_t*)(((char*)store)-offsetof(md_store_fs_t, s))
@@ -101,6 +104,9 @@ static int fs_is_newer(md_store_t *store
 static apr_time_t fs_get_modified(md_store_t *store, md_store_group_t group,  
                                   const char *name, const char *aspect, apr_pool_t *p);
 
+static apr_status_t fs_lock_global(md_store_t *store, apr_pool_t *p, apr_time_t max_wait);
+static void fs_unlock_global(md_store_t *store, apr_pool_t *p);
+
 static apr_status_t init_store_file(md_store_fs_t *s_fs, const char *fname, 
                                     apr_pool_t *p, apr_pool_t *ptemp)
 {
@@ -296,7 +302,9 @@ apr_status_t md_store_fs_init(md_store_t
     s_fs->s.is_newer = fs_is_newer;
     s_fs->s.get_modified = fs_get_modified;
     s_fs->s.remove_nms = fs_remove_nms;
-    
+    s_fs->s.lock_global = fs_lock_global;
+    s_fs->s.unlock_global = fs_unlock_global;
+
     /* by default, everything is only readable by the current user */ 
     s_fs->def_perms.dir = MD_FPROT_D_UONLY;
     s_fs->def_perms.file = MD_FPROT_F_UONLY;
@@ -1094,3 +1102,67 @@ static apr_status_t fs_rename(md_store_t
     md_store_fs_t *s_fs = FS_STORE(store);
     return md_util_pool_vdo(pfs_rename, s_fs, p, group, from, to, NULL);
 }
+
+static apr_status_t fs_lock_global(md_store_t *store, apr_pool_t *p, apr_time_t max_wait)
+{
+    md_store_fs_t *s_fs = FS_STORE(store);
+    apr_status_t rv;
+    const char *lpath;
+    apr_time_t end;
+
+    if (s_fs->global_lock) {
+        rv = APR_EEXIST;
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "already locked globally");
+        goto cleanup;
+    }
+
+    rv = md_util_path_merge(&lpath, p, s_fs->base, MD_FS_LOCK_NAME, NULL);
+    if (APR_SUCCESS != rv) goto cleanup;
+    end = apr_time_now() + max_wait;
+
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, p,
+                  "acquire global lock: %s", lpath);
+    while (apr_time_now() < end) {
+        rv = apr_file_open(&s_fs->global_lock, lpath,
+                           (APR_FOPEN_WRITE|APR_FOPEN_CREATE),
+                           MD_FPROT_F_UALL_GREAD, p);
+        if (APR_SUCCESS != rv) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, p,
+                          "unable to create/open lock file: %s",
+                          lpath);
+            goto next_try;
+        }
+        rv = apr_file_lock(s_fs->global_lock,
+                           APR_FLOCK_EXCLUSIVE|APR_FLOCK_NONBLOCK);
+        if (APR_SUCCESS == rv) {
+            goto cleanup;
+        }
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, p,
+                      "unable to obtain lock on: %s",
+                      lpath);
+
+    next_try:
+        if (s_fs->global_lock) {
+            apr_file_close(s_fs->global_lock);
+            s_fs->global_lock = NULL;
+        }
+        apr_sleep(apr_time_from_msec(100));
+    }
+    rv = APR_EGENERAL;
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, p,
+                  "acquire global lock: %s", lpath);
+
+cleanup:
+    return rv;
+}
+
+static void fs_unlock_global(md_store_t *store, apr_pool_t *p)
+{
+    md_store_fs_t *s_fs = FS_STORE(store);
+
+    (void)p;
+    if (s_fs->global_lock) {
+        apr_file_close(s_fs->global_lock);
+        s_fs->global_lock = NULL;
+    }
+}

Modified: httpd/httpd/trunk/modules/md/md_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_version.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_version.h (original)
+++ httpd/httpd/trunk/modules/md/md_version.h Thu Aug 25 14:00:13 2022
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "2.4.17"
+#define MOD_MD_VERSION "2.4.19"
 
 /**
  * @macro
@@ -35,7 +35,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_MD_VERSION_NUM 0x020410
+#define MOD_MD_VERSION_NUM 0x020413
 
 #define MD_ACME_DEF_URL         "https://acme-v02.api.letsencrypt.org/directory"
 #define MD_TAILSCALE_DEF_URL    "file://localhost/var/run/tailscale/tailscaled.sock"

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Thu Aug 25 14:00:13 2022
@@ -713,27 +713,6 @@ static apr_status_t merge_mds_with_conf(
     return rv;
 }
 
-static void load_staged_data(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
-{
-    apr_status_t rv;
-    md_t *md;
-    md_result_t *result;
-    int i;
-
-    for (i = 0; i < mc->mds->nelts; ++i) {
-        md = APR_ARRAY_IDX(mc->mds, i, md_t *);
-        result = md_result_md_make(p, md->name);
-        if (APR_SUCCESS == (rv = md_reg_load_staging(mc->reg, md, mc->env, result, p))) {
-            ap_log_error( APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(10068)
-                         "%s: staged set activated", md->name);
-        }
-        else if (!APR_STATUS_IS_ENOENT(rv)) {
-            ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10069)
-                         "%s: error loading staged set", md->name);
-        }
-    }
-}
-
 static apr_status_t check_invalid_duplicates(server_rec *base_server)
 {
     server_rec *s;
@@ -891,7 +870,8 @@ static apr_status_t md_post_config_befor
     if (APR_SUCCESS != rv) goto leave;
 
     rv = md_reg_create(&mc->reg, p, store, mc->proxy_url, mc->ca_certs,
-                       mc->min_delay, mc->retry_failover);
+                       mc->min_delay, mc->retry_failover,
+                       mc->use_store_locks, mc->lock_wait_timeout);
     if (APR_SUCCESS != rv) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10072) "setup md registry");
         goto leave;
@@ -934,14 +914,24 @@ static apr_status_t md_post_config_befor
     /*3*/
     if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p))) goto leave;
     /*4*/
+    if (APR_SUCCESS != (rv = md_reg_lock_global(mc->reg, ptemp))) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10398)
+                     "unable to obtain global registry lock, "
+                     "renewed certificates may remain inactive on "
+                     "this httpd instance!");
+        /* FIXME: or should we fail the server start/reload here? */
+        rv = APR_SUCCESS;
+        goto leave;
+    }
     if (APR_SUCCESS != (rv = md_reg_sync_start(mc->reg, mc->mds, ptemp))) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10073)
                      "syncing %d mds to registry", mc->mds->nelts);
         goto leave;
     }
     /*5*/
-    load_staged_data(mc, s, p);
+    md_reg_load_stagings(mc->reg, mc->mds, mc->env, p);
 leave:
+    md_reg_unlock_global(mc->reg, ptemp);
     return rv;
 }
 

Modified: httpd/httpd/trunk/modules/md/mod_md_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.c?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.c Thu Aug 25 14:00:13 2022
@@ -86,6 +86,8 @@ static md_mod_conf_t defmc = {
     NULL,                      /* CA cert file to use */
     apr_time_from_sec(5),      /* minimum delay for retries */
     13,                        /* retry_failover after 14 errors, with 5s delay ~ half a day */
+    0,                         /* store locks, disabled by default */
+    apr_time_from_sec(5),      /* max time to wait to obaint a store lock */
 };
 
 static md_timeslice_t def_renew_window = {
@@ -647,6 +649,36 @@ static const char *md_config_set_retry_f
     return NULL;
 }
 
+static const char *md_config_set_store_locks(cmd_parms *cmd, void *dc, const char *s)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD);
+    int use_store_locks;
+    apr_time_t wait_time = 0;
+
+    (void)dc;
+    if (err) {
+        return err;
+    }
+    else if (!apr_strnatcasecmp("off", s)) {
+        use_store_locks = 0;
+    }
+    else if (!apr_strnatcasecmp("on", s)) {
+        use_store_locks = 1;
+    }
+    else {
+        if (md_duration_parse(&wait_time, s, "s") != APR_SUCCESS) {
+            return "neither 'on', 'off' or a duration specified";
+        }
+        use_store_locks = (wait_time != 0);
+    }
+    config->mc->use_store_locks = use_store_locks;
+    if (wait_time) {
+        config->mc->lock_wait_timeout = wait_time;
+    }
+    return NULL;
+}
+
 static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const char *value)
 {
     md_srv_conf_t *config = md_config_get(cmd->server);
@@ -1215,6 +1247,8 @@ const command_rec md_cmds[] = {
                   "Time length for first retry, doubled on every consecutive error."),
     AP_INIT_TAKE1("MDRetryFailover", md_config_set_retry_failover, NULL, RSRC_CONF,
                   "The number of errors before a failover to another CA is triggered."),
+    AP_INIT_TAKE1("MDStoreLocks", md_config_set_store_locks, NULL, RSRC_CONF,
+                  "Configure locking of store for updates."),
 
     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 };

Modified: httpd/httpd/trunk/modules/md/mod_md_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.h?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.h Thu Aug 25 14:00:13 2022
@@ -72,6 +72,8 @@ struct md_mod_conf_t {
     const char *ca_certs;              /* root certificates to use for connections */
     apr_time_t min_delay;              /* minimum delay for retries */
     int retry_failover;                /* number of errors to trigger CA failover */
+    int use_store_locks;               /* use locks when updating store */
+    apr_time_t lock_wait_timeout;      /* fail after this time when unable to obtain lock */
 };
 
 typedef struct md_srv_conf_t {

Modified: httpd/httpd/trunk/test/modules/md/conftest.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/md/conftest.py?rev=1903677&r1=1903676&r2=1903677&view=diff
==============================================================================
--- httpd/httpd/trunk/test/modules/md/conftest.py (original)
+++ httpd/httpd/trunk/test/modules/md/conftest.py Thu Aug 25 14:00:13 2022
@@ -51,6 +51,7 @@ def _session_scope(env):
         'AH10170',  # mod_md, wrong config, tested
         'AH10171',  # mod_md, wrong config, tested
         'AH10373',  # SSL errors on uncompleted handshakes
+        'AH10398',  # test on global store lock
     ])
 
     env.httpd_error_log.add_ignored_patterns([
@@ -61,6 +62,7 @@ def _session_scope(env):
         re.compile(r'.*problem\[urn:org:apache:httpd:log:AH\d+:].*'),
         re.compile(r'.*Unsuccessful in contacting ACME server at :*'),
         re.compile(r'.*test-md-720-002-\S+.org: dns-01 setup command failed .*'),
+        re.compile(r'.*AH\d*: unable to obtain global registry lock, .*'),
     ])
     if env.lacks_ocsp():
         env.httpd_error_log.add_ignored_patterns([

Added: httpd/httpd/trunk/test/modules/md/test_820_locks.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/md/test_820_locks.py?rev=1903677&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/md/test_820_locks.py (added)
+++ httpd/httpd/trunk/test/modules/md/test_820_locks.py Thu Aug 25 14:00:13 2022
@@ -0,0 +1,72 @@
+import os
+
+import pytest
+from filelock import Timeout, FileLock
+
+from .md_cert_util import MDCertUtil
+from .md_conf import MDConf
+from .md_env import MDTestEnv
+
+
+@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
+                    reason="no ACME test server configured")
+class TestLocks:
+
+    @pytest.fixture(autouse=True, scope='class')
+    def _class_scope(self, env, acme):
+        env.APACHE_CONF_SRC = "data/test_auto"
+        acme.start(config='default')
+        env.check_acme()
+        env.clear_store()
+
+    @pytest.fixture(autouse=True, scope='function')
+    def _method_scope(self, env, request):
+        env.clear_store()
+        self.test_domain = env.get_request_domain(request)
+
+    def configure_httpd(self, env, domains, add_lines=""):
+        conf = MDConf(env)
+        conf.add(add_lines)
+        conf.add_md(domains)
+        conf.add_vhost(domains)
+        conf.install()
+
+    # normal renewal with store locks activated
+    def test_md_820_001(self, env):
+        domain = self.test_domain
+        self.configure_httpd(env, [domain], add_lines=[
+            "MDStoreLocks 1s"
+        ])
+        assert env.apache_restart() == 0
+        assert env.await_completion([domain])
+
+    # renewal, with global lock held during restert
+    def test_md_820_002(self, env):
+        domain = self.test_domain
+        self.configure_httpd(env, [domain], add_lines=[
+            "MDStoreLocks 1s"
+        ])
+        assert env.apache_restart() == 0
+        assert env.await_completion([domain])
+        # we have a cert now, add a dns name to force renewal
+        certa = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
+        self.configure_httpd(env, [domain, f"x.{domain}"], add_lines=[
+            "MDStoreLocks 1s"
+        ])
+        assert env.apache_restart() == 0
+        # await new cert, but do not restart, keeps the cert in staging
+        assert env.await_completion([domain], restart=False)
+        # obtain global lock and restart
+        lockfile = os.path.join(env.store_dir, "store.lock")
+        with FileLock(lockfile):
+            assert env.apache_restart() == 0
+        # lock should have prevented staging from being activated,
+        # meaning we will have the same cert
+        certb = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
+        assert certa.same_serial_as(certb)
+        # now restart without lock
+        assert env.apache_restart() == 0
+        certc = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
+        assert not certa.same_serial_as(certc)
+
+



Re: svn commit: r1903677 - in /httpd/httpd/trunk: changes-entries/ docs/manual/mod/ modules/md/ test/modules/md/

Posted by Stefan Eissing via dev <de...@httpd.apache.org>.

> Am 09.01.2023 um 22:14 schrieb Christophe JAILLET <ch...@wanadoo.fr>:
> 
> Le 25/08/2022 à 16:00, icing@apache.org a écrit :
>> Author: icing
>> Date: Thu Aug 25 14:00:13 2022
>> New Revision: 1903677
>> URL: http://svn.apache.org/viewvc?rev=1903677&view=rev
>> Log:
>> mod_md v2.4.19 from github sync
>>   *) mod_md: a new directive `MDStoreLocks` can be used on cluster
>>      setups with a shared file system for `MDStoreDir` to order
>>      activation of renewed certificates when several cluster nodes are
>>      restarted at the same time. Store locks are not enabled by default.
>>      Restored curl_easy cleanup behaviour from v2.4.14 and refactored
>>      the use of curl_multi for OCSP requests to work with that.
>>      Fixes <https://github.com/icing/mod_md/issues/293>.
>> Added:
>>     httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
>>     httpd/httpd/trunk/test/modules/md/test_820_locks.py
>> Modified:
>>     httpd/httpd/trunk/docs/manual/mod/mod_md.xml
>>     httpd/httpd/trunk/modules/md/md_curl.c
>>     httpd/httpd/trunk/modules/md/md_http.c
>>     httpd/httpd/trunk/modules/md/md_http.h
>>     httpd/httpd/trunk/modules/md/md_log.h
>>     httpd/httpd/trunk/modules/md/md_reg.c
>>     httpd/httpd/trunk/modules/md/md_reg.h
>>     httpd/httpd/trunk/modules/md/md_store.c
>>     httpd/httpd/trunk/modules/md/md_store.h
>>     httpd/httpd/trunk/modules/md/md_store_fs.c
>>     httpd/httpd/trunk/modules/md/md_version.h
>>     httpd/httpd/trunk/modules/md/mod_md.c
>>     httpd/httpd/trunk/modules/md/mod_md_config.c
>>     httpd/httpd/trunk/modules/md/mod_md_config.h
>>     httpd/httpd/trunk/test/modules/md/conftest.py
>> Added: httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt?rev=1903677&view=auto
>> ==============================================================================
>> --- httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt (added)
>> +++ httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt Thu Aug 25 14:00:13 2022
>> @@ -0,0 +1,8 @@
>> +  *) mod_md: a new directive `MDStoreLocks` can be used on cluster
>> +     setups with a shared file system for `MDStoreDir` to order
>> +     activation of renewed certificates when several cluster nodes are
>> +     restarted at the same time. Store locks are not enabled by default.
>> +
>> +     Restored curl_easy cleanup behaviour from v2.4.14 and refactored
>> +     the use of curl_multi for OCSP requests to work with that.
>> +     Fixes <https://github.com/icing/mod_md/issues/293>.
>> Modified: httpd/httpd/trunk/docs/manual/mod/mod_md.xml
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_md.xml?rev=1903677&r1=1903676&r2=1903677&view=diff
>> ==============================================================================
>> --- httpd/httpd/trunk/docs/manual/mod/mod_md.xml (original)
>> +++ httpd/httpd/trunk/docs/manual/mod/mod_md.xml Thu Aug 25 14:00:13 2022
>> @@ -1405,7 +1405,7 @@ MDMessageCmd /etc/apache/md-message
>>          </usage>
>>      </directivesynopsis>
>>  -        <directivesynopsis>
>> +    <directivesynopsis>
>>          <name>MDRetryFailover</name>
>>          <description></description>
>>          <syntax>MDRetryFailover <var>number</var></syntax>
>> @@ -1423,5 +1423,39 @@ MDMessageCmd /etc/apache/md-message
>>              </p>
>>          </usage>
>>      </directivesynopsis>
>> +
>> +    <directivesynopsis>
>> +        <name>MDStoreLocks</name>
>> +        <description></description>
> 
> Hi,
> 
> a description is missing.
> Not sure how to write it myself.

ChatGPT?
-.-
> 
> CJ
> 
>> +        <syntax>MDStoreLocks on|off|<var>duration</var></syntax>
>> +        <default>MDStoreLocks off</default>
>> +        <contextlist>
>> +            <context>server config</context>
>> +        </contextlist>
>> +        <compatibility>Available in version 2.4.55 and later</compatibility>
>> +        <usage>
>> +            <p>
>> +                Enable this to use a lock file on server startup when
>> +                <directive>MDStoreDir</directive> is synchronized with the server
>> +                configuration and renewed certificates are activated.
>> +            </p><p>
>> +                Locking is intended for setups in a cluster that have a shared
>> +                file system for MDStoreDir. It will protect the activation of
>> +                renewed certificates when cluster nodes are restarted/reloaded
>> +                at the same time. Under the condition that the shared file
>> +                 system does support file locking.
>> +            </p><p>
>> +                The default duration to obtain the lock is 5 seconds. If the log
>> +                cannot be obtained, an error is logged and the server startup will
>> +                continue. This may result in a cluster node to still use the
>> +                previous certificate afterwards.
>> +            </p><p>
>> +                A higher timeout will reduce that likelihood, but may delay server
>> +                startups/reloads in case the locks are not properly handled in
>> +                the underlying file system. A lock should only be held by a
>> +                httpd instance for a short duration.
>> +            </p>
>> +        </usage>
>> +    </directivesynopsis>
> 


Re: svn commit: r1903677 - in /httpd/httpd/trunk: changes-entries/ docs/manual/mod/ modules/md/ test/modules/md/

Posted by Christophe JAILLET <ch...@wanadoo.fr>.
Le 25/08/2022 à 16:00, icing@apache.org a écrit :
> Author: icing
> Date: Thu Aug 25 14:00:13 2022
> New Revision: 1903677
> 
> URL: http://svn.apache.org/viewvc?rev=1903677&view=rev
> Log:
> mod_md v2.4.19 from github sync
> 
>    *) mod_md: a new directive `MDStoreLocks` can be used on cluster
>       setups with a shared file system for `MDStoreDir` to order
>       activation of renewed certificates when several cluster nodes are
>       restarted at the same time. Store locks are not enabled by default.
> 
>       Restored curl_easy cleanup behaviour from v2.4.14 and refactored
>       the use of curl_multi for OCSP requests to work with that.
>       Fixes <https://github.com/icing/mod_md/issues/293>.
> 
> 
> Added:
>      httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
>      httpd/httpd/trunk/test/modules/md/test_820_locks.py
> Modified:
>      httpd/httpd/trunk/docs/manual/mod/mod_md.xml
>      httpd/httpd/trunk/modules/md/md_curl.c
>      httpd/httpd/trunk/modules/md/md_http.c
>      httpd/httpd/trunk/modules/md/md_http.h
>      httpd/httpd/trunk/modules/md/md_log.h
>      httpd/httpd/trunk/modules/md/md_reg.c
>      httpd/httpd/trunk/modules/md/md_reg.h
>      httpd/httpd/trunk/modules/md/md_store.c
>      httpd/httpd/trunk/modules/md/md_store.h
>      httpd/httpd/trunk/modules/md/md_store_fs.c
>      httpd/httpd/trunk/modules/md/md_version.h
>      httpd/httpd/trunk/modules/md/mod_md.c
>      httpd/httpd/trunk/modules/md/mod_md_config.c
>      httpd/httpd/trunk/modules/md/mod_md_config.h
>      httpd/httpd/trunk/test/modules/md/conftest.py
> 
> Added: httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt?rev=1903677&view=auto
> ==============================================================================
> --- httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt (added)
> +++ httpd/httpd/trunk/changes-entries/md_locks_and_fix.txt Thu Aug 25 14:00:13 2022
> @@ -0,0 +1,8 @@
> +  *) mod_md: a new directive `MDStoreLocks` can be used on cluster
> +     setups with a shared file system for `MDStoreDir` to order
> +     activation of renewed certificates when several cluster nodes are
> +     restarted at the same time. Store locks are not enabled by default.
> +
> +     Restored curl_easy cleanup behaviour from v2.4.14 and refactored
> +     the use of curl_multi for OCSP requests to work with that.
> +     Fixes <https://github.com/icing/mod_md/issues/293>.
> 
> Modified: httpd/httpd/trunk/docs/manual/mod/mod_md.xml
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_md.xml?rev=1903677&r1=1903676&r2=1903677&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/docs/manual/mod/mod_md.xml (original)
> +++ httpd/httpd/trunk/docs/manual/mod/mod_md.xml Thu Aug 25 14:00:13 2022
> @@ -1405,7 +1405,7 @@ MDMessageCmd /etc/apache/md-message
>           </usage>
>       </directivesynopsis>
>   
> -        <directivesynopsis>
> +    <directivesynopsis>
>           <name>MDRetryFailover</name>
>           <description></description>
>           <syntax>MDRetryFailover <var>number</var></syntax>
> @@ -1423,5 +1423,39 @@ MDMessageCmd /etc/apache/md-message
>               </p>
>           </usage>
>       </directivesynopsis>
> +
> +    <directivesynopsis>
> +        <name>MDStoreLocks</name>
> +        <description></description>

Hi,

a description is missing.
Not sure how to write it myself.

CJ

> +        <syntax>MDStoreLocks on|off|<var>duration</var></syntax>
> +        <default>MDStoreLocks off</default>
> +        <contextlist>
> +            <context>server config</context>
> +        </contextlist>
> +        <compatibility>Available in version 2.4.55 and later</compatibility>
> +        <usage>
> +            <p>
> +                Enable this to use a lock file on server startup when
> +                <directive>MDStoreDir</directive> is synchronized with the server
> +                configuration and renewed certificates are activated.
> +            </p><p>
> +                Locking is intended for setups in a cluster that have a shared
> +                file system for MDStoreDir. It will protect the activation of
> +                renewed certificates when cluster nodes are restarted/reloaded
> +                at the same time. Under the condition that the shared file
> +                 system does support file locking.
> +            </p><p>
> +                The default duration to obtain the lock is 5 seconds. If the log
> +                cannot be obtained, an error is logged and the server startup will
> +                continue. This may result in a cluster node to still use the
> +                previous certificate afterwards.
> +            </p><p>
> +                A higher timeout will reduce that likelihood, but may delay server
> +                startups/reloads in case the locks are not properly handled in
> +                the underlying file system. A lock should only be held by a
> +                httpd instance for a short duration.
> +            </p>
> +        </usage>
> +    </directivesynopsis>