From b5427c7395eaf6aac832bec3bfdfba7d3802f4b4 Mon Sep 17 00:00:00 2001 From: Rainer Jung Date: Thu, 4 Apr 2024 21:48:51 +0000 Subject: [PATCH 001/164] Fix pytest failure when using older nghttp2 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916804 13f79535-47bb-0310-9956-ffa450edef68 --- test/modules/http2/test_009_timing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/modules/http2/test_009_timing.py b/test/modules/http2/test_009_timing.py index 2c62bb0738b..2784f9ad35a 100644 --- a/test/modules/http2/test_009_timing.py +++ b/test/modules/http2/test_009_timing.py @@ -7,6 +7,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here") +@pytest.mark.skipif(not H2TestEnv().h2load_is_at_least('1.41.0'), reason="h2load misses --connect-to option") class TestTiming: LOGFILE = "" From 773a5f8f3499aa2848a584390d504b1c07b883c6 Mon Sep 17 00:00:00 2001 From: Rainer Jung Date: Thu, 4 Apr 2024 22:19:09 +0000 Subject: [PATCH 002/164] Fix failing pytest modules/http2/test_712_buffering.py:48. Do not count lines like "00:12:26.578220 <= Recv data, 0 bytes (0x0)" (which happen before the final close) as receiving a real chunk. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916806 13f79535-47bb-0310-9956-ffa450edef68 --- test/pyhttpd/curl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pyhttpd/curl.py b/test/pyhttpd/curl.py index d377a912387..7dcc25bcc80 100644 --- a/test/pyhttpd/curl.py +++ b/test/pyhttpd/curl.py @@ -112,7 +112,7 @@ def stutter_check(self, chunks: [str], stutter: datetime.timedelta): recv_times = [] for line in "".join(recv_err).split('\n'): m = re.match(r'^\s*(\d+:\d+:\d+(\.\d+)?) <= Recv data, (\d+) bytes.*', line) - if m: + if m and int(m.group(3)) > 0: recv_times.append(datetime.time.fromisoformat(m.group(1))) # received as many chunks as we sent assert len(chunks) == len(recv_times), "received response not in {0} chunks, but {1}".format( From 251d796a8d4b995ee5373c25d895604e4278114d Mon Sep 17 00:00:00 2001 From: Rainer Jung Date: Thu, 4 Apr 2024 22:46:59 +0000 Subject: [PATCH 003/164] Fix occasional pytest failures in modules/http2/test_800_websockets.py (test_h2_800_04_non_ws_resource and test_h2_800_09b_unsupported) due to additional RST messages. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916808 13f79535-47bb-0310-9956-ffa450edef68 --- test/modules/http2/test_800_websockets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/modules/http2/test_800_websockets.py b/test/modules/http2/test_800_websockets.py index 2e3d03a0e57..ef0e61673f7 100644 --- a/test/modules/http2/test_800_websockets.py +++ b/test/modules/http2/test_800_websockets.py @@ -174,7 +174,7 @@ def test_h2_800_03_not_found(self, env: H2TestEnv, ws_server): def test_h2_800_04_non_ws_resource(self, env: H2TestEnv, ws_server): r, infos, frames = ws_run(env, path='/alive.json') assert r.exit_code == 0, f'{r}' - assert infos == ['[1] :status: 502', '[1] EOF'], f'{r}' + assert infos == ['[1] :status: 502', '[1] EOF'] or infos == ['[1] :status: 502', '[1] EOF', '[1] RST'], f'{r}' assert frames == b'' # CONNECT to a URL path that sends a delayed HTTP response body @@ -214,7 +214,7 @@ def test_h2_800_09b_unsupported(self, env: H2TestEnv, ws_server): r, infos, frames = ws_run(env, path='/ws/echo/', authority=f'test1.{env.http_tld}:{env.http_port}') assert r.exit_code == 0, f'{r}' - assert infos == ['[1] :status: 501', '[1] EOF'], f'{r}' + assert infos == ['[1] :status: 501', '[1] EOF'] or infos == ['[1] :status: 501', '[1] EOF', '[1] RST'], f'{r}' # CONNECT and exchange a PING def test_h2_800_10_ws_ping(self, env: H2TestEnv, ws_server): From d3c4bf9ffe93e0a7dae2f1257782cdf087a68778 Mon Sep 17 00:00:00 2001 From: Rainer Jung Date: Sat, 6 Apr 2024 10:25:41 +0000 Subject: [PATCH 004/164] Fix occasional pytest failures in modules/http2/test_800_websockets.py (test_h2_800_03_not_found and test_h2_800_05_non_ws_delay_resource) due to additional RST messages. Maybe we should allow RST after EOF in all websocket tests? git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916830 13f79535-47bb-0310-9956-ffa450edef68 --- test/modules/http2/test_800_websockets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/modules/http2/test_800_websockets.py b/test/modules/http2/test_800_websockets.py index ef0e61673f7..14567675351 100644 --- a/test/modules/http2/test_800_websockets.py +++ b/test/modules/http2/test_800_websockets.py @@ -167,7 +167,7 @@ def test_h2_800_02_ws_empty(self, env: H2TestEnv, ws_server): def test_h2_800_03_not_found(self, env: H2TestEnv, ws_server): r, infos, frames = ws_run(env, path='/does-not-exist') assert r.exit_code == 0, f'{r}' - assert infos == ['[1] :status: 404', '[1] EOF'], f'{r}' + assert infos == ['[1] :status: 404', '[1] EOF'] or infos == ['[1] :status: 404', '[1] EOF', '[1] RST'], f'{r}' # CONNECT to a URL path that is a normal HTTP file resource # we do not want to receive the body of that @@ -182,7 +182,7 @@ def test_h2_800_04_non_ws_resource(self, env: H2TestEnv, ws_server): def test_h2_800_05_non_ws_delay_resource(self, env: H2TestEnv, ws_server): r, infos, frames = ws_run(env, path='/h2test/error?body_delay=100ms') assert r.exit_code == 0, f'{r}' - assert infos == ['[1] :status: 502', '[1] EOF'], f'{r}' + assert infos == ['[1] :status: 502', '[1] EOF'] or infos == ['[1] :status: 502', '[1] EOF', '[1] RST'], f'{r}' assert frames == b'' # CONNECT missing the sec-webSocket-version header From 8ffa19a1f7eb03b156e6bdbda65b3d2a2de9dfe8 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 8 Apr 2024 11:24:18 +0000 Subject: [PATCH 005/164] mod_md: update to v2.4.26 - Using OCSP stapling information to trigger certificate renewals. Proposed by @frasertweedale. - Added directive `MDCheckInterval` to control how often the server checks for detected revocations. Added proposals for configurations in the README.md chapter "Revocations". - OCSP stapling: accept OCSP responses without a `nextUpdate` entry which is allowed in RFC 6960. Treat those as having an update interval of 12 hours. Added by @frasertweedale. - Adapt OpenSSL usage to changes in their API. By Yann Ylavic. Test Updates - workarounds for using Pebble v2.5 - disable EAB tests for Pebble since v2.5 no longer supports HS256 FWT for EAB keys - some stability improvemnets in error/warning checks git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916861 13f79535-47bb-0310-9956-ffa450edef68 --- changes-entries/md_2.4.26.txt | 10 + docs/manual/mod/mod_md.xml | 25 +++ modules/md/md_ocsp.c | 15 +- modules/md/md_reg.c | 36 ++++ modules/md/md_reg.h | 7 + modules/md/md_version.h | 4 +- modules/md/mod_md_config.c | 22 ++- modules/md/mod_md_config.h | 1 + modules/md/mod_md_drive.c | 20 +- test/modules/md/conftest.py | 3 +- test/modules/md/md_cert_util.py | 4 +- test/modules/md/md_env.py | 15 +- test/modules/md/test_300_conf_validate.py | 31 ++- test/modules/md/test_310_conf_store.py | 220 +--------------------- test/modules/md/test_502_acmev2_drive.py | 10 - test/modules/md/test_602_roundtrip.py | 16 +- test/modules/md/test_750_eab.py | 21 ++- 17 files changed, 177 insertions(+), 283 deletions(-) create mode 100644 changes-entries/md_2.4.26.txt diff --git a/changes-entries/md_2.4.26.txt b/changes-entries/md_2.4.26.txt new file mode 100644 index 00000000000..9b82f611f18 --- /dev/null +++ b/changes-entries/md_2.4.26.txt @@ -0,0 +1,10 @@ + * mod_md: + - Using OCSP stapling information to trigger certificate renewals. Proposed + by @frasertweedale. + - Added directive `MDCheckInterval` to control how often the server checks + for detected revocations. Added proposals for configurations in the + README.md chapter "Revocations". + - OCSP stapling: accept OCSP responses without a `nextUpdate` entry which is + allowed in RFC 6960. Treat those as having an update interval of 12 hours. + Added by @frasertweedale. + - Adapt OpenSSL usage to changes in their API. By Yann Ylavic. diff --git a/docs/manual/mod/mod_md.xml b/docs/manual/mod/mod_md.xml index d4adac2114e..b136ca0d73a 100644 --- a/docs/manual/mod/mod_md.xml +++ b/docs/manual/mod/mod_md.xml @@ -1512,4 +1512,29 @@ MDMessageCmd /etc/apache/md-message + + MDCheckInterval + Determines how often certificates are checked + MDCheckInterval duration + MDCheckInterval 12h + + server config + + Available in version 2.4.60 and later + +

+ The time between certificate checks. By default, the validity + and need for renewals is checked twice a day. This interval is + not followed precisely. Instead the module randomly applies + a +/-50% jitter to it. With the default of 12 hours, this + means the actual time between runs varies between 6 and 18 + hours, jittered anew every run. This helps to mitigate + traffic peaks at ACME servers. +

+ The minimum duration you may configure is 1 second. It is + not recommended to use such short times in production. +

+
+
+ diff --git a/modules/md/md_ocsp.c b/modules/md/md_ocsp.c index c957c1d9cd3..8276137c3e8 100644 --- a/modules/md/md_ocsp.c +++ b/modules/md/md_ocsp.c @@ -678,12 +678,6 @@ static apr_status_t ostat_on_resp(const md_http_response_t *resp, void *baton) md_result_log(update->result, MD_LOG_DEBUG); goto cleanup; } - if (!bnextup) { - rv = APR_EINVAL; - md_result_set(update->result, rv, "OCSP basicresponse reports not valid dates"); - md_result_log(update->result, MD_LOG_DEBUG); - goto cleanup; - } /* Coming here, we have a response for our certid and it is either GOOD * or REVOKED. Both cases we want to remember and use in stapling. */ @@ -698,7 +692,14 @@ static apr_status_t ostat_on_resp(const md_http_response_t *resp, void *baton) new_der.free_data = md_openssl_free; nstat = (bstatus == V_OCSP_CERTSTATUS_GOOD)? MD_OCSP_CERT_ST_GOOD : MD_OCSP_CERT_ST_REVOKED; valid.start = bup? md_asn1_generalized_time_get(bup) : apr_time_now(); - valid.end = md_asn1_generalized_time_get(bnextup); + if (bnextup) { + valid.end = md_asn1_generalized_time_get(bnextup); + } + else { + /* nextUpdate not set; default to 12 hours. + * Refresh attempts will be started some time earlier. */ + valid.end = valid.start + apr_time_from_sec(MD_SECS_PER_DAY / 2); + } /* First, update the instance with a copy */ apr_thread_mutex_lock(ostat->reg->mutex); diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c index 8bceb0eb470..6aa7d788769 100644 --- a/modules/md/md_reg.c +++ b/modules/md/md_reg.c @@ -31,6 +31,7 @@ #include "md_json.h" #include "md_result.h" #include "md_reg.h" +#include "md_ocsp.h" #include "md_store.h" #include "md_status.h" #include "md_tailscale.h" @@ -1321,3 +1322,38 @@ md_job_t *md_reg_job_make(md_reg_t *reg, const char *mdomain, apr_pool_t *p) { return md_job_make(p, reg->store, MD_SG_STAGING, mdomain, reg->min_delay); } + +static int get_cert_count(const md_t *md) +{ + if (md->cert_files && md->cert_files->nelts) { + return md->cert_files->nelts; + } + return md_pkeys_spec_count(md->pks); +} + +int md_reg_has_revoked_certs(md_reg_t *reg, struct md_ocsp_reg_t *ocsp, + const md_t *md, apr_pool_t *p) +{ + const md_pubcert_t *pubcert; + const md_cert_t *cert; + md_timeperiod_t ocsp_valid; + md_ocsp_cert_stat_t cert_stat; + apr_status_t rv = APR_SUCCESS; + int i; + + if (!md->stapling || !ocsp) + return 0; + + for (i = 0; i < get_cert_count(md); ++i) { + if (APR_SUCCESS != md_reg_get_pubcert(&pubcert, reg, md, i, p)) + continue; + cert = APR_ARRAY_IDX(pubcert->certs, 0, const md_cert_t*); + if(!cert) + continue; + rv = md_ocsp_get_meta(&cert_stat, &ocsp_valid, ocsp, cert, p, md); + if (APR_SUCCESS == rv && cert_stat == MD_OCSP_CERT_ST_REVOKED) { + return 1; + } + } + return 0; +} diff --git a/modules/md/md_reg.h b/modules/md/md_reg.h index 58ee16ac62f..191b026e46a 100644 --- a/modules/md/md_reg.h +++ b/modules/md/md_reg.h @@ -23,6 +23,7 @@ struct md_pkey_t; struct md_cert_t; struct md_result_t; struct md_pkey_spec_t; +struct md_ocsp_reg_t; #include "md_store.h" @@ -310,4 +311,10 @@ apr_status_t md_reg_lock_global(md_reg_t *reg, apr_pool_t *p); */ void md_reg_unlock_global(md_reg_t *reg, apr_pool_t *p); +/** + * @return != 0 iff `md` has any certificates known to be REVOKED. + */ +int md_reg_has_revoked_certs(md_reg_t *reg, struct md_ocsp_reg_t *ocsp, + const md_t *md, apr_pool_t *p); + #endif /* mod_md_md_reg_h */ diff --git a/modules/md/md_version.h b/modules/md/md_version.h index 86a1821c7c8..cefbb8ded72 100644 --- a/modules/md/md_version.h +++ b/modules/md/md_version.h @@ -27,7 +27,7 @@ * @macro * Version number of the md module as c string */ -#define MOD_MD_VERSION "2.4.25" +#define MOD_MD_VERSION "2.4.26" /** * @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 0x020419 +#define MOD_MD_VERSION_NUM 0x02041a #define MD_ACME_DEF_URL "https://acme-v02.api.letsencrypt.org/directory" #define MD_TAILSCALE_DEF_URL "file://localhost/var/run/tailscale/tailscaled.sock" diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c index 31d06b4bc5b..cdd1e297c74 100644 --- a/modules/md/mod_md_config.c +++ b/modules/md/mod_md_config.c @@ -84,6 +84,7 @@ static md_mod_conf_t defmc = { "crt.sh", /* default cert checker site name */ "https://crt.sh?q=", /* default cert checker site url */ NULL, /* CA cert file to use */ + apr_time_from_sec(MD_SECS_PER_DAY/2), /* default time between cert checks */ 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 */ @@ -624,6 +625,24 @@ static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const cha return set_on_off(&config->mc->manage_base_server, value, cmd->pool); } +static const char *md_config_set_check_interval(cmd_parms *cmd, void *dc, const char *value) +{ + md_srv_conf_t *config = md_config_get(cmd->server); + const char *err = md_conf_check_location(cmd, MD_LOC_NOT_MD); + apr_time_t interval; + + (void)dc; + if (err) return err; + if (md_duration_parse(&interval, value, "s") != APR_SUCCESS) { + return "unrecognized duration format"; + } + if (interval < apr_time_from_sec(1)) { + return "check interval cannot be less than one second"; + } + config->mc->check_interval = interval; + return NULL; +} + static const char *md_config_set_min_delay(cmd_parms *cmd, void *dc, const char *value) { md_srv_conf_t *config = md_config_get(cmd->server); @@ -1304,7 +1323,8 @@ const command_rec md_cmds[] = { "Configure locking of store for updates."), AP_INIT_TAKE1("MDMatchNames", md_config_set_match_mode, NULL, RSRC_CONF, "Determines how DNS names are matched to vhosts."), - + AP_INIT_TAKE1("MDCheckInterval", md_config_set_check_interval, NULL, RSRC_CONF, + "Time between certificate checks."), AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) }; diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h index 7e87440ed18..1ce2375f00d 100644 --- a/modules/md/mod_md_config.h +++ b/modules/md/mod_md_config.h @@ -75,6 +75,7 @@ struct md_mod_conf_t { const char *cert_check_name; /* name of the linked certificate check site */ const char *cert_check_url; /* url "template for" checking a certificate */ const char *ca_certs; /* root certificates to use for connections */ + apr_time_t check_interval; /* duration between cert renewal checks */ 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 */ diff --git a/modules/md/mod_md_drive.c b/modules/md/mod_md_drive.c index 5565f44d758..f131f07b888 100644 --- a/modules/md/mod_md_drive.c +++ b/modules/md/mod_md_drive.c @@ -100,7 +100,7 @@ static void process_drive_job(md_renew_ctx_t *dctx, md_job_t *job, apr_pool_t *p } if (md_will_renew_cert(md)) { - /* Renew the MDs credentials in a STAGING area. Might be invoked repeatedly + /* Renew the MDs credentials in a STAGING area. Might be invoked repeatedly * without discarding previous/intermediate results. * Only returns SUCCESS when the renewal is complete, e.g. STAGING has a * complete set of new credentials. @@ -108,7 +108,12 @@ static void process_drive_job(md_renew_ctx_t *dctx, md_job_t *job, apr_pool_t *p ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10052) "md(%s): state=%d, driving", job->mdomain, md->state); - if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) { + if (md->stapling && dctx->mc->ocsp && + md_reg_has_revoked_certs(dctx->mc->reg, dctx->mc->ocsp, md, dctx->p)) { + ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO() + "md(%s): has revoked certificates", job->mdomain); + } + else if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) { ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10053) "md(%s): no need to renew", job->mdomain); goto expiry; @@ -180,10 +185,13 @@ int md_will_renew_cert(const md_t *md) return 1; } -static apr_time_t next_run_default(void) +static apr_time_t next_run_default(md_renew_ctx_t *dctx) { - /* we'd like to run at least twice a day by default */ - return apr_time_now() + apr_time_from_sec(MD_SECS_PER_DAY / 2); + unsigned char c; + apr_time_t delay = dctx->mc->check_interval; + + md_rand_bytes(&c, sizeof(c), dctx->p); + return apr_time_now() + delay + (delay * (c - 128) / 256); } static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp) @@ -211,7 +219,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp) * and we schedule ourself at the earliest of all. A job may specify 0 * as next_run to indicate that it wants to participate in the normal * regular runs. */ - next_run = next_run_default(); + next_run = next_run_default(dctx); for (i = 0; i < dctx->jobs->nelts; ++i) { job = APR_ARRAY_IDX(dctx->jobs, i, md_job_t *); diff --git a/test/modules/md/conftest.py b/test/modules/md/conftest.py index 192cd31a80b..0118de5e133 100755 --- a/test/modules/md/conftest.py +++ b/test/modules/md/conftest.py @@ -32,7 +32,8 @@ def env(pytestconfig) -> MDTestEnv: env.setup_httpd() env.apache_access_log_clear() env.httpd_error_log.clear_log() - return env + yield env + env.apache_stop() @pytest.fixture(autouse=True, scope="package") diff --git a/test/modules/md/md_cert_util.py b/test/modules/md/md_cert_util.py index 8cd99aa76f6..abcd36b938c 100755 --- a/test/modules/md/md_cert_util.py +++ b/test/modules/md/md_cert_util.py @@ -166,10 +166,10 @@ def get_key_length(self): def get_san_list(self): text = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, self.cert).decode("utf-8") - m = re.search(r"X509v3 Subject Alternative Name:\s*(.*)", text) + m = re.search(r"X509v3 Subject Alternative Name:(\s+critical)?\s*(.*)", text) sans_list = [] if m: - sans_list = m.group(1).split(",") + sans_list = m.group(2).split(",") def _strip_prefix(s): return s.split(":")[1] if s.strip().startswith("DNS:") else s.strip() diff --git a/test/modules/md/md_env.py b/test/modules/md/md_env.py index e8e36e5b1bc..193651948ad 100755 --- a/test/modules/md/md_env.py +++ b/test/modules/md/md_env.py @@ -73,7 +73,11 @@ def has_acme_server(cls): @classmethod def has_acme_eab(cls): - return cls.get_acme_server() == 'pebble' + return False + # Pebble, since v2.5.0 no longer supports HS256 for EAB, which + # is the only thing mod_md supports. Issue opened at pebble: + # https://github.com/letsencrypt/pebble/issues/455 + # return cls.get_acme_server() == 'pebble' @classmethod def is_pebble(cls) -> bool: @@ -356,13 +360,14 @@ def check_md_credentials(self, domain): MDCertUtil.validate_privkey(self.store_domain_file(domain, 'privkey.pem')) cert = MDCertUtil(self.store_domain_file(domain, 'pubcert.pem')) cert.validate_cert_matches_priv_key(self.store_domain_file(domain, 'privkey.pem')) - # check SANs and CN - assert cert.get_cn() == domain + # No longer check CN, it may not be set or is not trusted anyway + # assert cert.get_cn() == domain, f'CN: expected "{domain}", got {cert.get_cn()}' + # check SANs # compare lists twice in opposite directions: SAN may not respect ordering san_list = list(cert.get_san_list()) assert len(san_list) == len(domains) - assert set(san_list).issubset(domains) - assert set(domains).issubset(san_list) + assert set(san_list).issubset(domains), f'{san_list} not subset of {domains}' + assert set(domains).issubset(san_list), f'{domains} not subset of {san_list}' # check valid dates interval not_before = cert.get_not_before() not_after = cert.get_not_after() diff --git a/test/modules/md/test_300_conf_validate.py b/test/modules/md/test_300_conf_validate.py index f73bf67999d..88df1683413 100644 --- a/test/modules/md/test_300_conf_validate.py +++ b/test/modules/md/test_300_conf_validate.py @@ -15,7 +15,8 @@ class TestConf: @pytest.fixture(autouse=True, scope='class') - def _class_scope(self, env): + def _class_scope(self, env, acme): + acme.start(config='default') env.clear_store() # test case: just one MDomain definition @@ -413,7 +414,7 @@ def test_md_300_025(self, env, ca, url): def test_md_300_026(self, env): assert env.apache_stop() == 0 conf = MDConf(env) - domain = f"t300_026.{env.http_tld}" + domain = f"t300-026.{env.http_tld}" conf.add(f""" MDomain {domain} """) @@ -460,11 +461,12 @@ def test_md_300_027(self, env, cas, should_work): def test_md_300_028(self, env): assert env.apache_stop() == 0 conf = MDConf(env) - domaina = f"t300_028a.{env.http_tld}" - domainb = f"t300_028b.{env.http_tld}" - dalias = f"t300_028alias.{env.http_tld}" + domaina = f"t300-028a.{env.http_tld}" + domainb = f"t300-028b.{env.http_tld}" + dalias = f"t300-028alias.{env.http_tld}" conf.add_vhost(port=env.http_port, domains=[domaina, domainb, dalias], with_ssl=False) conf.add(f""" + MDMembers manual MDomain {domaina} MDomain {domainb} {dalias} """) @@ -481,23 +483,28 @@ def test_md_300_028(self, env): """) conf.install() - # This does not work as we have both MDs match domaina's vhost + # This does not work as we have both MDs match domain's vhost assert env.apache_fail() == 0 env.httpd_error_log.ignore_recent( - lognos = [ - "AH10238" # 2 MDs match the same vhost + lognos=[ + "AH10238", # 2 MDs match the same vhost ] ) # It works, if we only match on ServerNames conf.add("MDMatchNames servernames") conf.install() assert env.apache_restart() == 0 + env.httpd_error_log.ignore_recent( + lognos=[ + "AH10040", # ServerAlias not covered + ] + ) # wildcard and specfic MD overlaps def test_md_300_029(self, env): assert env.apache_stop() == 0 conf = MDConf(env) - domain = f"t300_029.{env.http_tld}" + domain = f"t300-029.{env.http_tld}" subdomain = f"sub.{domain}" conf.add_vhost(port=env.http_port, domains=[domain, subdomain], with_ssl=False) conf.add(f""" @@ -531,4 +538,10 @@ def test_md_300_029(self, env): conf.add("MDMatchNames servernames") conf.install() assert env.apache_restart() == 0 + time.sleep(2) + assert env.apache_stop() == 0 + # we need dns-01 challenge for the wildcard, which is not configured + env.httpd_error_log.ignore_recent(matches=[ + r'.*None of offered challenge types.*are supported.*' + ]) diff --git a/test/modules/md/test_310_conf_store.py b/test/modules/md/test_310_conf_store.py index d56790bb1fb..f2bb9c723ac 100644 --- a/test/modules/md/test_310_conf_store.py +++ b/test/modules/md/test_310_conf_store.py @@ -48,11 +48,6 @@ def test_md_310_100(self, env, confline, dns_lists, md_count): assert env.apache_restart() == 0 for i in range(0, len(dns_lists)): env.check_md(dns_lists[i], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add managed domains as separate steps def test_md_310_101(self, env): @@ -68,11 +63,6 @@ def test_md_310_101(self, env): assert env.apache_restart() == 0 env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) env.check_md(["testdomain2.org", "www.testdomain2.org", "mail.testdomain2.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add dns to existing md def test_md_310_102(self, env): @@ -82,11 +72,6 @@ def test_md_310_102(self, env): """).install() assert env.apache_restart() == 0 env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add new md definition with acme url, acme protocol, acme agreement def test_md_310_103(self, env): @@ -102,11 +87,6 @@ def test_md_310_103(self, env): env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, ca="http://acme.test.org:4000/directory", protocol="ACME", agreement="http://acme.test.org:4000/terms/v1") - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add to existing md: acme url, acme protocol def test_md_310_104(self, env): @@ -128,11 +108,6 @@ def test_md_310_104(self, env): env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, ca="http://acme.test.org:4000/directory", protocol="ACME", agreement="http://acme.test.org:4000/terms/v1") - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add new md definition with server admin def test_md_310_105(self, env): @@ -143,11 +118,6 @@ def test_md_310_105(self, env): name = "testdomain.org" env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, contacts=["mailto:admin@testdomain.org"]) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: add to existing md: server admin def test_md_310_106(self, env): @@ -159,11 +129,6 @@ def test_md_310_106(self, env): assert env.apache_restart() == 0 env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, contacts=["mailto:admin@testdomain.org"]) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: assign separate contact info based on VirtualHost def test_md_310_107(self, env): @@ -196,11 +161,6 @@ def test_md_310_108(self, env): """).install() assert env.apache_restart() == 0 env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: default drive mode - auto def test_md_310_109(self, env): @@ -209,11 +169,6 @@ def test_md_310_109(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1 - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: drive mode manual def test_md_310_110(self, env): @@ -223,11 +178,6 @@ def test_md_310_110(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 0 - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: drive mode auto def test_md_310_111(self, env): @@ -237,11 +187,6 @@ def test_md_310_111(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1 - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: drive mode always def test_md_310_112(self, env): @@ -260,11 +205,6 @@ def test_md_310_113a(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-window'] == '14d' - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: renew window - 10 percent def test_md_310_113b(self, env): @@ -274,12 +214,7 @@ def test_md_310_113b(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-window'] == '10%' - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) - + # test case: ca challenge type - http-01 def test_md_310_114(self, env): MDConf(env, text=""" @@ -288,11 +223,6 @@ def test_md_310_114(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01'] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: ca challenge type - http-01 def test_md_310_115(self, env): @@ -302,11 +232,6 @@ def test_md_310_115(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['tls-alpn-01'] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: ca challenge type - all def test_md_310_116(self, env): @@ -316,11 +241,6 @@ def test_md_310_116(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01', 'tls-alpn-01'] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: automatically collect md names from vhost config def test_md_310_117(self, env): @@ -349,11 +269,6 @@ def test_md_310_118(self, env): assert env.apache_restart() == 0 stat = env.get_md_status("testdomain.org") assert stat['renew-window'] == '14d' - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: set RSA key length 2048 def test_md_310_119(self, env): @@ -366,11 +281,6 @@ def test_md_310_119(self, env): "type": "RSA", "bits": 2048 } - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: set RSA key length 4096 def test_md_310_120(self, env): @@ -383,11 +293,6 @@ def test_md_310_120(self, env): "type": "RSA", "bits": 4096 } - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: require HTTPS def test_md_310_121(self, env): @@ -397,12 +302,6 @@ def test_md_310_121(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['require-https'] == "temporary" - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045", # No VirtualHost matches Managed Domain - "AH10105" # no domain match - ] - ) # test case: require OCSP stapling def test_md_310_122(self, env): @@ -412,11 +311,6 @@ def test_md_310_122(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['must-staple'] is True - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove managed domain from config def test_md_310_200(self, env): @@ -440,11 +334,6 @@ def test_md_310_201(self, env): assert env.apache_restart() == 0 # check: DNS has been removed from md in store env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove primary name from managed domain def test_md_310_202(self, env): @@ -458,11 +347,6 @@ def test_md_310_202(self, env): # check: md overwrite previous name and changes name env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], md="testdomain.org", state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove one md, keep another def test_md_310_203(self, env): @@ -479,11 +363,6 @@ def test_md_310_203(self, env): # all mds stay in store env.check_md(dns_list1, state=1) env.check_md(dns_list2, state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove ca info from md, should switch over to new defaults def test_md_310_204(self, env): @@ -503,11 +382,6 @@ def test_md_310_204(self, env): assert env.apache_restart() == 0 env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, ca="https://acme-v02.api.letsencrypt.org/directory", protocol="ACME") - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove server admin from md def test_md_310_205(self, env): @@ -524,11 +398,6 @@ def test_md_310_205(self, env): # check: md stays the same with previous admin info env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, contacts=["mailto:admin@testdomain.org"]) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove renew window from conf -> fallback to default def test_md_310_206(self, env): @@ -544,11 +413,6 @@ def test_md_310_206(self, env): assert env.apache_restart() == 0 # check: renew window not set assert env.a2md(["list"]).json['output'][0]['renew-window'] == '33%' - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove drive mode from conf -> fallback to default (auto) @pytest.mark.parametrize("renew_mode,exp_code", [ @@ -569,11 +433,6 @@ def test_md_310_207(self, env, renew_mode, exp_code): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1 - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: remove challenges from conf -> fallback to default (not set) def test_md_310_208(self, env): @@ -589,11 +448,6 @@ def test_md_310_208(self, env): """).install() assert env.apache_restart() == 0 assert 'challenges' not in env.a2md(["list"]).json['output'][0]['ca'] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: specify RSA key @pytest.mark.parametrize("key_size", ["2048", "4096"]) @@ -610,11 +464,6 @@ def test_md_310_209(self, env, key_size): """).install() assert env.apache_restart() == 0 assert "privkey" not in env.a2md(["list"]).json['output'][0] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: require HTTPS @pytest.mark.parametrize("mode", ["temporary", "permanent"]) @@ -635,12 +484,6 @@ def test_md_310_210(self, env, mode): assert env.apache_restart() == 0 assert "require-https" not in env.a2md(["list"]).json['output'][0], \ "HTTPS require still persisted in store. config: {}".format(mode) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045", # No VirtualHost matches Managed Domain - "AH10105", # MDomain does not match any vhost - ] - ) # test case: require OCSP stapling def test_md_310_211(self, env): @@ -656,11 +499,6 @@ def test_md_310_211(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['must-staple'] is False - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: reorder DNS names in md definition def test_md_310_300(self, env): @@ -673,11 +511,6 @@ def test_md_310_300(self, env): assert env.apache_restart() == 0 # check: dns list changes env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: move DNS from one md to another def test_md_310_301(self, env): @@ -693,11 +526,6 @@ def test_md_310_301(self, env): assert env.apache_restart() == 0 env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) env.check_md(["testdomain2.org", "www.testdomain2.org", "mail.testdomain2.org"], state=1) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change ca info def test_md_310_302(self, env): @@ -724,11 +552,6 @@ def test_md_310_302(self, env): env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, ca="http://somewhere.com:6666/directory", protocol="ACME", agreement="http://somewhere.com:6666/terms/v1") - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change server admin def test_md_310_303(self, env): @@ -749,11 +572,6 @@ def test_md_310_303(self, env): # check: md stays the same with previous admin info env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1, contacts=["mailto:webmaster@testdomain.org"]) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change drive mode - manual -> auto -> always def test_md_310_304(self, env): @@ -777,11 +595,6 @@ def test_md_310_304(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 2 - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change config value for renew window, use various syntax alternatives def test_md_310_305(self, env): @@ -806,11 +619,6 @@ def test_md_310_305(self, env): assert env.apache_restart() == 0 md = env.a2md(["list"]).json['output'][0] assert md['renew-window'] == '10%' - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change challenge types - http -> tls-sni -> all def test_md_310_306(self, env): @@ -834,11 +642,6 @@ def test_md_310_306(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01', 'tls-alpn-01'] - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: RSA key length: 4096 -> 2048 -> 4096 def test_md_310_307(self, env): @@ -869,11 +672,6 @@ def test_md_310_307(self, env): "type": "RSA", "bits": 4096 } - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change HTTPS require settings on existing md def test_md_310_308(self, env): @@ -899,12 +697,6 @@ def test_md_310_308(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['require-https'] == "permanent" - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045", # No VirtualHost matches Managed Domain - "AH10105", # MDomain matches no vhost - ] - ) # test case: change OCSP stapling settings on existing md def test_md_310_309(self, env): @@ -928,11 +720,6 @@ def test_md_310_309(self, env): """).install() assert env.apache_restart() == 0 assert env.a2md(["list"]).json['output'][0]['must-staple'] is False - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: change renew window parameter @pytest.mark.parametrize("window", [ @@ -1005,11 +792,6 @@ def test_md_310_500(self, env): env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1) env.clear_store() env.set_store_dir_default() - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test case: place an unexpected file into the store, check startup survival, see #218 def test_md_310_501(self, env): diff --git a/test/modules/md/test_502_acmev2_drive.py b/test/modules/md/test_502_acmev2_drive.py index a98e4ad97c7..eb754f25eff 100644 --- a/test/modules/md/test_502_acmev2_drive.py +++ b/test/modules/md/test_502_acmev2_drive.py @@ -436,11 +436,6 @@ def test_md_502_201(self, env, renew_window, test_data_list): md = env.a2md(["list", name]).json['output'][0] assert md["renew"] == tc["renew"], \ "Expected renew == {} indicator in {}, test case {}".format(tc["renew"], md, tc) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) @pytest.mark.parametrize("key_type,key_params,exp_key_length", [ ("RSA", [2048], 2048), @@ -467,11 +462,6 @@ def test_md_502_202(self, env, key_type, key_params, exp_key_length): # check cert key length cert = MDCertUtil(env.store_domain_file(name, 'pubcert.pem')) assert cert.get_key_length() == exp_key_length - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # test_502_203 removed, as ToS agreement is not really checked in ACMEv2 diff --git a/test/modules/md/test_602_roundtrip.py b/test/modules/md/test_602_roundtrip.py index e2e74c7d81b..9ff87e5df7e 100644 --- a/test/modules/md/test_602_roundtrip.py +++ b/test/modules/md/test_602_roundtrip.py @@ -52,13 +52,9 @@ def test_md_602_000(self, env): # check: SSL is running OK cert = env.get_cert(domain) assert domain in cert.get_san_list() + # check file system permissions: env.check_file_permissions(domain) - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) def test_md_602_001(self, env): # test case: same as test_600_000, but with two parallel managed domains @@ -97,11 +93,6 @@ def test_md_602_001(self, env): assert domains_a == cert_a.get_san_list() cert_b = env.get_cert(domain_b) assert domains_b == cert_b.get_san_list() - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) def test_md_602_002(self, env): # test case: one md, that covers two vhosts @@ -143,11 +134,6 @@ def test_md_602_002(self, env): assert cert_a.same_serial_as(cert_b) assert env.get_content(name_a, "/name.txt") == name_a assert env.get_content(name_b, "/name.txt") == name_b - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10045" # No VirtualHost matches Managed Domain - ] - ) # --------- _utils_ --------- diff --git a/test/modules/md/test_750_eab.py b/test/modules/md/test_750_eab.py index 7d81917829e..aec7e89b8c2 100644 --- a/test/modules/md/test_750_eab.py +++ b/test/modules/md/test_750_eab.py @@ -82,14 +82,17 @@ def test_md_750_003(self, env): assert env.apache_restart() == 0 md = env.await_error(domain) assert md['renewal']['errors'] > 0 - assert md['renewal']['last']['problem'] == 'urn:ietf:params:acme:error:unauthorized' + assert md['renewal']['last']['problem'] in [ + 'urn:ietf:params:acme:error:unauthorized', + 'urn:ietf:params:acme:error:malformed', + ] # env.httpd_error_log.ignore_recent( lognos = [ "AH10056" # the field 'kid' references a key that is not known to the ACME server ], matches = [ - r'.*urn:ietf:params:acme:error:unauthorized.*' + r'.*urn:ietf:params:acme:error:(unauthorized|malformed).*' ] ) @@ -105,14 +108,17 @@ def test_md_750_004(self, env): assert env.apache_restart() == 0 md = env.await_error(domain) assert md['renewal']['errors'] > 0 - assert md['renewal']['last']['problem'] == 'urn:ietf:params:acme:error:unauthorized' + assert md['renewal']['last']['problem'] in [ + 'urn:ietf:params:acme:error:unauthorized', + 'urn:ietf:params:acme:error:malformed', + ] # env.httpd_error_log.ignore_recent( lognos = [ "AH10056" # the field 'kid' references a key that is not known to the ACME server ], matches = [ - r'.*urn:ietf:params:acme:error:unauthorized.*' + r'.*urn:ietf:params:acme:error:(unauthorized|malformed).*' ] ) @@ -128,14 +134,17 @@ def test_md_750_005(self, env): assert env.apache_restart() == 0 md = env.await_error(domain) assert md['renewal']['errors'] > 0 - assert md['renewal']['last']['problem'] == 'urn:ietf:params:acme:error:unauthorized' + assert md['renewal']['last']['problem'] in [ + 'urn:ietf:params:acme:error:unauthorized', + 'urn:ietf:params:acme:error:malformed', + ] # env.httpd_error_log.ignore_recent( lognos = [ "AH10056" # external account binding JWS verification error: square/go-jose: error in cryptographic primitive ], matches = [ - r'.*urn:ietf:params:acme:error:unauthorized.*' + r'.*urn:ietf:params:acme:error:(unauthorized|malformed).*' ] ) From dee1eb37d787d34cb37df7eab535240e1774293a Mon Sep 17 00:00:00 2001 From: Ruediger Pluem Date: Mon, 8 Apr 2024 13:18:28 +0000 Subject: [PATCH 006/164] * Ensure that we set the default DH parameters for the key Replace else with an if as the if branch no longer ensures that custome DH parameters have been loaded. This fixes a regression that causes the default DH parameters for a key no longer set and thus effectively disabling DH ciphers when no explicit DH parameters are set. PR: 68863 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916863 13f79535-47bb-0310-9956-ffa450edef68 --- changes-entries/pr68863.txt | 3 +++ modules/ssl/ssl_engine_init.c | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 changes-entries/pr68863.txt diff --git a/changes-entries/pr68863.txt b/changes-entries/pr68863.txt new file mode 100644 index 00000000000..d45ffc708cc --- /dev/null +++ b/changes-entries/pr68863.txt @@ -0,0 +1,3 @@ + *) mod_ssl: Fix a regression that causes the default DH parameters for a key + no longer set and thus effectively disabling DH ciphers when no explicit + DH parameters are set. PR 68863 [Ruediger Pluem] diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index 64e4aaf1dcd..f657026d137 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1416,6 +1416,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s, const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile; int i; EVP_PKEY *pkey; + int custom_dh_done = 0; #ifdef HAVE_ECC EC_GROUP *ecgroup = NULL; int curve_nid = 0; @@ -1591,14 +1592,14 @@ static apr_status_t ssl_init_server_certs(server_rec *s, */ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *); if (certfile && !modssl_is_engine_id(certfile)) { - int done = 0, num_bits = 0; + int num_bits = 0; #if OPENSSL_VERSION_NUMBER < 0x30000000L DH *dh = modssl_dh_from_file(certfile); if (dh) { num_bits = DH_bits(dh); SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dh); DH_free(dh); - done = 1; + custom_dh_done = 1; } #else pkey = modssl_dh_pkey_from_file(certfile); @@ -1608,18 +1609,18 @@ static apr_status_t ssl_init_server_certs(server_rec *s, EVP_PKEY_free(pkey); } else { - done = 1; + custom_dh_done = 1; } } #endif - if (done) { + if (custom_dh_done) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540) "Custom DH parameters (%d bits) for %s loaded from %s", num_bits, vhost_id, certfile); } } #if !MODSSL_USE_OPENSSL_PRE_1_1_API - else { + if (!custom_dh_done) { /* If no parameter is manually configured, enable auto * selection. */ SSL_CTX_set_dh_auto(mctx->ssl_ctx, 1); From 2b2f6a15ac39af23074303673bbec901c5ffc154 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Thu, 11 Apr 2024 06:53:31 +0000 Subject: [PATCH 007/164] * modules/md: Fill in APLOGNO. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916907 13f79535-47bb-0310-9956-ffa450edef68 --- docs/log-message-tags/next-number | 2 +- modules/md/mod_md_drive.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 705464ad7a3..b35999b4fca 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -10500 +10501 diff --git a/modules/md/mod_md_drive.c b/modules/md/mod_md_drive.c index f131f07b888..d2655b8a0c8 100644 --- a/modules/md/mod_md_drive.c +++ b/modules/md/mod_md_drive.c @@ -110,7 +110,7 @@ static void process_drive_job(md_renew_ctx_t *dctx, md_job_t *job, apr_pool_t *p if (md->stapling && dctx->mc->ocsp && md_reg_has_revoked_certs(dctx->mc->reg, dctx->mc->ocsp, md, dctx->p)) { - ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO() + ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10500) "md(%s): has revoked certificates", job->mdomain); } else if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) { From a9a10f36a996c8e509bc371f0f8b17cb3feaeb4d Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 12 Apr 2024 09:56:34 +0000 Subject: [PATCH 008/164] mod_ssl_ct: Fix format warnings. * modules/ssl/mod_ssl_ct.c(client_extension_add_callback, server_extension_add_callback): Variable ext_type is unsigned, so use %u instead of %hu. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916924 13f79535-47bb-0310-9956-ffa450edef68 --- modules/ssl/mod_ssl_ct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ssl/mod_ssl_ct.c b/modules/ssl/mod_ssl_ct.c index 769adba795d..06c1aeecffb 100644 --- a/modules/ssl/mod_ssl_ct.c +++ b/modules/ssl/mod_ssl_ct.c @@ -2048,7 +2048,7 @@ static int client_extension_add_callback(SSL *ssl, unsigned ext_type, ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "client_extension_add_callback called, " - "ext %hu will be in ClientHello", + "ext %u will be in ClientHello", ext_type); return 1; @@ -2330,7 +2330,7 @@ static int server_extension_add_callback(SSL *ssl, unsigned ext_type, ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "server_extension_add_callback called, " - "ext %hu will be in ServerHello", + "ext %u will be in ServerHello", ext_type); rv = read_scts(c->pool, fingerprint, From f9f742e7ba347ba207aaf23c4676e833a5cc8f1f Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 12 Apr 2024 10:35:10 +0000 Subject: [PATCH 009/164] mpm_event: Simplify pollset "good methods" vs APR_POLLSET_WAKEABLE. * server/mpm/event/event.c(setup_threads_runtime): Simplify pollset creation code. All pollset "good methods" implement APR_POLLSET_WAKEABLE and wake-ability is quite important for MPM event's correctness anyway so simplify code around pollset creation so as not to suggest that APR_POLLSET_NODEFAULT if favored against APR_POLLSET_WAKEABLE. While at it account for the wakeup pipe in the pollset_size since not all pollset methods seem to do it internally in APR. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916925 13f79535-47bb-0310-9956-ffa450edef68 --- server/mpm/event/event.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 3841142c603..372b94f61b4 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -2630,29 +2630,27 @@ static void setup_threads_runtime(void) /* Create the main pollset */ pollset_flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY | - APR_POLLSET_NODEFAULT | APR_POLLSET_WAKEABLE; + APR_POLLSET_WAKEABLE | APR_POLLSET_NODEFAULT; for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) { - rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime, + rv = apr_pollset_create_ex(&event_pollset, pollset_size + 1, pruntime, pollset_flags, good_methods[i]); if (rv == APR_SUCCESS) { listener_is_wakeable = 1; break; } } - if (rv != APR_SUCCESS) { - pollset_flags &= ~APR_POLLSET_WAKEABLE; - for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) { - rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime, - pollset_flags, good_methods[i]); - if (rv == APR_SUCCESS) { - break; - } - } - } if (rv != APR_SUCCESS) { pollset_flags &= ~APR_POLLSET_NODEFAULT; - rv = apr_pollset_create(&event_pollset, pollset_size, pruntime, + rv = apr_pollset_create(&event_pollset, pollset_size + 1, pruntime, pollset_flags); + if (rv == APR_SUCCESS) { + listener_is_wakeable = 1; + } + else { + pollset_flags &= ~APR_POLLSET_WAKEABLE; + rv = apr_pollset_create(&event_pollset, pollset_size, pruntime, + pollset_flags); + } } if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03103) From d010a86e2d1fccb82e41db6097503370b7868249 Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 12 Apr 2024 11:02:31 +0000 Subject: [PATCH 010/164] mpm_worker: Fix AH00045 about children processes not terminating timely. * server/mpm/worker/worker.c(setup_threads_runtime): Create pollset with APR_POLLSET_WAKEABLE to be able to wake up the listener when stopping. * server/mpm/worker/worker.c(wakeup_listener): Wake up the listener using the wakeup pipe (apr_pollset_wakeup). * server/mpm/worker/worker.c(join_workers): Like mpm_event, don't depend on `pthread_kill(listener_thread, 0)` to check whether the listener has exited (this does not work on some systems), but use the "dying" global variable instead which is set by the listener just before exiting. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916926 13f79535-47bb-0310-9956-ffa450edef68 --- changes-entries/worker_exit.txt | 2 ++ server/mpm/worker/worker.c | 50 +++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 changes-entries/worker_exit.txt diff --git a/changes-entries/worker_exit.txt b/changes-entries/worker_exit.txt new file mode 100644 index 00000000000..5a2e712e95d --- /dev/null +++ b/changes-entries/worker_exit.txt @@ -0,0 +1,2 @@ + *) mpm_worker: Fix possible warning (AH00045) about children processes not + terminating timely. [Yann Ylavic] diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index ecf38aedfc9..2d1061f5cfc 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -126,10 +126,11 @@ static int max_workers = 0; static int server_limit = 0; static int thread_limit = 0; static int had_healthy_child = 0; -static int dying = 0; +static volatile int dying = 0; static int workers_may_exit = 0; static int start_thread_may_exit = 0; static int listener_may_exit = 0; +static int listener_is_wakeable = 0; /* Pollset supports APR_POLLSET_WAKEABLE */ static int requests_this_child; static int num_listensocks = 0; static int resource_shortage = 0; @@ -276,6 +277,15 @@ static void close_worker_sockets(void) static void wakeup_listener(void) { listener_may_exit = 1; + + /* Unblock the listener if it's poll()ing */ + if (worker_pollset && listener_is_wakeable) { + apr_pollset_wakeup(worker_pollset); + } + + /* unblock the listener if it's waiting for a worker */ + ap_queue_info_term(worker_queue_info); + if (!listener_os_thread) { /* XXX there is an obscure path that this doesn't handle perfectly: * right after listener thread is created but before @@ -284,10 +294,6 @@ static void wakeup_listener(void) */ return; } - - /* unblock the listener if it's waiting for a worker */ - ap_queue_info_term(worker_queue_info); - /* * we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all * platforms and wake up the listener thread since it is the only thread @@ -870,6 +876,7 @@ static void create_listener_thread(thread_starter *ts) static void setup_threads_runtime(void) { ap_listen_rec *lr; + int pollset_flags; apr_status_t rv; /* All threads (listener, workers) and synchronization objects (queues, @@ -903,8 +910,17 @@ static void setup_threads_runtime(void) } /* Create the main pollset */ - rv = apr_pollset_create(&worker_pollset, num_listensocks, pruntime, - APR_POLLSET_NOCOPY); + pollset_flags = APR_POLLSET_NOCOPY | APR_POLLSET_WAKEABLE; + rv = apr_pollset_create(&worker_pollset, num_listensocks + 1, pruntime, + pollset_flags); + if (rv == APR_SUCCESS) { + listener_is_wakeable = 1; + } + else { + pollset_flags &= ~APR_POLLSET_WAKEABLE; + rv = apr_pollset_create(&worker_pollset, num_listensocks, pruntime, + pollset_flags); + } if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(03285) "Couldn't create pollset in thread;" @@ -1058,19 +1074,17 @@ static void join_workers(apr_thread_t *listener, apr_thread_t **threads, */ iter = 0; - while (iter < 10 && -#ifdef HAVE_PTHREAD_KILL - pthread_kill(*listener_os_thread, 0) -#else - kill(ap_my_pid, 0) -#endif - == 0) { - /* listener not dead yet */ - apr_sleep(apr_time_make(0, 500000)); + while (!dying) { + apr_sleep(apr_time_from_msec(500)); + if (dying || ++iter > 10) { + break; + } + /* listener has not stopped accepting yet */ + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, + "listener has not stopped accepting yet (%d iter)", iter); wakeup_listener(); - ++iter; } - if (iter >= 10) { + if (iter > 10) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00276) "the listener thread didn't exit"); } From 6ed524dc3f4c2f655cdd316ddc7b78e0477c696f Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 12 Apr 2024 13:16:40 +0000 Subject: [PATCH 011/164] mpm_event,mpm_worker: Comment about pollset sizing when APR_POLLSET_WAKEABLE. Follow up to r1916925 and r1916926. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916929 13f79535-47bb-0310-9956-ffa450edef68 --- server/mpm/event/event.c | 5 ++++- server/mpm/worker/worker.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 372b94f61b4..0a11046b86c 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -2628,7 +2628,10 @@ static void setup_threads_runtime(void) clean_child_exit(APEXIT_CHILDFATAL); } - /* Create the main pollset */ + /* Create the main pollset. When APR_POLLSET_WAKEABLE is asked we account + * for the wakeup pipe explicitely with pollset_size+1 because some pollset + * implementations don't do it implicitely in APR. + */ pollset_flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY | APR_POLLSET_WAKEABLE | APR_POLLSET_NODEFAULT; for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) { diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 2d1061f5cfc..42b81a8ed1b 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -909,7 +909,10 @@ static void setup_threads_runtime(void) clean_child_exit(APEXIT_CHILDFATAL); } - /* Create the main pollset */ + /* Create the main pollset. When APR_POLLSET_WAKEABLE is asked we account + * for the wakeup pipe explicitely with num_listensocks+1 because some + * pollset implementations don't do it implicitely in APR. + */ pollset_flags = APR_POLLSET_NOCOPY | APR_POLLSET_WAKEABLE; rv = apr_pollset_create(&worker_pollset, num_listensocks + 1, pruntime, pollset_flags); From fc2f1ccb1e2dc2041a8e7fb45c71112ce7e4b7b1 Mon Sep 17 00:00:00 2001 From: Lucien Gentis Date: Sat, 13 Apr 2024 13:47:21 +0000 Subject: [PATCH 012/164] fr doc XML files updates. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916961 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/mod/mod_cgi.xml.fr | 3 ++- docs/manual/mod/mod_md.xml.fr | 33 ++++++++++++++++++++++++++++++-- docs/manual/mod/mod_proxy.xml.fr | 6 ++++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/manual/mod/mod_cgi.xml.fr b/docs/manual/mod/mod_cgi.xml.fr index e8c8e7584f1..6fab723eadc 100644 --- a/docs/manual/mod/mod_cgi.xml.fr +++ b/docs/manual/mod/mod_cgi.xml.fr @@ -1,7 +1,7 @@ - + @@ -198,6 +198,7 @@ module="core">Timeout lorsqu'elle n'est pas définie server config virtual hostdirectory .htaccess +Disponible à partir de la version 2.5.1 du serveur HTTP Apache.

Cette directive permet de limiter le temps d'attente jusqu'à une diff --git a/docs/manual/mod/mod_md.xml.fr b/docs/manual/mod/mod_md.xml.fr index 215e02d5c27..db068bd5d0b 100644 --- a/docs/manual/mod/mod_md.xml.fr +++ b/docs/manual/mod/mod_md.xml.fr @@ -2,7 +2,7 @@ - + + @@ -815,9 +815,11 @@ ProxyRemote ftp http://ftpproxy.mydomain:8080 ProxyRemoteMatch Le mandataire distant à utiliser pour traiter les requêtes correspondant à une expression rationnelle -ProxyRemoteMatch regex serveur-distant +ProxyRemoteMatch regex remote-server [username:password] server configvirtual host +Le troisième argument facultatif est disponible à partir de la +version 2.5.1 du serveur HTTP Apache.

La directive ProxyRemoteMatch est From 2ebb356671443c004c5a4784b05d43d199a897ce Mon Sep 17 00:00:00 2001 From: Lucien Gentis Date: Sat, 13 Apr 2024 14:02:16 +0000 Subject: [PATCH 013/164] fr doc rebuild. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1916962 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/bind.html.fr.utf8 | 2 +- docs/manual/caching.html.fr.utf8 | 2 +- docs/manual/compliance.html.fr.utf8 | 2 +- docs/manual/configuring.html.fr.utf8 | 2 +- docs/manual/content-negotiation.html.fr.utf8 | 2 +- docs/manual/custom-error.html.fr.utf8 | 2 +- docs/manual/dns-caveats.html.fr.utf8 | 2 +- docs/manual/dso.html.fr.utf8 | 2 +- docs/manual/env.html.fr.utf8 | 2 +- docs/manual/expr.html.fr.utf8 | 2 +- docs/manual/faq/index.html.fr.utf8 | 2 +- docs/manual/filter.html.fr.utf8 | 2 +- docs/manual/getting-started.html.fr.utf8 | 2 +- docs/manual/glossary.html.fr.utf8 | 2 +- docs/manual/handler.html.fr.utf8 | 2 +- docs/manual/howto/access.html.fr.utf8 | 2 +- docs/manual/howto/auth.html.fr.utf8 | 2 +- docs/manual/howto/cgi.html.fr.utf8 | 2 +- docs/manual/howto/encrypt.html.fr.utf8 | 2 +- docs/manual/howto/htaccess.html.fr.utf8 | 2 +- docs/manual/howto/http2.html.fr.utf8 | 2 +- docs/manual/howto/index.html.fr.utf8 | 2 +- docs/manual/howto/public_html.html.fr.utf8 | 2 +- docs/manual/howto/reverse_proxy.html.fr.utf8 | 2 +- docs/manual/howto/ssi.html.fr.utf8 | 2 +- docs/manual/index.html.fr.utf8 | 2 +- docs/manual/install.html.fr.utf8 | 2 +- docs/manual/invoking.html.fr.utf8 | 2 +- docs/manual/logs.html.fr.utf8 | 2 +- docs/manual/misc/index.html.fr.utf8 | 2 +- .../misc/password_encryptions.html.fr.utf8 | 2 +- docs/manual/misc/perf-scaling.html.fr.utf8 | 2 +- docs/manual/misc/perf-tuning.html.fr.utf8 | 2 +- .../misc/relevant_standards.html.fr.utf8 | 2 +- docs/manual/misc/security_tips.html.fr.utf8 | 2 +- docs/manual/mod/core.html.fr.utf8 | 2 +- docs/manual/mod/directive-dict.html.fr.utf8 | 2 +- docs/manual/mod/directives.html.fr.utf8 | 3 +- docs/manual/mod/event.html.fr.utf8 | 2 +- docs/manual/mod/index.html.fr.utf8 | 2 +- .../manual/mod/mod_access_compat.html.fr.utf8 | 2 +- docs/manual/mod/mod_actions.html.fr.utf8 | 2 +- docs/manual/mod/mod_alias.html.fr.utf8 | 2 +- .../manual/mod/mod_allowhandlers.html.fr.utf8 | 2 +- docs/manual/mod/mod_allowmethods.html.fr.utf8 | 2 +- docs/manual/mod/mod_asis.html.fr.utf8 | 2 +- docs/manual/mod/mod_auth_basic.html.fr.utf8 | 2 +- docs/manual/mod/mod_auth_bearer.html.fr.utf8 | 2 +- docs/manual/mod/mod_auth_digest.html.fr.utf8 | 2 +- docs/manual/mod/mod_auth_form.html.fr.utf8 | 2 +- docs/manual/mod/mod_authn_anon.html.fr.utf8 | 2 +- docs/manual/mod/mod_authn_core.html.fr.utf8 | 2 +- docs/manual/mod/mod_authn_dbd.html.fr.utf8 | 2 +- docs/manual/mod/mod_authn_dbm.html.fr.utf8 | 2 +- docs/manual/mod/mod_authn_file.html.fr.utf8 | 2 +- .../manual/mod/mod_authn_socache.html.fr.utf8 | 2 +- docs/manual/mod/mod_authnz_fcgi.html.fr.utf8 | 2 +- docs/manual/mod/mod_authnz_ldap.html.fr.utf8 | 2 +- docs/manual/mod/mod_autht_core.html.fr.utf8 | 2 +- docs/manual/mod/mod_autht_jwt.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_core.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_dbd.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_dbm.html.fr.utf8 | 2 +- .../mod/mod_authz_groupfile.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_host.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_owner.html.fr.utf8 | 2 +- docs/manual/mod/mod_authz_user.html.fr.utf8 | 2 +- docs/manual/mod/mod_autoindex.html.fr.utf8 | 2 +- docs/manual/mod/mod_brotli.html.fr.utf8 | 2 +- docs/manual/mod/mod_buffer.html.fr.utf8 | 2 +- docs/manual/mod/mod_cache.html.fr.utf8 | 2 +- docs/manual/mod/mod_cache_disk.html.fr.utf8 | 2 +- .../manual/mod/mod_cache_socache.html.fr.utf8 | 2 +- docs/manual/mod/mod_cern_meta.html.fr.utf8 | 2 +- docs/manual/mod/mod_cgi.html.fr.utf8 | 3 +- docs/manual/mod/mod_cgi.xml.ja | 2 +- docs/manual/mod/mod_cgi.xml.ko | 2 +- docs/manual/mod/mod_cgid.html.fr.utf8 | 2 +- docs/manual/mod/mod_charset_lite.html.fr.utf8 | 2 +- docs/manual/mod/mod_crypto.html.fr.utf8 | 2 +- docs/manual/mod/mod_data.html.fr.utf8 | 2 +- docs/manual/mod/mod_dav.html.fr.utf8 | 2 +- docs/manual/mod/mod_dav_fs.html.fr.utf8 | 2 +- docs/manual/mod/mod_dav_lock.html.fr.utf8 | 2 +- docs/manual/mod/mod_dbd.html.fr.utf8 | 2 +- docs/manual/mod/mod_deflate.html.fr.utf8 | 2 +- docs/manual/mod/mod_dialup.html.fr.utf8 | 2 +- docs/manual/mod/mod_dir.html.fr.utf8 | 2 +- docs/manual/mod/mod_dumpio.html.fr.utf8 | 2 +- docs/manual/mod/mod_echo.html.fr.utf8 | 2 +- docs/manual/mod/mod_env.html.fr.utf8 | 2 +- .../manual/mod/mod_example_hooks.html.fr.utf8 | 2 +- docs/manual/mod/mod_expires.html.fr.utf8 | 2 +- docs/manual/mod/mod_ext_filter.html.fr.utf8 | 2 +- docs/manual/mod/mod_file_cache.html.fr.utf8 | 2 +- docs/manual/mod/mod_filter.html.fr.utf8 | 2 +- docs/manual/mod/mod_firehose.html.fr.utf8 | 2 +- docs/manual/mod/mod_headers.html.fr.utf8 | 2 +- docs/manual/mod/mod_heartbeat.html.fr.utf8 | 2 +- docs/manual/mod/mod_heartmonitor.html.fr.utf8 | 2 +- docs/manual/mod/mod_http2.html.fr.utf8 | 2 +- docs/manual/mod/mod_ident.html.fr.utf8 | 2 +- docs/manual/mod/mod_imagemap.html.fr.utf8 | 2 +- docs/manual/mod/mod_include.html.fr.utf8 | 2 +- docs/manual/mod/mod_info.html.fr.utf8 | 2 +- docs/manual/mod/mod_isapi.html.fr.utf8 | 2 +- docs/manual/mod/mod_journald.html.fr.utf8 | 2 +- .../mod/mod_lbmethod_bybusyness.html.fr.utf8 | 2 +- .../mod/mod_lbmethod_byrequests.html.fr.utf8 | 2 +- .../mod/mod_lbmethod_bytraffic.html.fr.utf8 | 2 +- .../mod/mod_lbmethod_heartbeat.html.fr.utf8 | 2 +- docs/manual/mod/mod_ldap.html.fr.utf8 | 2 +- docs/manual/mod/mod_log_config.html.fr.utf8 | 2 +- docs/manual/mod/mod_log_debug.html.fr.utf8 | 2 +- docs/manual/mod/mod_log_forensic.html.fr.utf8 | 2 +- docs/manual/mod/mod_logio.html.fr.utf8 | 2 +- docs/manual/mod/mod_lua.html.fr.utf8 | 2 +- docs/manual/mod/mod_macro.html.fr.utf8 | 2 +- docs/manual/mod/mod_md.html.fr.utf8 | 32 +- docs/manual/mod/mod_mime.html.fr.utf8 | 2 +- docs/manual/mod/mod_mime_magic.html.fr.utf8 | 2 +- docs/manual/mod/mod_negotiation.html.fr.utf8 | 2 +- docs/manual/mod/mod_nw_ssl.html.fr.utf8 | 2 +- docs/manual/mod/mod_policy.html.fr.utf8 | 2 +- docs/manual/mod/mod_privileges.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy.html.fr.utf8 | 6 +- docs/manual/mod/mod_proxy.xml.ja | 2 +- docs/manual/mod/mod_proxy_ajp.html.fr.utf8 | 2 +- .../mod/mod_proxy_balancer.html.fr.utf8 | 2 +- .../manual/mod/mod_proxy_connect.html.fr.utf8 | 2 +- .../manual/mod/mod_proxy_express.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_fcgi.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_fdpass.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_ftp.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_hcheck.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_html.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_http.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_http2.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_scgi.html.fr.utf8 | 2 +- docs/manual/mod/mod_proxy_uwsgi.html.fr.utf8 | 2 +- .../mod/mod_proxy_wstunnel.html.fr.utf8 | 2 +- docs/manual/mod/mod_ratelimit.html.fr.utf8 | 2 +- docs/manual/mod/mod_reflector.html.fr.utf8 | 2 +- docs/manual/mod/mod_remoteip.html.fr.utf8 | 2 +- docs/manual/mod/mod_reqtimeout.html.fr.utf8 | 2 +- docs/manual/mod/mod_request.html.fr.utf8 | 2 +- docs/manual/mod/mod_rewrite.html.fr.utf8 | 2 +- docs/manual/mod/mod_sed.html.fr.utf8 | 2 +- docs/manual/mod/mod_session.html.fr.utf8 | 2 +- .../mod/mod_session_cookie.html.fr.utf8 | 2 +- .../mod/mod_session_crypto.html.fr.utf8 | 2 +- docs/manual/mod/mod_session_dbd.html.fr.utf8 | 2 +- docs/manual/mod/mod_setenvif.html.fr.utf8 | 2 +- .../manual/mod/mod_slotmem_plain.html.fr.utf8 | 2 +- docs/manual/mod/mod_slotmem_shm.html.fr.utf8 | 2 +- docs/manual/mod/mod_so.html.fr.utf8 | 2 +- docs/manual/mod/mod_socache_dbm.html.fr.utf8 | 2 +- docs/manual/mod/mod_socache_dc.html.fr.utf8 | 2 +- .../mod/mod_socache_memcache.html.fr.utf8 | 2 +- .../manual/mod/mod_socache_redis.html.fr.utf8 | 2 +- .../manual/mod/mod_socache_shmcb.html.fr.utf8 | 2 +- docs/manual/mod/mod_speling.html.fr.utf8 | 2 +- docs/manual/mod/mod_ssl.html.fr.utf8 | 2 +- docs/manual/mod/mod_ssl_ct.html.fr.utf8 | 2 +- docs/manual/mod/mod_status.html.fr.utf8 | 2 +- docs/manual/mod/mod_substitute.html.fr.utf8 | 2 +- docs/manual/mod/mod_suexec.html.fr.utf8 | 2 +- docs/manual/mod/mod_syslog.html.fr.utf8 | 2 +- docs/manual/mod/mod_systemd.html.fr.utf8 | 2 +- docs/manual/mod/mod_unique_id.html.fr.utf8 | 2 +- docs/manual/mod/mod_unixd.html.fr.utf8 | 2 +- docs/manual/mod/mod_userdir.html.fr.utf8 | 2 +- docs/manual/mod/mod_usertrack.html.fr.utf8 | 2 +- docs/manual/mod/mod_version.html.fr.utf8 | 2 +- docs/manual/mod/mod_vhost_alias.html.fr.utf8 | 2 +- docs/manual/mod/mod_watchdog.html.fr.utf8 | 2 +- docs/manual/mod/mod_xml2enc.html.fr.utf8 | 2 +- docs/manual/mod/module-dict.html.fr.utf8 | 2 +- docs/manual/mod/mpm_common.html.fr.utf8 | 2 +- docs/manual/mod/mpm_netware.html.fr.utf8 | 2 +- docs/manual/mod/mpm_winnt.html.fr.utf8 | 2 +- docs/manual/mod/mpmt_os2.html.fr.utf8 | 2 +- docs/manual/mod/overrides.html.fr.utf8 | 2 +- docs/manual/mod/prefork.html.fr.utf8 | 2 +- docs/manual/mod/quickreference.html.fr.utf8 | 811 +++++++++--------- docs/manual/mod/worker.html.fr.utf8 | 2 +- docs/manual/mpm.html.fr.utf8 | 2 +- docs/manual/new_features_2_0.html.fr.utf8 | 2 +- docs/manual/new_features_2_2.html.fr.utf8 | 2 +- docs/manual/new_features_2_4.html.fr.utf8 | 2 +- docs/manual/platform/index.html.fr.utf8 | 2 +- docs/manual/platform/netware.html.fr.utf8 | 2 +- docs/manual/platform/perf-hp.html.fr.utf8 | 2 +- docs/manual/platform/rpm.html.fr.utf8 | 2 +- .../platform/win_compiling.html.fr.utf8 | 2 +- docs/manual/platform/windows.html.fr.utf8 | 2 +- docs/manual/programs/ab.html.fr.utf8 | 2 +- docs/manual/programs/apachectl.html.fr.utf8 | 2 +- docs/manual/programs/apxs.html.fr.utf8 | 2 +- docs/manual/programs/configure.html.fr.utf8 | 2 +- docs/manual/programs/ctlogconfig.html.fr.utf8 | 2 +- docs/manual/programs/dbmmanage.html.fr.utf8 | 2 +- docs/manual/programs/fcgistarter.html.fr.utf8 | 2 +- docs/manual/programs/firehose.html.fr.utf8 | 2 +- .../manual/programs/htcacheclean.html.fr.utf8 | 2 +- docs/manual/programs/htdbm.html.fr.utf8 | 2 +- docs/manual/programs/htdigest.html.fr.utf8 | 2 +- docs/manual/programs/htpasswd.html.fr.utf8 | 2 +- docs/manual/programs/httpd.html.fr.utf8 | 2 +- docs/manual/programs/httxt2dbm.html.fr.utf8 | 2 +- docs/manual/programs/index.html.fr.utf8 | 2 +- .../programs/log_server_status.html.fr.utf8 | 2 +- docs/manual/programs/logresolve.html.fr.utf8 | 2 +- docs/manual/programs/other.html.fr.utf8 | 2 +- docs/manual/programs/rotatelogs.html.fr.utf8 | 2 +- .../programs/split-logfile.html.fr.utf8 | 2 +- docs/manual/programs/suexec.html.fr.utf8 | 2 +- docs/manual/rewrite/access.html.fr.utf8 | 2 +- docs/manual/rewrite/advanced.html.fr.utf8 | 2 +- docs/manual/rewrite/avoid.html.fr.utf8 | 2 +- docs/manual/rewrite/flags.html.fr.utf8 | 2 +- docs/manual/rewrite/htaccess.html.fr.utf8 | 2 +- docs/manual/rewrite/index.html.fr.utf8 | 2 +- docs/manual/rewrite/intro.html.fr.utf8 | 2 +- docs/manual/rewrite/proxy.html.fr.utf8 | 2 +- docs/manual/rewrite/remapping.html.fr.utf8 | 2 +- docs/manual/rewrite/rewritemap.html.fr.utf8 | 2 +- docs/manual/rewrite/tech.html.fr.utf8 | 2 +- docs/manual/rewrite/vhosts.html.fr.utf8 | 2 +- docs/manual/sections.html.fr.utf8 | 2 +- docs/manual/server-wide.html.fr.utf8 | 2 +- docs/manual/sitemap.html.fr.utf8 | 2 +- docs/manual/socache.html.fr.utf8 | 2 +- docs/manual/ssl/index.html.fr.utf8 | 2 +- docs/manual/ssl/ssl_compat.html.fr.utf8 | 2 +- docs/manual/ssl/ssl_faq.html.fr.utf8 | 2 +- docs/manual/ssl/ssl_howto.html.fr.utf8 | 2 +- docs/manual/ssl/ssl_intro.html.fr.utf8 | 2 +- docs/manual/stopping.html.fr.utf8 | 2 +- docs/manual/suexec.html.fr.utf8 | 2 +- docs/manual/upgrading.html.fr.utf8 | 2 +- docs/manual/urlmapping.html.fr.utf8 | 2 +- docs/manual/vhosts/details.html.fr.utf8 | 2 +- docs/manual/vhosts/examples.html.fr.utf8 | 2 +- docs/manual/vhosts/fd-limits.html.fr.utf8 | 2 +- docs/manual/vhosts/index.html.fr.utf8 | 2 +- docs/manual/vhosts/ip-based.html.fr.utf8 | 2 +- docs/manual/vhosts/mass.html.fr.utf8 | 2 +- docs/manual/vhosts/name-based.html.fr.utf8 | 2 +- 249 files changed, 689 insertions(+), 654 deletions(-) diff --git a/docs/manual/bind.html.fr.utf8 b/docs/manual/bind.html.fr.utf8 index f44f377112a..eeab8467952 100644 --- a/docs/manual/bind.html.fr.utf8 +++ b/docs/manual/bind.html.fr.utf8 @@ -247,7 +247,7 @@ var comments_identifier = 'http://httpd.apache.org/docs/trunk/bind.html'; } })(window, document); //-->