]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_md: update to v2.4.26
authorStefan Eissing <icing@apache.org>
Mon, 8 Apr 2024 11:24:18 +0000 (11:24 +0000)
committerStefan Eissing <icing@apache.org>
Mon, 8 Apr 2024 11:24:18 +0000 (11:24 +0000)
- 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

17 files changed:
changes-entries/md_2.4.26.txt [new file with mode: 0644]
docs/manual/mod/mod_md.xml
modules/md/md_ocsp.c
modules/md/md_reg.c
modules/md/md_reg.h
modules/md/md_version.h
modules/md/mod_md_config.c
modules/md/mod_md_config.h
modules/md/mod_md_drive.c
test/modules/md/conftest.py
test/modules/md/md_cert_util.py
test/modules/md/md_env.py
test/modules/md/test_300_conf_validate.py
test/modules/md/test_310_conf_store.py
test/modules/md/test_502_acmev2_drive.py
test/modules/md/test_602_roundtrip.py
test/modules/md/test_750_eab.py

diff --git a/changes-entries/md_2.4.26.txt b/changes-entries/md_2.4.26.txt
new file mode 100644 (file)
index 0000000..9b82f61
--- /dev/null
@@ -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.
index d4adac2114eadacdd734ca341e5119c2a3a45b53..b136ca0d73a847b7bffa7e79f2af6f4279083c5e 100644 (file)
@@ -1512,4 +1512,29 @@ MDMessageCmd /etc/apache/md-message
         </usage>
     </directivesynopsis>
 
+    <directivesynopsis>
+        <name>MDCheckInterval</name>
+        <description>Determines how often certificates are checked</description>
+        <syntax>MDCheckInterval <var>duration</var></syntax>
+        <default>MDCheckInterval 12h</default>
+        <contextlist>
+            <context>server config</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.60 and later</compatibility>
+        <usage>
+            <p>
+                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.
+            </p><p>
+                The minimum duration you may configure is 1 second. It is
+                not recommended to use such short times in production.
+            </p>
+        </usage>
+    </directivesynopsis>
+
 </modulesynopsis>
index c957c1d9cd3bd4f3d01c0ab21c4de4d5189bf7a6..8276137c3e805b3eaf8feecb85675e1d3cfff9d9 100644 (file)
@@ -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);
index 8bceb0eb47086f06e388d7ea13e75067a2aa61a4..6aa7d788769398a7ed500f16c7034600fe051354 100644 (file)
@@ -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;
+}
index 58ee16ac62f9452c933e5f8fcd980ed65da75497..191b026e46af74fe9a048ee6592cc40bb1e168c6 100644 (file)
@@ -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 */
index 86a1821c7c8eb12d3ce2e6131aab9c98c6027c44..cefbb8ded7287e3e6d5d7d152808d8fb093fc497 100644 (file)
@@ -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"
index 31d06b4bc5b9081470aff8942e3ebcaa890bbf77..cdd1e297c74b99b373b0fb97d16c98b4da0a5f20 100644 (file)
@@ -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)
 };
 
index 7e87440ed18051668aa40da2944836fd81e90e14..1ce2375f00de09da8e60d305d80433721db7e1ff 100644 (file)
@@ -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 */
index 5565f44d758c1c7cd5ff5ca6316cd2bf44ead640..f131f07b8884fc721e42b4ce0a3875275705817f 100644 (file)
@@ -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 *);
                 
index 192cd31a80bb28cc963068be774bea483baea930..0118de5e1339705855a28f157082af4637b3dab5 100755 (executable)
@@ -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")
index 8cd99aa76f6a6f601c374be1c4f518e19d3fb653..abcd36b938c37bda3e249c75b9889750297992ce 100755 (executable)
@@ -166,10 +166,10 @@ class MDCertUtil(object):
 
     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()
index e8e36e5b1bc3d4d5b98131db1a1565b41d03d78b..193651948ad931eebe3edd96d3957f6857975361 100755 (executable)
@@ -73,7 +73,11 @@ class MDTestEnv(HttpdTestEnv):
 
     @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 @@ class MDTestEnv(HttpdTestEnv):
         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()
index f73bf67999db2795074597fdfbdcee576180f35d..88df1683413973241e5a27675603966d4933d52d 100644 (file)
@@ -15,7 +15,8 @@ from .md_env import MDTestEnv
 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 @@ class TestConf:
     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 @@ class TestConf:
     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 @@ class TestConf:
             </VirtualHost>
             """)
         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 @@ class TestConf:
         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.*'
+        ])
 
index d56790bb1fbd5c73656591b1cffe70ea52794c11..f2bb9c723aced8bfea2ec285ae8c80cfcc005b7d 100644 (file)
@@ -48,11 +48,6 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
             "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 @@ class TestConf:
             "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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
         # 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 @@ class TestConf:
         # 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 @@ class TestConf:
         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 @@ class TestConf:
         # 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 @@ class TestConf:
         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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
         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 @@ class TestConf:
         # 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 @@ class TestConf:
             """).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 @@ class TestConf:
         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 @@ class TestConf:
             """).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 @@ class TestConf:
             "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 @@ class TestConf:
             """).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 @@ class TestConf:
             """).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 @@ class TestConf:
         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):
index a98e4ad97c7442798db92392262ba9ba83d03e51..eb754f25eff9fafea6bbe933b974fc24a1acaf31 100644 (file)
@@ -436,11 +436,6 @@ class TestDrivev2:
             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 @@ class TestDrivev2:
         # 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
 
index e2e74c7d81b1f5ea6dd576637fd7fcd39b2aea98..9ff87e5df7e88361513ed3c5f3ba8194c905eb2e 100644 (file)
@@ -52,13 +52,9 @@ class TestRoundtripv2:
         # 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 @@ class TestRoundtripv2:
         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 @@ class TestRoundtripv2:
         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_ ---------
 
index 7d81917829e2a3bb1e41162e71e43571d57adbe0..aec7e89b8c2542fe7fa6dac0bc8d14f732c595cf 100644 (file)
@@ -82,14 +82,17 @@ class TestEab:
         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 @@ class TestEab:
         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 @@ class TestEab:
         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).*'
             ]
         )