]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
*) mod_md: full squash of all changes in trunk, since people
authorStefan Eissing <icing@apache.org>
Wed, 5 Feb 2025 12:41:51 +0000 (12:41 +0000)
committerStefan Eissing <icing@apache.org>
Wed, 5 Feb 2025 12:41:51 +0000 (12:41 +0000)
    do changes and miss proposing them for backport.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1923593 13f79535-47bb-0310-9956-ffa450edef68

48 files changed:
changes-entries/md_v2.5.1.txt [new file with mode: 0644]
modules/md/md.h
modules/md/md_acme.c
modules/md/md_acme.h
modules/md/md_acme_drive.c
modules/md/md_acme_drive.h
modules/md/md_acme_order.c
modules/md/md_acme_order.h
modules/md/md_acmev2_drive.c
modules/md/md_core.c
modules/md/md_crypt.c
modules/md/md_crypt.h
modules/md/md_curl.c
modules/md/md_reg.c
modules/md/md_status.c
modules/md/md_store_fs.c
modules/md/md_version.h
modules/md/mod_md.c
modules/md/mod_md_config.c
modules/md/mod_md_config.h
test/modules/md/conftest.py
test/modules/md/pebble/pebble-eab.json.template
test/modules/md/pebble/pebble.json.template
test/modules/md/test_010_store_migrate.py
test/modules/md/test_202_acmev2_regs.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_702_auto.py
test/modules/md/test_710_profiles.py [new file with mode: 0644]
test/modules/md/test_720_wildcard.py
test/modules/md/test_730_static.py
test/modules/md/test_740_acme_errors.py
test/modules/md/test_741_setup_errors.py
test/modules/md/test_750_eab.py
test/modules/md/test_751_sectigo.py
test/modules/md/test_752_zerossl.py
test/modules/md/test_780_tailscale.py
test/modules/md/test_790_failover.py
test/modules/md/test_800_must_staple.py
test/modules/md/test_801_stapling.py
test/modules/md/test_810_ec.py
test/modules/md/test_820_locks.py
test/modules/md/test_900_notify.py
test/modules/md/test_901_message.py
test/modules/md/test_910_cleanups.py
test/modules/md/test_920_status.py

diff --git a/changes-entries/md_v2.5.1.txt b/changes-entries/md_v2.5.1.txt
new file mode 100644 (file)
index 0000000..051f2aa
--- /dev/null
@@ -0,0 +1,14 @@
+  *) mod_md: update to version 2.5.1
+     - Added support for ACME profiles with new directives MDProfile and
+       MDProfileMandatory.
+     - When installing a custom CA file via `MDCACertificateFile`, also set the
+       libcurl option CURLSSLOPT_NO_REVOKE that suppresses complains by Schannel
+       (when curl is linked with it) about missing CRL/OCSP in certificates.
+     - Fixed handling of corrupted httpd.json and added test 300_30 for it.
+       File is removed on error and written again. Fixes #369.
+     - Added explanation in log for how to proceed when md_store.json could not be
+       parsed and prevented the server start.
+     - restored fixed to #336 and #337 which got lost in a sync with Apache svn
+     - Add Issue Name/Uris to certificate information in md-status handler
+     - MDomains with static certificate files have MDRenewMode "manual", unless
+       "always" is configured.
index 035ccba78376a98b5c75d2a43b41566b32c56cba..3f298eaa6f33e33ebee6d2d337f01966522bc7a3 100644 (file)
@@ -92,6 +92,8 @@ struct md_t {
     struct apr_array_header_t *pkey_files; /* != NULL iff privkeys explicitly configured */
     const char *ca_eab_kid;         /* optional KEYID for external account binding */
     const char *ca_eab_hmac;        /* optional HMAC for external account binding */
+    const char *profile;            /* optional cert profile to order */
+    int profile_mandatory;          /* if profile, when given, is mandatory */
 
     const char *state_descr;        /* description of state of NULL */
     
@@ -154,6 +156,8 @@ struct md_t {
 #define MD_KEY_HTTPS            "https"
 #define MD_KEY_ID               "id"
 #define MD_KEY_IDENTIFIER       "identifier"
+#define MD_KEY_ISSUER_NAME      "issuer-name"
+#define MD_KEY_ISSUER_URI       "issuer-uri"
 #define MD_KEY_KEY              "key"
 #define MD_KEY_KID              "kid"
 #define MD_KEY_KEYAUTHZ         "keyAuthorization"
@@ -175,6 +179,8 @@ struct md_t {
 #define MD_KEY_PKEY             "privkey"
 #define MD_KEY_PKEY_FILES       "pkey-files"
 #define MD_KEY_PROBLEM          "problem"
+#define MD_KEY_PROFILE          "profile"
+#define MD_KEY_PROFILE_MANDATORY "profile-mandatory"
 #define MD_KEY_PROTO            "proto"
 #define MD_KEY_READY            "ready"
 #define MD_KEY_REGISTRATION     "registration"
index 4366bf695c2286aa51fdaa16d9c9569ad9da43b5..f8624513ba8e10655157cf8a5635687935c2d325 100644 (file)
@@ -664,6 +664,15 @@ typedef struct {
     md_result_t *result;
 } update_dir_ctx;
 
+static int collect_profiles(void *baton, const char* key, md_json_t *json)
+{
+    update_dir_ctx *ctx = baton;
+    (void)json;
+    APR_ARRAY_PUSH(ctx->acme->api.v2.profiles, const char *) =
+        apr_pstrdup(ctx->acme->p, key);
+    return 1;
+}
+
 static apr_status_t update_directory(const md_http_response_t *res, void *data)
 {
     md_http_request_t *req = res->req;
@@ -728,6 +737,20 @@ static apr_status_t update_directory(const md_http_response_t *res, void *data)
         acme->new_nonce_fn = acmev2_new_nonce;
         acme->req_init_fn = acmev2_req_init;
         acme->post_new_account_fn = acmev2_POST_new_account;
+
+        if (md_json_has_key(json, "meta", "profiles", NULL)) {
+            acme->api.v2.profiles = apr_array_make(acme->p, 5, sizeof(const char*));
+            md_json_iterkey(collect_profiles, data, json, "meta", "profiles", NULL);
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, req->pool,
+                          "found %d profiles in ACME directory meta",
+                          acme->api.v2.profiles->nelts);
+        }
+        else {
+            acme->api.v2.profiles = NULL;
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, req->pool,
+                          "no profiles in ACME directory meta");
+
+        }
     }
     else if ((s = md_json_dups(acme->p, json, "new-authz", NULL))) {
         acme->api.v1.new_authz = s;
index f28f2b6c617f6bbe890e880e3ab691ce10b81782..9931f92493ff9b80f8ec4ce2d34251d61d86c36d 100644 (file)
@@ -118,6 +118,7 @@ struct md_acme_t {
             const char *key_change;
             const char *revoke_cert;
             const char *new_nonce;
+            struct apr_array_header_t *profiles;
         } v2;
     } api;
     const char *ca_agreement;
index 45c39b37ad1959a5ddd38f57e3939f27191301a9..f5cd08c5214f53fa69c6dd5436e094417eb87075 100644 (file)
@@ -765,7 +765,9 @@ static apr_status_t acme_renew(md_proto_driver_t *d, md_result_t *result)
     if (!ad->domains) {
         ad->domains = md_dns_make_minimal(d->p, ad->md->domains);
     }
-    
+    ad->profile = ad->md->profile;
+    ad->profile_mandatory = ad->md->profile_mandatory;
+
     md_result_activity_printf(result, "Contacting ACME server for %s at %s",
                               d->md->name, ca_effective);
     if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
index 88761fab95dbbdb060912758f0d8027dd4009c43..986b49e8f4a119097979cfe846d9df100644ac6b 100644 (file)
@@ -29,6 +29,8 @@ typedef struct md_acme_driver_t {
     md_t *md;
     struct apr_array_header_t *domains;
     apr_array_header_t *ca_challenges;
+    const char *profile;
+    int profile_mandatory;
     
     int complete;
     apr_array_header_t *creds;       /* the new md_credentials_t */
index 061093a413262f1e861db22ae93064879cb0b983..0a0ad7ff0aee943b80019f271e246d7834b0f9f3 100644 (file)
@@ -263,13 +263,14 @@ typedef struct {
     md_acme_order_t *order;
     md_acme_t *acme;
     const char *name;
+    const char *profile;
     apr_array_header_t *domains;
     md_result_t *result;
 } order_ctx_t;
 
-#define ORDER_CTX_INIT(ctx, p, o, a, n, d, r) \
+#define ORDER_CTX_INIT(ctx, p, o, a, n, d, pf, r) \
     (ctx)->p = (p); (ctx)->order = (o); (ctx)->acme = (a); \
-    (ctx)->name = (n); (ctx)->domains = d; (ctx)->result = r
+    (ctx)->name = (n); (ctx)->domains = d; (ctx)->profile = pf; (ctx)->result = r
 
 static apr_status_t identifier_to_json(void *value, md_json_t *json, apr_pool_t *p, void *baton)
 {
@@ -289,6 +290,8 @@ static apr_status_t on_init_order_register(md_acme_req_t *req, void *baton)
 
     jpayload = md_json_create(req->p);
     md_json_seta(ctx->domains, identifier_to_json, NULL, jpayload, "identifiers", NULL);
+    if (ctx->profile)
+      md_json_sets(ctx->profile, jpayload, "profile", NULL);
 
     return md_acme_req_body_init(req, jpayload);
 } 
@@ -321,13 +324,14 @@ out:
 }
 
 apr_status_t md_acme_order_register(md_acme_order_t **porder, md_acme_t *acme, apr_pool_t *p, 
-                                    const char *name, apr_array_header_t *domains)
+                                    const char *name, apr_array_header_t *domains,
+                                    const char *profile)
 {
     order_ctx_t ctx;
     apr_status_t rv;
     
     assert(MD_ACME_VERSION_MAJOR(acme->version) > 1);
-    ORDER_CTX_INIT(&ctx, p, NULL, acme, name, domains, NULL);
+    ORDER_CTX_INIT(&ctx, p, NULL, acme, name, domains, profile, NULL);
     rv = md_acme_POST(acme, acme->api.v2.new_order, on_init_order_register, on_order_upd, NULL, NULL, &ctx);
     *porder = (APR_SUCCESS == rv)? ctx.order : NULL;
     return rv;
@@ -340,7 +344,7 @@ apr_status_t md_acme_order_update(md_acme_order_t *order, md_acme_t *acme,
     apr_status_t rv;
     
     assert(MD_ACME_VERSION_MAJOR(acme->version) > 1);
-    ORDER_CTX_INIT(&ctx, p, order, acme, NULL, NULL, result);
+    ORDER_CTX_INIT(&ctx, p, order, acme, NULL, NULL, NULL, result);
     rv = md_acme_GET(acme, order->url, NULL, on_order_upd, NULL, NULL, &ctx);
     if (APR_SUCCESS != rv && APR_SUCCESS != acme->last->status) {
         md_result_dup(result, acme->last);
@@ -380,7 +384,7 @@ apr_status_t md_acme_order_await_ready(md_acme_order_t *order, md_acme_t *acme,
     apr_status_t rv;
     
     assert(MD_ACME_VERSION_MAJOR(acme->version) > 1);
-    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, result);
+    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, NULL, result);
 
     md_result_activity_setn(result, "Waiting for order to become ready");
     rv = md_util_try(await_ready, &ctx, 0, timeout, 0, 0, 1);
@@ -423,7 +427,7 @@ apr_status_t md_acme_order_await_valid(md_acme_order_t *order, md_acme_t *acme,
     apr_status_t rv;
     
     assert(MD_ACME_VERSION_MAJOR(acme->version) > 1);
-    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, result);
+    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, NULL, result);
 
     md_result_activity_setn(result, "Waiting for finalized order to become valid");
     rv = md_util_try(await_valid, &ctx, 0, timeout, 0, 0, 1);
@@ -552,7 +556,7 @@ apr_status_t md_acme_order_monitor_authzs(md_acme_order_t *order, md_acme_t *acm
     order_ctx_t ctx;
     apr_status_t rv;
     
-    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, result);
+    ORDER_CTX_INIT(&ctx, p, order, acme, md->name, NULL, NULL, result);
     
     md_result_activity_printf(result, "Monitoring challenge status for %s", md->name);
     rv = md_util_try(check_challenges, &ctx, 0, timeout, 0, 0, 1);
index 417044018f51e47f65c2a9d6cb60e1d1e04492cd..01d73d41b9f4921908b8467bd58bb5d73f048e64 100644 (file)
@@ -76,7 +76,8 @@ apr_status_t md_acme_order_monitor_authzs(md_acme_order_t *order, md_acme_t *acm
 /* ACMEv2 only ************************************************************************************/
 
 apr_status_t md_acme_order_register(md_acme_order_t **porder, md_acme_t *acme, apr_pool_t *p, 
-                                    const char *name, struct apr_array_header_t *domains);
+                                    const char *name, struct apr_array_header_t *domains,
+                                    const char *profile);
 
 apr_status_t md_acme_order_update(md_acme_order_t *order, md_acme_t *acme, 
                                   struct md_result_t *result, apr_pool_t *p);
index 1eda1dc15bfda0bd011000473c3f8549faf2f86e..e5821e560aef83a5046811c742d2f80eed6fe35e 100644 (file)
@@ -56,6 +56,7 @@ static apr_status_t ad_setup_order(md_proto_driver_t *d, md_result_t *result, in
     md_acme_driver_t *ad = d->baton;
     apr_status_t rv;
     md_t *md = ad->md;
+    const char *profile = NULL;
     
     assert(ad->md);
     assert(ad->acme);
@@ -77,7 +78,33 @@ static apr_status_t ad_setup_order(md_proto_driver_t *d, md_result_t *result, in
     }
     
     md_result_activity_setn(result, "Creating new order");
-    rv = md_acme_order_register(&ad->order, ad->acme, d->p, d->md->name, ad->domains);
+    if (ad->profile) {
+        if(ad->acme->api.v2.profiles) {
+            int i;
+            for (i = 0; !profile && i < ad->acme->api.v2.profiles->nelts; ++i) {
+                const char *s = APR_ARRAY_IDX(ad->acme->api.v2.profiles, i, const char*);
+                if (!apr_strnatcasecmp(s, ad->profile))
+                   profile = s;
+            }
+        }
+        if (profile)
+            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p,
+                          "%s: ordering ACME profile '%s'", md->name, profile);
+        else if (ad->profile_mandatory) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
+                          "%s: mandatory ACME profile '%s' is not offered by CA",
+                          md->name, ad->profile);
+            rv = APR_EINVAL;
+            goto leave;
+        }
+        else {
+            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p,
+                          "%s: ACME profile '%s' is not offered by CA, continuing without",
+                          md->name, ad->profile);
+        }
+    }
+
+    rv = md_acme_order_register(&ad->order, ad->acme, d->p, d->md->name, ad->domains, profile);
     if (APR_SUCCESS !=rv) goto leave;
     rv = md_acme_order_save(d->store, d->p, MD_SG_STAGING, d->md->name, ad->order, 0);
     if (APR_SUCCESS != rv) {
index 7aacff0497a38e201bc439e98dc1d7502c4c199a..70f20c4bebe3262b8f9b5d5963448aa32de5debf 100644 (file)
@@ -317,6 +317,8 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
             md_json_sets(md->ca_eab_kid, json, MD_KEY_EAB, MD_KEY_KID, NULL);
             if (md->ca_eab_hmac) md_json_sets(md->ca_eab_hmac, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
         }
+        if (md->profile) md_json_sets(md->profile, json, MD_KEY_PROFILE, NULL);
+        md_json_setb(md->profile_mandatory > 0, json, MD_KEY_PROFILE_MANDATORY, NULL);
         return json;
     }
     return NULL;
@@ -383,6 +385,10 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
             md->ca_eab_kid = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_KID, NULL);
             md->ca_eab_hmac = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
         }
+
+        md->profile_mandatory = (int)md_json_getb(json, MD_KEY_PROFILE_MANDATORY, NULL);
+        if (md_json_has_key(json, MD_KEY_PROFILE, NULL))
+            md->profile = md_json_dups(p, json, MD_KEY_PROFILE, NULL);
         return md;
     }
     return NULL;
index ca44fab064c2622128db02d89e4a2213f9a4a4c5..77feb55a850209675b61bde3764e1b7b974d174e 100644 (file)
         || LIBRESSL_VERSION_NUMBER >= 0x3050000fL)
 /* Missing from LibreSSL < 3.5.0 and only available since OpenSSL v1.1.x */
 #include <openssl/ct.h>
+#define MD_HAVE_CT 1
+#endif
+#ifndef MD_HAVE_CT
+#define MD_HAVE_CT 0
 #endif
 
 static int initialized;
@@ -978,42 +982,64 @@ static const char *bn64(const BIGNUM *b, apr_pool_t *p)
 
 const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p)
 {
+    const char *e64 = NULL;
+
 #if OPENSSL_VERSION_NUMBER < 0x30000000L
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+#else
     const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+#endif
     if (rsa) {
         const BIGNUM *e;
         RSA_get0_key(rsa, NULL, &e, NULL);
-        return bn64(e, p);
+        e64 = bn64(e, p);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+        RSA_free(rsa);
+#endif
     }
-#else
+
+#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
     BIGNUM *e = NULL;
     if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_E, &e)) {
-        const char *e64 = bn64(e, p);
+        e64 = bn64(e, p);
         BN_free(e);
-        return e64;
     }
 #endif
-    return NULL;
+
+    return e64;
 }
 
 const char *md_pkey_get_rsa_n64(md_pkey_t *pkey, apr_pool_t *p)
 {
+    const char *n64 = NULL;
+
 #if OPENSSL_VERSION_NUMBER < 0x30000000L
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+#else
     const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+#endif
     if (rsa) {
         const BIGNUM *n;
         RSA_get0_key(rsa, &n, NULL, NULL);
-        return bn64(n, p);
+        n64 = bn64(n, p);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+        RSA_free(rsa);
+#endif
     }
-#else
+
+#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
     BIGNUM *n = NULL;
     if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_N, &n)) {
-        const char *n64 = bn64(n, p);
+        n64 = bn64(n, p);
         BN_free(n);
-        return n64;
     }
 #endif
-    return NULL;
+
+    return n64;
 }
 
 apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *p, 
@@ -1265,6 +1291,18 @@ int md_cert_covers_md(md_cert_t *cert, const md_t *md)
     return 0;
 }
 
+const char *md_cert_get_issuer_name(const md_cert_t *cert, apr_pool_t *p)
+{
+    X509_NAME *xname = X509_get_issuer_name(cert->x509);
+    if(xname) {
+      char *name, *s = X509_NAME_oneline(xname, NULL, 0);
+      name = apr_pstrdup(p, s);
+      OPENSSL_free(s);
+      return name;
+    }
+    return NULL;
+}
+
 apr_status_t md_cert_get_issuers_uri(const char **puri, const md_cert_t *cert, apr_pool_t *p)
 {
     apr_status_t rv = APR_ENOENT;
@@ -2037,11 +2075,10 @@ out:
     return rv;
 }
 
+#if MD_HAVE_CT
 #define MD_OID_CT_SCTS_NUM          "1.3.6.1.4.1.11129.2.4.2"
 #define MD_OID_CT_SCTS_SNAME        "CT-SCTs"
 #define MD_OID_CT_SCTS_LNAME        "CT Certificate SCTs" 
-
-#ifndef OPENSSL_NO_CT
 static int get_ct_scts_nid(void)
 {
     int nid = OBJ_txt2nid(MD_OID_CT_SCTS_NUM);
@@ -2065,7 +2102,7 @@ const char *md_nid_get_lname(int nid)
 
 apr_status_t md_cert_get_ct_scts(apr_array_header_t *scts, apr_pool_t *p, const md_cert_t *cert)
 {
-#ifndef OPENSSL_NO_CT
+#if MD_HAVE_CT
     int nid, i, idx, critical;
     STACK_OF(SCT) *sct_list;
     SCT *sct_handle;
index a892e00f1e6b399cd31c6a528c5e3300991be7a1..e6b3ac2e783e4ecc2af7f693d77adcb4d5a57cfd 100644 (file)
@@ -190,6 +190,7 @@ struct md_timeperiod_t md_cert_get_valid(const md_cert_t *cert);
  */
 int md_certs_are_equal(const md_cert_t *a, const md_cert_t *b);
 
+const char *md_cert_get_issuer_name(const md_cert_t *cert, apr_pool_t *p);
 apr_status_t md_cert_get_issuers_uri(const char **puri, const md_cert_t *cert, apr_pool_t *p);
 apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, const md_cert_t *cert, apr_pool_t *p);
 
index 217e8579dd834dbd1d0683b6d853146a818b1280..3105d31ef8b6e60e6e27e89d0e10c043f45bb7ec 100644 (file)
@@ -244,6 +244,7 @@ static apr_status_t internals_setup(md_http_request_t *req)
     md_curl_internals_t *internals;
     CURL *curl;
     apr_status_t rv = APR_SUCCESS;
+    long ssl_options = 0;
 
     curl = md_http_get_impl_data(req->http);
     if (!curl) {
@@ -302,6 +303,10 @@ static apr_status_t internals_setup(md_http_request_t *req)
     }
     if (req->ca_file) {
         curl_easy_setopt(curl, CURLOPT_CAINFO, req->ca_file);
+        /* for a custom CA, allow certificates checking to ignore the
+         * Schannel error CRYPT_E_NO_REVOCATION_CHECK (could be a missing OCSP
+         * responder URL in the certs???). See issue #361 */
+        ssl_options |= CURLSSLOPT_NO_REVOKE;
     }
     if (req->unix_socket_path) {
         curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, req->unix_socket_path);
@@ -340,7 +345,10 @@ static apr_status_t internals_setup(md_http_request_t *req)
         curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debug_log);
         curl_easy_setopt(curl, CURLOPT_DEBUGDATA, req);
     }
-    
+
+    if (ssl_options)
+        curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, ssl_options);
+
 leave:
     req->internals = (APR_SUCCESS == rv)? internals : NULL;
     return rv;
index dc49446ae452011dabbb528a0414f1110aff302e..d0a41de177d0882fb16b97249c5a765b20b5a018 100644 (file)
@@ -81,6 +81,18 @@ static apr_status_t load_props(md_reg_t *reg, apr_pool_t *p)
     else if (APR_STATUS_IS_ENOENT(rv)) {
         rv = APR_SUCCESS;
     }
+    else {
+        apr_status_t rv2;
+        md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, p,
+                      "removing md/%s on error loading it", MD_FN_HTTPD_JSON);
+        rv2 = md_store_remove(reg->store, MD_SG_NONE, NULL, MD_FN_HTTPD_JSON,
+                              p, TRUE);
+        if (rv2 != APR_SUCCESS)
+          md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, p,
+                        "error removing md/%s", MD_FN_HTTPD_JSON);
+        else
+          rv = APR_SUCCESS;
+    }
     return rv;
 }
 
@@ -222,11 +234,16 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
     const md_cert_t *cert;
     const md_pkey_spec_t *spec;
     apr_status_t rv = APR_SUCCESS;
-    int i;
+    int i, is_static = (md->cert_files && md->cert_files->nelts);
 
     if (md->renew_window == NULL) md->renew_window = reg->renew_window;
     if (md->warn_window == NULL) md->warn_window = reg->warn_window;
 
+    if(is_static) {
+      if(md->renew_mode == MD_RENEW_AUTO)
+        md->renew_mode = MD_RENEW_MANUAL;
+    }
+
     if (md->domains && md->domains->pool != p) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
                       "md{%s}: state_init called with foreign pool", md->name);
@@ -860,12 +877,24 @@ apr_status_t md_reg_sync_start(md_reg_t *reg, apr_array_header_t *master_mds, ap
         idx = md_array_str_index(ctx.store_names, md->name, 0, 1);
         if (idx < 0) {
             APR_ARRAY_PUSH(ctx.maybe_new_mds, md_t*) = md;
+        }
+        else {
             md_array_remove_at(ctx.store_names, idx);
         }
     }
     
-    if (ctx.maybe_new_mds->nelts == 0) goto leave; /* none new */
-    if (ctx.store_names->nelts == 0) goto leave;   /* all new */
+    if (ctx.maybe_new_mds->nelts == 0) {
+        /* none new */
+        goto leave;
+    }
+    if (ctx.store_names->nelts == 0) {
+        /* all new */
+        for (i = 0; i < ctx.maybe_new_mds->nelts; ++i) {
+            md = APR_ARRAY_IDX(ctx.maybe_new_mds, i, md_t*);
+            APR_ARRAY_PUSH(ctx.new_mds, md_t*) = md;
+        }
+        goto leave;
+    }
     
     md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
                   "sync MDs, %d potentially new MDs detected, looking for renames among "
index 936c65349f9b2ed103da7a37a36cae2bf9c65b6a..5490e770108b22757657b9a20ef661718f9bae8c 100644 (file)
@@ -47,12 +47,21 @@ static apr_status_t status_get_cert_json(md_json_t **pjson, const md_cert_t *cer
     apr_status_t rv = APR_SUCCESS;
     md_timeperiod_t valid;
     md_json_t *json;
-    
+    const char *issuer_uri, *issuer_name;
+
+
     json = md_json_create(p);
+    issuer_name = md_cert_get_issuer_name(cert, p);
+    if (issuer_name)
+      md_json_sets(issuer_name, json, MD_KEY_ISSUER_NAME, NULL);
+    rv = md_cert_get_issuers_uri(&issuer_uri, cert, p);
+    if(rv == APR_SUCCESS && issuer_uri)
+      md_json_sets(issuer_uri, json, MD_KEY_ISSUER_URI, NULL);
     valid.start = md_cert_get_not_before(cert);
     valid.end = md_cert_get_not_after(cert);
     md_json_set_timeperiod(&valid, json, MD_KEY_VALID, NULL);
     md_json_sets(md_cert_get_serial_number(cert, p), json, MD_KEY_SERIAL, NULL);
+    md_json_sets(md_cert_get_serial_number(cert, p), json, MD_KEY_SERIAL, NULL);
     if (APR_SUCCESS != (rv = md_cert_to_sha256_fingerprint(&finger, cert, p))) goto leave;
     md_json_sets(finger, json, MD_KEY_SHA256_FINGERPRINT, NULL);
 
index 35c24b4180435a2b021894fd7e49fc5f1641ee48..77063bff703071251dcef794267a0d8042acab56 100644 (file)
@@ -275,6 +275,15 @@ static apr_status_t setup_store_file(void *baton, apr_pool_t *p, apr_pool_t *pte
 read:
     if (MD_OK(md_util_is_file(fname, ptemp))) {
         rv = read_store_file(s_fs, fname, p, ptemp);
+        if (rv != APR_SUCCESS) {
+            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p,
+            "The central store file md/md_store.json seems to exist, but "
+            "its content are not readable as JSON. Either it got somehow corrupted "
+            "or the store directory was configured for a location with a foreign "
+            "md_store.json file. Either way, it is unclear how to proceeed. "
+            "You should either restore the correct file/location or clean the directory "
+            "so it gets initialized again.");
+        }
     }
     else if (APR_STATUS_IS_ENOENT(rv)
         && APR_STATUS_IS_EEXIST(rv = init_store_file(s_fs, fname, p, ptemp))) {
@@ -511,7 +520,6 @@ static apr_status_t mk_group_dir(const char **pdir, md_store_fs_t *s_fs,
     
     perms = gperms(s_fs, group);
 
-    *pdir = NULL;
     rv = fs_get_dname(pdir, &s_fs->s, group, name, p);
     if ((APR_SUCCESS != rv) || (MD_SG_NONE == group)) goto cleanup;
 
@@ -531,7 +539,7 @@ static apr_status_t mk_group_dir(const char **pdir, md_store_fs_t *s_fs,
 cleanup:
     if (APR_SUCCESS != rv) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "mk_group_dir %d %s",
-            group, (*pdir? *pdir : (name? name : "(null)")));
+                      group, (*pdir? *pdir : (name? name : "(null)")));
     }
     return rv;
 }
index 2c3b66bc28e946bf599cb7b4250283ed168d16c8..609cc93ec3d1343669672a366d2eb2e4b0a0a622 100644 (file)
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "2.4.31"
+#define MOD_MD_VERSION "2.5.1"
 
 /**
  * @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 0x02041f
+#define MOD_MD_VERSION_NUM 0x020501
 
 #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 6d3f5b791d100fb593bb6f6e197619446bada73e..c34aeb2909a162a1f7166297ba913935147013ab 100644 (file)
@@ -363,6 +363,12 @@ static void merge_srv_config(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
     if (md->stapling < 0) {
         md->stapling = md_config_geti(md->sc, MD_CONFIG_STAPLING);
     }
+    if (!md->profile) {
+        md->profile = md_config_gets(md->sc, MD_CONFIG_CA_PROFILE);
+    }
+    if (md->profile_mandatory < 0) {
+        md->profile_mandatory = md_config_geti(md->sc, MD_CONFIG_CA_PROFILE_MANDATORY);
+    }
 }
 
 static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s,
@@ -945,7 +951,8 @@ static apr_status_t md_post_config_before_ssl(apr_pool_t *p, apr_pool_t *plog,
     /*5*/
     md_reg_load_stagings(mc->reg, mc->mds, mc->env, p);
 leave:
-    md_reg_unlock_global(mc->reg, ptemp);
+    if (mc->reg)
+        md_reg_unlock_global(mc->reg, ptemp);
     return rv;
 }
 
@@ -1256,7 +1263,7 @@ static int md_add_cert_files(server_rec *s, apr_pool_t *p,
                          s->server_hostname);
         }
         ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
-                     "host '%s' is covered by a Managed Domaina and "
+                     "host '%s' is covered by a Managed Domain and "
                      "is being provided with %d key/certificate files.",
                      s->server_hostname, md_cert_files->nelts);
         apr_array_cat(cert_files, md_cert_files);
@@ -1514,7 +1521,7 @@ static void md_hooks(apr_pool_t *pool)
     /* Run once after configuration is set, before mod_ssl.
      * Run again after mod_ssl is done.
      */
-    ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_MIDDLE);
+    ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_FIRST);
     ap_hook_post_config(md_post_config_after_ssl, mod_ssl, mod_wd, APR_HOOK_LAST);
 
     /* Run once after a child process has been created.
index cdd1e297c74b99b373b0fb97d16c98b4da0a5f20..413f1e8e99bfa74902d16bedcc595af23a085843 100644 (file)
@@ -120,6 +120,8 @@ static md_srv_conf_t defconf = {
     NULL,                      /* ca challenges array */
     NULL,                      /* ca eab kid */
     NULL,                      /* ca eab hmac */
+    NULL,                      /* ACME profile */
+    0,                         /* ACME profile mandatory */
     0,                         /* stapling */
     1,                         /* staple others */
     NULL,                      /* dns01_cmd */
@@ -175,6 +177,8 @@ static void srv_conf_props_clear(md_srv_conf_t *sc)
     sc->ca_challenges = NULL;
     sc->ca_eab_kid = NULL;
     sc->ca_eab_hmac = NULL;
+    sc->profile = NULL;
+    sc->profile_mandatory = DEF_VAL;
     sc->stapling = DEF_VAL;
     sc->staple_others = DEF_VAL;
     sc->dns01_cmd = NULL;
@@ -196,6 +200,8 @@ static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
     to->ca_challenges = from->ca_challenges;
     to->ca_eab_kid = from->ca_eab_kid;
     to->ca_eab_hmac = from->ca_eab_hmac;
+    to->profile = from->profile;
+    to->profile_mandatory = from->profile_mandatory;
     to->stapling = from->stapling;
     to->staple_others = from->staple_others;
     to->dns01_cmd = from->dns01_cmd;
@@ -221,6 +227,8 @@ static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t
     if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges);
     if (from->ca_eab_kid) md->ca_eab_kid = from->ca_eab_kid;
     if (from->ca_eab_hmac) md->ca_eab_hmac = from->ca_eab_hmac;
+    if (from->profile) md->profile = from->profile;
+    if (from->profile_mandatory != DEF_VAL) md->profile_mandatory = from->profile_mandatory;
     if (from->stapling != DEF_VAL) md->stapling = from->stapling;
     if (from->dns01_cmd) md->dns01_cmd = from->dns01_cmd;
 }
@@ -266,6 +274,8 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
                     : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL));
     nsc->ca_eab_kid = add->ca_eab_kid? add->ca_eab_kid : base->ca_eab_kid;
     nsc->ca_eab_hmac = add->ca_eab_hmac? add->ca_eab_hmac : base->ca_eab_hmac;
+    nsc->profile = add->profile? add->profile : base->profile;
+    nsc->profile_mandatory = (add->profile_mandatory != DEF_VAL)? add->profile_mandatory : base->profile_mandatory;
     nsc->stapling = (add->stapling != DEF_VAL)? add->stapling : base->stapling;
     nsc->staple_others = (add->staple_others != DEF_VAL)? add->staple_others : base->staple_others;
     nsc->dns01_cmd = (add->dns01_cmd)? add->dns01_cmd : base->dns01_cmd;
@@ -579,6 +589,31 @@ static const char *md_config_set_renew_mode(cmd_parms *cmd, void *dc, const char
     return NULL;
 }
 
+static const char *md_config_set_profile(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) {
+        return err;
+    }
+    config->profile = value;
+    return NULL;
+}
+
+static const char *md_config_set_profile_mandatory(cmd_parms *cmd, void *dc, const char *value)
+{
+    md_srv_conf_t *config = md_config_get(cmd->server);
+    const char *err;
+
+    (void)dc;
+    if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) {
+        return err;
+    }
+    return set_on_off(&config->profile_mandatory, value, cmd->pool);
+}
+
 static const char *md_config_set_must_staple(cmd_parms *cmd, void *dc, const char *value)
 {
     md_srv_conf_t *config = md_config_get(cmd->server);
@@ -1325,6 +1360,10 @@ const command_rec md_cmds[] = {
                   "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("MDProfile", md_config_set_profile, NULL, RSRC_CONF,
+                  "The name of an CA profile to order certificates for."),
+    AP_INIT_TAKE1("MDProfileMandatory", md_config_set_profile_mandatory, NULL, RSRC_CONF,
+                  "Determines if a configured CA profile is mandatory."),
     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 };
 
@@ -1395,6 +1434,8 @@ const char *md_config_gets(const md_srv_conf_t *sc, md_config_var_t var)
             return sc->ca_agreement? sc->ca_agreement : defconf.ca_agreement;
         case MD_CONFIG_NOTIFY_CMD:
             return sc->mc->notify_cmd;
+        case MD_CONFIG_CA_PROFILE:
+            return sc->profile? sc->profile : defconf.profile;
         default:
             return NULL;
     }
@@ -1415,6 +1456,8 @@ int md_config_geti(const md_srv_conf_t *sc, md_config_var_t var)
             return (sc->stapling != DEF_VAL)? sc->stapling : defconf.stapling;
         case MD_CONFIG_STAPLE_OTHERS:
             return (sc->staple_others != DEF_VAL)? sc->staple_others : defconf.staple_others;
+        case MD_CONFIG_CA_PROFILE_MANDATORY:
+            return (sc->profile_mandatory != DEF_VAL)? sc->profile_mandatory : defconf.profile_mandatory;
         default:
             return 0;
     }
index 1ce2375f00de09da8e60d305d80433721db7e1ff..48272cfdf13239a106262fe3c2100097b72db389 100644 (file)
@@ -39,6 +39,8 @@ typedef enum {
     MD_CONFIG_MESSGE_CMD,
     MD_CONFIG_STAPLING,
     MD_CONFIG_STAPLE_OTHERS,
+    MD_CONFIG_CA_PROFILE,
+    MD_CONFIG_CA_PROFILE_MANDATORY,
 } md_config_var_t;
 
 typedef enum {
@@ -103,6 +105,8 @@ typedef struct md_srv_conf_t {
     struct apr_array_header_t *ca_challenges; /* challenge types configured */
     const char *ca_eab_kid;            /* != NULL, external account binding keyid */
     const char *ca_eab_hmac;           /* != NULL, external account binding hmac */
+    const char *profile;               /* != NULL, ACME order profile */
+    int profile_mandatory;             /* if ACME profile, when set, is mandatory */
 
     int stapling;                      /* OCSP stapling enabled */
     int staple_others;                 /* Provide OCSP stapling for non-MD certificates */
index 0118de5e1339705855a28f157082af4637b3dab5..defa649376ffbd87f0af2a9e171bb841cc2a1547 100755 (executable)
@@ -5,12 +5,11 @@ import pytest
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
-from .md_conf import HttpdConf
 from .md_env import MDTestEnv
 from .md_acme import MDPebbleRunner, MDBoulderRunner
 
 
-def pytest_report_header(config, startdir):
+def pytest_report_header(config):
     env = MDTestEnv()
     return "mod_md: [apache: {aversion}({prefix}), mod_{ssl}, ACME server: {acme}]".format(
         prefix=env.prefix,
index dd5bee5ea82cb88a9c0b9dec4defb8a643303825..972e1b4e6674d15dd9e8e049667674d5483922b6 100644 (file)
     "externalAccountMACKeys": {
       "kid-1": "zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W",
       "kid-2": "b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH"
-    }
+    },
+    "profiles": {
+       "default": {
+         "description": "The profile you know and love",
+         "validityPeriod": 7776000
+       },
+       "shortlived": {
+         "description": "A short-lived cert profile, without actual enforcement",
+         "validityPeriod": 518400
+       }
+     }
   }
 }
index 9c41271785a8d94e6c9200c91a7b4a1ba7688284..2b97289c5372a6ef87253678a4f5f9c682fe61b5 100644 (file)
@@ -7,6 +7,21 @@
     "httpPort": ${http_port},
     "tlsPort": ${https_port},
     "ocspResponderURL": "",
-    "externalAccountBindingRequired": false
+    "externalAccountBindingRequired": false,
+    "domainBlocklist": ["blocked-domain.example"],
+    "retryAfter": {
+        "authz": 3,
+        "order": 5
+    },
+    "profiles": {
+       "default": {
+         "description": "The profile you know and love",
+         "validityPeriod": 7776000
+       },
+       "shortlived": {
+         "description": "A short-lived cert profile, without actual enforcement",
+         "validityPeriod": 518400
+       }
+     }
   }
 }
index d734b29b38ec26fc2f07db9372e6b0106cf0c1c5..1872f471077eff75403225b375dba2813850bc4d 100644 (file)
@@ -13,7 +13,7 @@ class TestStoreMigrate:
     @pytest.fixture(autouse=True, scope='class')
     def _class_scope(self, env):
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # install old store, start a2md list, check files afterwards
     def test_md_010_000(self, env):
index 97f093ebc8eee42fe2cac603e65c15d4973462b6..b99c745f2a5f61eb85fc973331cd752105c6bb15 100644 (file)
@@ -19,7 +19,7 @@ class TestAcmeAcc:
         env.check_acme()
         env.APACHE_CONF_SRC = "data/test_drive"
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env):
index 88df1683413973241e5a27675603966d4933d52d..dc65aa206909d25c271a01d6fa3c15e4208738f6 100644 (file)
@@ -1,4 +1,5 @@
 # test mod_md basic configurations
+import os
 
 import re
 import time
@@ -17,14 +18,19 @@ class TestConf:
     @pytest.fixture(autouse=True, scope='class')
     def _class_scope(self, env, acme):
         acme.start(config='default')
+        env.purge_store()
+
+    @pytest.fixture(autouse=True, scope='function')
+    def _method_scope(self, env, request):
         env.clear_store()
+        self.test_domain = env.get_request_domain(request)
 
     # test case: just one MDomain definition
     def test_md_300_001(self, env):
         MDConf(env, text="""
             MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -38,7 +44,7 @@ class TestConf:
             MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org
             MDomain example2.org www.example2.org mail.example2.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -84,7 +90,7 @@ class TestConf:
                 MDomain example2.org www.example2.org www.example3.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -101,7 +107,7 @@ class TestConf:
                 MDomain example2.org www.example2.org www.example3.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -121,7 +127,7 @@ class TestConf:
                 ServerName www.example2.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -144,7 +150,7 @@ class TestConf:
                 ServerAlias example2.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -186,7 +192,7 @@ class TestConf:
             </VirtualHost>
             """)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: MDomain, misses one ServerAlias
     def test_md_300_011a(self, env):
@@ -216,7 +222,7 @@ class TestConf:
                 ServerAlias test4.not-forbidden.org
             </VirtualHost>
             """ % env.https_port).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: MDomain does not match any vhost
     def test_md_300_012(self, env):
@@ -227,7 +233,7 @@ class TestConf:
                 ServerAlias test3.not-forbidden.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -246,18 +252,19 @@ class TestConf:
                 ServerName test-b.example2.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: global server name as managed domain name
     def test_md_300_014(self, env):
         MDConf(env, text=f"""
             MDomain www.{env.http_tld} www.example2.org
+            MDRenewMode manual
 
             <VirtualHost *:12346>
                 ServerName www.example2.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: valid pkey specification
     def test_md_300_015(self, env):
@@ -267,8 +274,9 @@ class TestConf:
             MDPrivateKeys RSA 2048
             MDPrivateKeys RSA 3072
             MDPrivateKeys RSA 4096
+            MDRenewMode manual
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: invalid pkey specification
     @pytest.mark.parametrize("line,exp_err_msg", [
@@ -355,7 +363,7 @@ class TestConf:
                 ServerName secret.com
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         #
         env.httpd_error_log.ignore_recent(
             lognos = [
@@ -431,7 +439,7 @@ class TestConf:
             </VirtualHost>
             """)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: configure more than 1 CA
     @pytest.mark.parametrize("cas, should_work", [
@@ -493,7 +501,7 @@ class TestConf:
         # It works, if we only match on ServerNames
         conf.add("MDMatchNames servernames")
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.httpd_error_log.ignore_recent(
             lognos=[
                 "AH10040",  # ServerAlias not covered
@@ -537,7 +545,7 @@ class TestConf:
         # It works, if we only match on ServerNames
         conf.add("MDMatchNames servernames")
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         time.sleep(2)
         assert env.apache_stop() == 0
         # we need dns-01 challenge for the wildcard, which is not configured
@@ -545,3 +553,45 @@ class TestConf:
             r'.*None of offered challenge types.*are supported.*'
         ])
 
+    # test case: corrupted md/httpd.json, see #369
+    def test_md_300_030(self, env):
+        domain = self.test_domain
+        domains = [domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("manual")
+        conf.add_md(domains)
+        conf.add_vhost(domain)
+        conf.install()
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        with open(os.path.join(env.store_dir, 'httpd.json'), 'w') as fd:
+            fd.write('garbage\n')
+        # self-repairing now
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        env.httpd_error_log.ignore_recent(matches=[
+            r'.*failed to load JSON file.*'
+        ])
+
+    # test case: corrupted md/md_store.json, related to #369
+    def test_md_300_031(self, env):
+        env.purge_store()
+        domain = self.test_domain
+        domains = [domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("manual")
+        conf.add_md(domains)
+        conf.add_vhost(domain)
+        conf.install()
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        with open(os.path.join(env.store_dir, 'md_store.json'), 'w') as fd:
+            fd.write('garbage\n')
+        # not self-repairing, failing to start
+        r = env.apache_restart()
+        env.purge_store()
+        assert r != 0, f'{env.apachectl_stderr}'
+        env.httpd_error_log.ignore_recent(matches=[
+            r'.*failed to load JSON file.*',
+            r'.*init fs store at .*',
+            f'.* The central store file .* seems to exist, but its content are not readable.*',
+        ], lognos=[
+            "AH10046" # setup store
+        ])
\ No newline at end of file
index f2bb9c723aced8bfea2ec285ae8c80cfcc005b7d..992a63027726a853a49461ce7c04cf74958dfa05 100644 (file)
@@ -30,7 +30,7 @@ class TestConf:
     # test case: no md definitions in config
     def test_md_310_001(self, env):
         MDConf(env, text="").install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         r = env.a2md(["list"])
         assert 0 == len(r.json["output"])
 
@@ -45,7 +45,7 @@ class TestConf:
     ])
     def test_md_310_100(self, env, confline, dns_lists, md_count):
         MDConf(env, text=confline).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         for i in range(0, len(dns_lists)):
             env.check_md(dns_lists[i], state=1)
 
@@ -54,13 +54,13 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDomain testdomain2.org www.testdomain2.org mail.testdomain2.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         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)
 
@@ -70,7 +70,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
 
     # test case: add new md definition with acme url, acme protocol, acme agreement
@@ -82,7 +82,7 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """, local_ca=False).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         name = "testdomain.org"
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      ca="http://acme.test.org:4000/directory", protocol="ACME",
@@ -94,7 +94,7 @@ class TestConf:
         MDConf(env, local_ca=False, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      ca="https://acme-v02.api.letsencrypt.org/directory", protocol="ACME")
         MDConf(env, local_ca=False, text="""
@@ -104,7 +104,7 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         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")
@@ -114,7 +114,7 @@ class TestConf:
         MDConf(env, admin="admin@testdomain.org", text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         name = "testdomain.org"
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      contacts=["mailto:admin@testdomain.org"])
@@ -126,7 +126,7 @@ class TestConf:
         MDConf(env, admin="admin@testdomain.org", text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      contacts=["mailto:admin@testdomain.org"])
 
@@ -148,7 +148,7 @@ class TestConf:
                 ServerAdmin mailto:admin@testdomain2.org
             </VirtualHost>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         name1 = "testdomain.org"
         name2 = "testdomain2.org"
         env.check_md([name1, "www." + name1, "mail." + name1], state=1, contacts=["mailto:admin@" + name1])
@@ -159,7 +159,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org WWW.testdomain.org MAIL.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
 
     # test case: default drive mode - auto
@@ -167,7 +167,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1
 
     # test case: drive mode manual
@@ -176,7 +176,7 @@ class TestConf:
             MDRenewMode manual
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 0
 
     # test case: drive mode auto
@@ -185,7 +185,7 @@ class TestConf:
             MDRenewMode auto
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1
 
     # test case: drive mode always
@@ -194,7 +194,7 @@ class TestConf:
             MDRenewMode always
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 2
 
     # test case: renew window - 14 days
@@ -203,7 +203,7 @@ class TestConf:
             MDRenewWindow 14d
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-window'] == '14d'
 
     # test case: renew window - 10 percent
@@ -212,7 +212,7 @@ class TestConf:
             MDRenewWindow 10%
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-window'] == '10%'
         
     # test case: ca challenge type - http-01
@@ -221,7 +221,7 @@ class TestConf:
             MDCAChallenges http-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01']
 
     # test case: ca challenge type - http-01
@@ -230,7 +230,7 @@ class TestConf:
             MDCAChallenges tls-alpn-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['tls-alpn-01']
 
     # test case: ca challenge type - all
@@ -239,7 +239,7 @@ class TestConf:
             MDCAChallenges http-01 tls-alpn-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01', 'tls-alpn-01']
 
     # test case: automatically collect md names from vhost config
@@ -252,7 +252,7 @@ class TestConf:
             "testdomain.org", "test.testdomain.org", "mail.testdomain.org",
         ], with_ssl=True)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['domains'] == \
                ['testdomain.org', 'test.testdomain.org', 'mail.testdomain.org']
 
@@ -261,12 +261,12 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         MDConf(env, text="""
             MDRenewWindow 14d
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status("testdomain.org")
         assert stat['renew-window'] == '14d'
 
@@ -276,7 +276,7 @@ class TestConf:
             MDPrivateKeys RSA 2048
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey'] == {
             "type": "RSA",
             "bits": 2048
@@ -288,7 +288,7 @@ class TestConf:
             MDPrivateKeys RSA 4096
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey'] == {
             "type": "RSA",
             "bits": 4096
@@ -300,7 +300,7 @@ class TestConf:
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDRequireHttps temporary
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['require-https'] == "temporary"
 
     # test case: require OCSP stapling
@@ -309,7 +309,7 @@ class TestConf:
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDMustStaple on
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is True
 
     # test case: remove managed domain from config
@@ -319,7 +319,7 @@ class TestConf:
         env.check_md(dns_list, state=1)
         conf = MDConf(env,)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: md stays in store
         env.check_md(dns_list, state=1)
 
@@ -331,7 +331,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: DNS has been removed from md in store
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
 
@@ -343,7 +343,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: md overwrite previous name and changes name
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"],
                      md="testdomain.org", state=1)
@@ -359,7 +359,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # all mds stay in store
         env.check_md(dns_list1, state=1)
         env.check_md(dns_list2, state=1)
@@ -374,12 +374,12 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: sync with ca info removed
         MDConf(env, local_ca=False, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      ca="https://acme-v02.api.letsencrypt.org/directory", protocol="ACME")
 
@@ -389,12 +389,12 @@ class TestConf:
         MDConf(env, admin="admin@testdomain.org", text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: sync with admin info removed
         MDConf(env, admin="", text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # 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"])
@@ -405,12 +405,12 @@ class TestConf:
             MDRenewWindow 14d
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-window'] == '14d'
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: renew window not set
         assert env.a2md(["list"]).json['output'][0]['renew-window'] == '33%'
 
@@ -425,13 +425,13 @@ class TestConf:
             MDRenewMode %s
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """ % renew_mode).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == exp_code
         #
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1
 
     # test case: remove challenges from conf -> fallback to default (not set)
@@ -440,13 +440,13 @@ class TestConf:
             MDCAChallenges http-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01']
         #
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert 'challenges' not in env.a2md(["list"]).json['output'][0]['ca']
 
     # test case: specify RSA key
@@ -456,13 +456,13 @@ class TestConf:
             MDPrivateKeys RSA %s
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """ % key_size).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey']['type'] == "RSA"
         #
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert "privkey" not in env.a2md(["list"]).json['output'][0]
 
     # test case: require HTTPS
@@ -474,14 +474,14 @@ class TestConf:
                 MDRequireHttps %s
             </MDomainSet>
             """ % mode).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['require-https'] == mode, \
             "Unexpected HTTPS require mode in store. config: {}".format(mode)
         #
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert "require-https" not in env.a2md(["list"]).json['output'][0], \
             "HTTPS require still persisted in store. config: {}".format(mode)
 
@@ -491,13 +491,13 @@ class TestConf:
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDMustStaple on
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is True
         #
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is False
 
     # test case: reorder DNS names in md definition
@@ -508,7 +508,7 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: dns list changes
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
 
@@ -523,7 +523,7 @@ class TestConf:
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDomain testdomain2.org www.testdomain2.org mail.testdomain2.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         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)
 
@@ -537,7 +537,7 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: sync with changed ca info
         MDConf(env, local_ca=False, admin="webmaster@testdomain.org",
                   text="""
@@ -547,7 +547,7 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: md stays the same with previous ca info
         env.check_md([name, "www.testdomain.org", "mail.testdomain.org"], state=1,
                      ca="http://somewhere.com:6666/directory", protocol="ACME",
@@ -559,7 +559,7 @@ class TestConf:
         MDConf(env, admin="admin@testdomain.org", text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: sync with changed admin info
         MDConf(env, local_ca=False, admin="webmaster@testdomain.org", text="""
             MDCertificateAuthority http://somewhere.com:6666/directory
@@ -568,7 +568,7 @@ class TestConf:
 
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # 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"])
@@ -579,21 +579,21 @@ class TestConf:
             MDRenewMode manual
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 0
         # test case: drive mode auto
         MDConf(env, text="""
             MDRenewMode auto
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1
         # test case: drive mode always
         MDConf(env, text="""
             MDRenewMode always
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 2
 
     # test case: change config value for renew window, use various syntax alternatives
@@ -602,21 +602,21 @@ class TestConf:
             MDRenewWindow 14d
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.a2md(["list"]).json['output'][0]
         assert md['renew-window'] == '14d'
         MDConf(env, text="""
             MDRenewWindow 10
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.a2md(["list"]).json['output'][0]
         assert md['renew-window'] == '10d'
         MDConf(env, text="""
             MDRenewWindow 10%
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.a2md(["list"]).json['output'][0]
         assert md['renew-window'] == '10%'
 
@@ -626,21 +626,21 @@ class TestConf:
             MDCAChallenges http-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01']
         # test case: drive mode auto
         MDConf(env, text="""
             MDCAChallenges tls-alpn-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['tls-alpn-01']
         # test case: drive mode always
         MDConf(env, text="""
             MDCAChallenges http-01 tls-alpn-01
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['ca']['challenges'] == ['http-01', 'tls-alpn-01']
 
     # test case:  RSA key length: 4096 -> 2048 -> 4096
@@ -649,7 +649,7 @@ class TestConf:
             MDPrivateKeys RSA 4096
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey'] == {
             "type": "RSA",
             "bits": 4096
@@ -658,7 +658,7 @@ class TestConf:
             MDPrivateKeys RSA 2048
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey'] == {
             "type": "RSA",
             "bits": 2048
@@ -667,7 +667,7 @@ class TestConf:
             MDPrivateKeys RSA 4096
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['privkey'] == {
             "type": "RSA",
             "bits": 4096
@@ -679,14 +679,14 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert "require-https" not in env.a2md(["list"]).json['output'][0]
         # test case: temporary redirect
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDRequireHttps temporary
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['require-https'] == "temporary"
         # test case: permanent redirect
         MDConf(env, text="""
@@ -695,7 +695,7 @@ class TestConf:
                 MDRequireHttps permanent
             </MDomainSet>
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['require-https'] == "permanent"
 
     # test case: change OCSP stapling settings on existing md
@@ -704,21 +704,21 @@ class TestConf:
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is False
         # test case: OCSP stapling on
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDMustStaple on
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is True
         # test case: OCSP stapling off
         MDConf(env, text="""
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             MDMustStaple off
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'][0]['must-staple'] is False
 
     # test case: change renew window parameter
@@ -735,7 +735,7 @@ class TestConf:
         conf.end_md()
         conf.add_vhost(domains=domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain)
         assert stat["renew-window"] == window
 
@@ -748,7 +748,7 @@ class TestConf:
         assert env.a2md(["update", name, "contacts", "admin@" + name]).exit_code == 0
         assert env.a2md(["update", name, "agreement", env.acme_tos]).exit_code == 0
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
         # setup: drive it
         r = env.a2md(["-v", "drive", name])
@@ -771,7 +771,7 @@ class TestConf:
         assert env.a2md(["add", name]).exit_code == 0
         assert env.a2md(["update", name, "contacts", "admin@" + name]).exit_code == 0
         assert env.a2md(["update", name, "agreement", env.acme_tos]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: drive it
         assert env.a2md(["drive", name]).exit_code == 0
         assert env.a2md(["list", name]).json['output'][0]['state'] == env.MD_S_COMPLETE
@@ -786,7 +786,7 @@ class TestConf:
             MDStoreDir md-other
             MDomain testdomain.org www.testdomain.org mail.testdomain.org
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list"]).json['output'] == []
         env.set_store_dir("md-other")
         env.check_md(["testdomain.org", "www.testdomain.org", "mail.testdomain.org"], state=1)
@@ -802,13 +802,13 @@ class TestConf:
         conf.end_md()
         conf.add_vhost(domains=[domain])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # add a file at top level
         assert env.await_completion([domain])
         fpath = os.path.join(env.store_domains(), "wrong.com")
         with open(fpath, 'w') as fd:
             fd.write("this does not belong here\n")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # test case: add external account binding
     def test_md_310_601(self, env):
@@ -821,7 +821,7 @@ class TestConf:
         conf.end_md()
         conf.add_vhost(domains=domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain)
         assert stat["eab"] == {'kid': 'k123', 'hmac': '***'}
         # eab inherited
@@ -832,7 +832,7 @@ class TestConf:
         conf.end_md()
         conf.add_vhost(domains=domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain)
         assert stat["eab"] == {'kid': 'k456', 'hmac': '***'}
         # override eab inherited
@@ -844,7 +844,7 @@ class TestConf:
         conf.end_md()
         conf.add_vhost(domains=domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain)
         assert "eab" not in stat
 
index b064647450e4dd66fdf3a0f7077c76a47ab966ba..484e4d4bd95d2a6cf3ce24bbe7ee3f35661dc053 100644 (file)
@@ -25,7 +25,7 @@ class TestDrivev2:
         env.check_acme()
         env.APACHE_CONF_SRC = "data/test_drive"
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -76,7 +76,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # drive
         prev_md = env.a2md(["list", name]).json['output'][0]
         r = env.a2md(["-vv", "drive", "-c", "http-01", name])
@@ -117,7 +117,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name, "test." + domain])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # drive
         r = env.a2md(["-vv", "drive", "-c", "http-01", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
@@ -132,7 +132,7 @@ class TestDrivev2:
         name = "www." + domain
         assert env.a2md(["add", name]).exit_code == 0
         assert env.a2md(["update", name, "contacts", "admin@" + domain]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: create account on server
         r = env.a2md(["-t", "accepted", "acme", "newreg", "admin@" + domain], raw=True)
         assert r.exit_code == 0
@@ -152,7 +152,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: create account on server
         r = env.a2md(["-t", "accepted", "acme", "newreg", "test@" + domain], raw=True)
         assert r.exit_code == 0
@@ -172,7 +172,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # drive
         r = env.a2md(["-vv", "drive", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
@@ -204,7 +204,7 @@ class TestDrivev2:
         conf = MDConf(env, proxy=True)
         conf.add('LogLevel proxy:trace8')
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
         # drive it, with wrong proxy url -> FAIL
         r = env.a2md(["-p", "http://localhost:1", "drive", name])
@@ -231,11 +231,11 @@ class TestDrivev2:
         # setup: create resource files
         self._write_res_file(os.path.join(env.server_docs_dir, "test"), "name.txt", name)
         self._write_res_file(os.path.join(env.server_docs_dir), "name.txt", "not-forbidden.org")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
         # drive it
         assert env.a2md(["drive", name]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # test HTTP access - no redirect
         jdata = env.get_json_content(f"test1.{env.http_tld}", "/alive.json", use_https=False)
         assert jdata['host']== "test1"
@@ -249,7 +249,7 @@ class TestDrivev2:
         # test HTTP access again -> redirect to default HTTPS port
         conf.add("MDRequireHttps temporary")
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         r = env.get_meta(name, "/name.txt", use_https=False)
         assert r.response['status'] == 302
         exp_location = "https://%s/name.txt" % name
@@ -266,7 +266,7 @@ class TestDrivev2:
         # test HTTP access again -> redirect permanent
         conf.add("MDRequireHttps permanent")
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         r = env.get_meta(name, "/name.txt", use_https=False)
         assert r.response['status'] == 301
         exp_location = "https://%s/name.txt" % name
@@ -288,15 +288,15 @@ class TestDrivev2:
         conf.add_vhost(name, port=env.http_port)
         conf.add_vhost(name)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # drive it
         assert env.a2md(["drive", name]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
         # test override HSTS header
         conf.add('Header set Strict-Transport-Security "max-age=10886400; includeSubDomains; preload"')
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         r = env.get_meta(name, "/name.txt", use_https=True)
         assert 'strict-transport-security' in r.response['header'], r.response['header']
         assert r.response['header']['strict-transport-security'] == \
@@ -306,7 +306,7 @@ class TestDrivev2:
         conf.add('  Redirect /a /name.txt')
         conf.add('  Redirect seeother /b /name.txt')
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: default redirect by mod_md still works
         exp_location = "https://%s/name.txt" % name
         r = env.get_meta(name, "/name.txt", use_https=False)
@@ -330,17 +330,17 @@ class TestDrivev2:
         conf.add_vhost(name, port=env.http_port)
         conf.add_vhost(name)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # drive it
         r = env.a2md(["-v", "drive", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
         # setup: place redirect rules
         conf.add('  Redirect /a /name.txt')
         conf.add('  Redirect seeother /b /name.txt')
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: redirects on HTTP
         exp_location = "http://%s:%s/name.txt" % (name, env.http_port)
         r = env.get_meta(name, "/a", use_https=False)
@@ -367,12 +367,12 @@ class TestDrivev2:
         conf.add_md([name])
         conf.add_vhost(name)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.run(["openssl", "s_client",
                  f"-connect", "localhost:{env.https_port}",
                  "-servername", "example.com", "-crlf"
                  ], intext="GET https:// HTTP/1.1\nHost: example.com\n\n")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     # --------- critical state change -> drive again ---------
 
@@ -382,7 +382,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: drive it
         r = env.a2md(["drive", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
@@ -419,7 +419,7 @@ class TestDrivev2:
         conf.add_renew_window(renew_window)
         conf.add_md([name])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list", name]).json['output'][0]['state'] == env.MD_S_INCOMPLETE
         # setup: drive it
         r = env.a2md(["drive", name])
@@ -457,7 +457,7 @@ class TestDrivev2:
         conf.add_private_key(key_type, key_params)
         conf.add_md([name])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.a2md(["list", name]).json['output'][0]['state'] == env.MD_S_INCOMPLETE
         # setup: drive it
         r = env.a2md(["-vv", "drive", name])
@@ -477,7 +477,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name, "test." + domain, "xxx." + domain])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: drive it
         r = env.a2md(["drive", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
@@ -496,7 +496,7 @@ class TestDrivev2:
         domain = self.test_domain
         name = "www." + domain
         self._prepare_md(env, [name])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # setup: drive it
         r = env.a2md(["drive", name])
         assert r.exit_code == 0, "a2md drive failed: {0}".format(r.stderr)
index 9ff87e5df7e88361513ed3c5f3ba8194c905eb2e..2d050ae2e0f693c2bb1d811bce9b0738e23ac575 100644 (file)
@@ -39,16 +39,16 @@ class TestRoundtripv2:
         conf.add_md(domains)
         conf.install()
         # - restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # - drive
         assert env.a2md(["-v", "drive", domain]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain)
         # - append vhost to config
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: SSL is running OK
         cert = env.get_cert(domain)
         assert domain in cert.get_san_list()
@@ -71,14 +71,14 @@ class TestRoundtripv2:
         conf.install()
 
         # - restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains_a)
         env.check_md(domains_b)
 
         # - drive
         assert env.a2md(["drive", domain_a]).exit_code == 0
         assert env.a2md(["drive", domain_b]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain_a)
         env.check_md_complete(domain_b)
 
@@ -88,7 +88,7 @@ class TestRoundtripv2:
         conf.install()
 
         # check: SSL is running OK
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         cert_a = env.get_cert(domain_a)
         assert domains_a == cert_a.get_san_list()
         cert_b = env.get_cert(domain_b)
@@ -108,12 +108,12 @@ class TestRoundtripv2:
         conf.install()
         
         # - restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
 
         # - drive
         assert env.a2md(["drive", domain]).exit_code == 0
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain)
 
         # - append vhost to config
@@ -126,7 +126,7 @@ class TestRoundtripv2:
         self._write_res_file(os.path.join(env.server_docs_dir, "b"), "name.txt", name_b)
 
         # check: SSL is running OK
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         cert_a = env.get_cert(name_a)
         assert name_a in cert_a.get_san_list()
         cert_b = env.get_cert(name_b)
index 90103e3aff7c8d3f5e9afeb9604def37978d27cb..df24290f5767dd558066c89409713f3d163fbc14 100644 (file)
@@ -1,4 +1,5 @@
 import os
+import time
 from datetime import timedelta
 
 import pytest
@@ -21,7 +22,7 @@ class TestAutov2:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -44,7 +45,7 @@ class TestAutov2:
         conf.install()
         #
         # restart, check that MD is synched to store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         stat = env.get_md_status(domain)
         assert stat["watched"] == 0
@@ -52,7 +53,7 @@ class TestAutov2:
         # add vhost for MD, restart should drive it
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.check_md_complete(domain)
         stat = env.get_md_status(domain)
@@ -89,7 +90,7 @@ class TestAutov2:
         conf.install()
         #
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains_a)
         env.check_md(domains_b)
         #
@@ -106,7 +107,7 @@ class TestAutov2:
         assert status['state-descr'] == "certificate(rsa) is missing"
 
         # restart and activate
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # check: SSL is running OK
         cert_a = env.get_cert(domain_a)
         assert domains_a == cert_a.get_san_list()
@@ -136,7 +137,7 @@ class TestAutov2:
         self._write_res_file(os.path.join(env.server_docs_dir, "b"), "name.txt", name_b)
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([domain])
         md = env.check_md_complete(domain)
@@ -170,7 +171,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([domain])
         env.check_md_complete(domain)
@@ -196,7 +197,7 @@ class TestAutov2:
         self._write_res_file(os.path.join(env.server_docs_dir, "a"), "name.txt", name_a)
         #
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         #        
         # check: that request to domains give 503 Service Unavailable
@@ -227,7 +228,7 @@ class TestAutov2:
         self._write_res_file(os.path.join(env.server_docs_dir, "a"), "name.txt", name_a)
         #
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         md = env.await_error(domain)
@@ -262,7 +263,7 @@ class TestAutov2:
         conf.install()
         #
         # - restart (-> drive)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # await drive completion
         md = env.await_error(domain)
         assert md
@@ -291,9 +292,9 @@ class TestAutov2:
         conf.install()
         #
         # - restart (-> drive), check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain)
 
     # Force cert renewal due to critical remaining valid duration
@@ -311,30 +312,23 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that md+cert is in store, TLS is up
-        assert env.apache_restart() == 0
-        assert env.await_completion([domain])
-        env.check_md_complete(domain)
-        cert1 = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
-        # compare with what md reports as status
-        stat = env.get_certificate_status(domain)
-        assert cert1.same_serial_as(stat['rsa']['serial'])
-        #
-        # create self-signed cert, with critical remaining valid duration -> drive again
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        assert env.await_completion([domain], restart=False)
+        # Overwrite stages cert with one which has little remaining lifetime
         creds = env.create_self_signed_cert(CertificateSpec(domains=[domain]),
                                             valid_from=timedelta(days=-120),
                                             valid_to=timedelta(days=2),
                                             serial=7029)
-        creds.save_cert_pem(env.store_domain_file(domain, 'pubcert.pem'))
-        creds.save_pkey_pem(env.store_domain_file(domain, 'privkey.pem'))
         assert creds.certificate.serial_number == 7029
-        assert env.apache_restart() == 0
+        creds.save_cert_pem(env.store_staged_file(domain, 'pubcert.pem'))
+        creds.save_pkey_pem(env.store_staged_file(domain, 'privkey.pem'))
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_certificate_status(domain)
         assert creds.certificate.serial_number == int(stat['rsa']['serial'], 16)
-        #
         # cert should renew and be different afterwards
         assert env.await_completion([domain], must_renew=True)
         stat = env.get_certificate_status(domain)
-        creds.certificate.serial_number != int(stat['rsa']['serial'], 16)
+        assert creds.certificate.serial_number != int(stat['rsa']['serial'], 16)
 
     # test case: drive with an unsupported challenge due to port availability 
     def test_md_702_010(self, env):
@@ -348,7 +342,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md["renewal"]["errors"] > 0
         #
@@ -360,7 +354,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([domain])
         #
@@ -386,7 +380,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md["renewal"]["errors"] > 0
         #
@@ -399,7 +393,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([domain])
         #
@@ -431,7 +425,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([name_x])
         env.check_md_complete(name_x)
@@ -451,7 +445,7 @@ class TestAutov2:
         conf.add_vhost(name_b)
         conf.install()
         # restart, check that host still works and kept the cert
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(new_list)
         status = env.get_certificate_status(name_a)
         assert cert_a.same_serial_as(status['rsa']['serial'])
@@ -475,7 +469,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([name_x])
         env.check_md_complete(name_x)
@@ -495,7 +489,7 @@ class TestAutov2:
         conf.add_vhost(name_b)
         conf.install()
         # restart, check that host still works and have new cert
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(new_list)
         assert env.await_completion([name_a])
         #
@@ -520,7 +514,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md([name1])
         env.check_md([name2])
         assert env.await_completion([name1, name2])
@@ -538,7 +532,7 @@ class TestAutov2:
         conf.add_md([name1])
         conf.add_vhost([name1, name2])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md([name1, name2])
         assert env.await_completion([name1])
         #
@@ -563,7 +557,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains1)
         assert env.await_completion([name_x])
         env.check_md_complete(name_x)
@@ -576,7 +570,7 @@ class TestAutov2:
         conf.add_vhost(domains=domains2)
         conf.install()
         # restart, check that host still works and kept the cert
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         status = env.get_certificate_status(name_a)
         assert cert_x.same_serial_as(status['rsa']['serial'])
 
@@ -597,7 +591,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # check that acme-tls/1 is available for all domains
         stat = env.get_md_status(domain)
@@ -625,11 +619,18 @@ class TestAutov2:
         #
         # restart (-> drive), check that MD job shows errors 
         # and that missing proto is detected
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # check that acme-tls/1 is available for none of the domains
         stat = env.get_md_status(domain)
         assert stat["proto"]["acme-tls/1"] == []
+        MDConf(env).install()
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        env.httpd_error_log.ignore_recent(matches=[
+            r'.*None of offered challenge types for domain.*',
+        ], lognos=[
+            "AH10056"  # challenges not available
+        ])
 
     # test case: 2.4.40 mod_ssl stumbles over a SSLCertificateChainFile when installing
     # a fallback certificate
@@ -645,7 +646,7 @@ class TestAutov2:
         conf.add_md(dns_list)
         conf.add_vhost(dns_list)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
 
     # test case: test "tls-alpn-01" without enabling 'acme-tls/1' challenge protocol
@@ -666,7 +667,7 @@ class TestAutov2:
         #
         # restart (-> drive), check that MD job shows errors
         # and that missing proto is detected
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # check that acme-tls/1 is available for none of the domains
         stat = env.get_md_status(domain)
@@ -694,7 +695,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(md_domains)
         assert env.await_completion([domain])
         env.check_md_complete(domain)
@@ -714,7 +715,7 @@ class TestAutov2:
             """)
         conf.add_md([domain])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
 
     # Make a setup using the base server without http:, will fail.
@@ -728,7 +729,7 @@ class TestAutov2:
             """)
         conf.add_md([domain])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         #
         env.httpd_error_log.ignore_recent(
@@ -759,7 +760,7 @@ class TestAutov2:
         ])
         conf.add_md([domain])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain, via_domain=env.http_addr, use_https=False)
         assert stat["proto"]["acme-tls/1"] == [domain]
         assert env.await_completion([domain], via_domain=env.http_addr, use_https=False)
@@ -786,7 +787,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_error(long_domain)
         # add a short domain to the SAN list, the CA should now use that one
@@ -800,7 +801,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([long_domain])
         env.check_md_complete(long_domain)
         #
@@ -823,7 +824,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         env.check_md_complete(domains[0])
 
@@ -842,7 +843,7 @@ class TestAutov2:
         conf.install()
         #
         # restart (-> drive), check that MD was synched and completes
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         env.check_md_complete(domains[0])
 
diff --git a/test/modules/md/test_710_profiles.py b/test/modules/md/test_710_profiles.py
new file mode 100644 (file)
index 0000000..d8fc1a4
--- /dev/null
@@ -0,0 +1,132 @@
+import datetime
+import email.utils
+import os
+from datetime import timedelta
+
+import pytest
+from pyhttpd.certs import CertificateSpec
+
+from pyhttpd.env import HttpdTestEnv
+from .md_cert_util import MDCertUtil
+from .md_env import MDTestEnv
+from .md_conf import MDConf
+
+
+@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
+                    reason="no ACME test server configured")
+class TestProfiles:
+
+    @pytest.fixture(autouse=True, scope='class')
+    def _class_scope(self, env, acme):
+        env.APACHE_CONF_SRC = "data/test_auto"
+        acme.start(config='default')
+        env.check_acme()
+        env.clear_store()
+        MDConf(env).install()
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+
+    @pytest.fixture(autouse=True, scope='function')
+    def _method_scope(self, env, request):
+        env.clear_store()
+        self.test_domain = env.get_request_domain(request)
+
+    def _write_res_file(self, doc_root, name, content):
+        if not os.path.exists(doc_root):
+            os.makedirs(doc_root)
+        open(os.path.join(doc_root, name), "w").write(content)
+
+    # create a MD with 'default' profile, get cert
+    def test_md_710_001(self, env):
+        domain = self.test_domain
+        # generate config with one MD
+        domains = [domain, "www." + domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("auto")
+        conf.start_md(domains)
+        conf.add(f'  MDProfile default')
+        conf.end_md()
+        conf.add_vhost(domains)
+        conf.install()
+        #
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        assert env.await_completion(domains)
+        stat = env.get_md_status(domain)
+        assert stat["watched"] == 1
+        assert stat["profile"] == "default", f'{stat}'
+        assert stat['cert']['rsa']['valid']['until'], f'{stat}'
+        ts = email.utils.parsedate_to_datetime(stat['cert']['rsa']['valid']['until'])
+        valid = ts - datetime.datetime.now(datetime.UTC)
+        assert valid.days in [89, 90]
+
+    # create a MD with 'shortlived' profile, get cert
+    def test_md_710_002(self, env):
+        domain = self.test_domain
+        # generate config with one MD
+        domains = [domain, "www." + domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("auto")
+        conf.start_md(domains)
+        conf.add(f'  MDProfile shortlived')
+        conf.add(f'  MDProfileMandatory on')
+        conf.end_md()
+        conf.add_vhost(domains)
+        conf.install()
+        #
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        assert env.await_completion(domains)
+        stat = env.get_md_status(domain)
+        assert stat["watched"] == 1
+        assert stat["profile"] == "shortlived", f'{stat}'
+        assert stat['cert']['rsa']['valid']['until'], f'{stat}'
+        ts = email.utils.parsedate_to_datetime(stat['cert']['rsa']['valid']['until'])
+        valid = ts - datetime.datetime.now(datetime.UTC)
+        assert valid.days in [5, 6]
+
+    # create a MD with unknown 'XXX' profile, get cert
+    def test_md_710_003(self, env):
+        domain = self.test_domain
+        # generate config with one MD
+        domains = [domain, "www." + domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("auto")
+        conf.start_md(domains)
+        conf.add(f'  MDProfile XXX')
+        conf.end_md()
+        conf.add_vhost(domains)
+        conf.install()
+        #
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        assert env.await_completion(domains)
+        stat = env.get_md_status(domain)
+        assert stat["watched"] == 1
+        assert stat["profile"] == "XXX", f'{stat}'
+
+    # create a MD with unknown 'XXX' profile, mandatory, fail
+    def test_md_710_004(self, env):
+        domain = self.test_domain
+        # generate config with one MD
+        domains = [domain, "www." + domain]
+        conf = MDConf(env, admin="admin@" + domain)
+        conf.add_drive_mode("auto")
+        conf.start_md(domains)
+        conf.add(f'  MDProfile XXX')
+        conf.add(f'  MDProfileMandatory on')
+        conf.end_md()
+        conf.add_vhost(domains)
+        conf.install()
+        #
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        assert env.await_error(domain)
+        stat = env.get_md_status(domain)
+        assert stat["watched"] == 1
+        assert stat["profile"] == "XXX", f'{stat}'
+        assert len(stat['cert']) == 0, f'{stat}'
+        assert stat['renewal']['errors'] > 0, f'{stat}'
+        assert stat['renewal']['last']['activity'] == 'Creating new order', f'{stat}'
+        MDConf(env).install()
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        env.httpd_error_log.ignore_recent(matches=[
+            r'.*mandatory ACME profile \'XXX\' is not offered by CA.*',
+        ], lognos=[
+            "AH10056"  # processing failed
+        ])
index 916c47a5d852801994dd6e1a92e9c28933058431..1930f72fba8b1545b0be5900160af6250c36cc13 100644 (file)
@@ -18,7 +18,7 @@ class TestWildcard:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -37,7 +37,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         md = env.await_error(domain)
@@ -69,7 +69,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         md = env.await_error(domain)
@@ -100,7 +100,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([domain])
@@ -125,7 +125,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         md = env.await_error(domain)
@@ -156,7 +156,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([domain])
@@ -183,7 +183,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([domain])
@@ -211,7 +211,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([domain])
@@ -238,7 +238,7 @@ class TestWildcard:
         conf.install()
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([wwwdomain])
@@ -270,7 +270,7 @@ class TestWildcard:
             fd.write(content)
 
         # restart, check that md is in store
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         # await drive completion
         assert env.await_completion([domain], restart=False)
@@ -278,5 +278,5 @@ class TestWildcard:
         r = env.curl_get(f"http://{domain}:{env.http_port}/.well-known/acme-challenge/123456")
         assert r.response['status'] == 200
         assert r.response['body'] == content
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain)
index 91a5f4445d2bc694bd9fe9ec6644fdd1f8072e23..3c4d26e88c90ae159cb1745a97ef54707fdd68dc 100644 (file)
@@ -19,7 +19,7 @@ class TestStatic:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -48,7 +48,7 @@ class TestStatic:
         conf.end_md()
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         
         # check if the domain uses it, it appears in our stats and renewal is off
         cert = env.get_cert(domain)
@@ -83,7 +83,7 @@ class TestStatic:
         conf.end_md()
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # this should enforce a renewal
         stat = env.get_md_status(domain)
         assert stat['renew'] is True, stat
index 364aaca6c8dffb7786555b3d27d3706dd22e1309..130723ba11b421b0001fd27446dcaf7a7157ee44 100644 (file)
@@ -16,7 +16,7 @@ class TestAcmeErrors:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -33,7 +33,7 @@ class TestAcmeErrors:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md
         assert md['renewal']['errors'] > 0
@@ -65,7 +65,7 @@ class TestAcmeErrors:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md
         assert md['renewal']['errors'] > 0
index 958f13f4d1331958fc87bdd5fc6a8cd96fe0d7a8..d041e970772a242b48e538c5d9db2ffbdc74cd29 100644 (file)
@@ -18,7 +18,7 @@ class TestSetupErrors:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -42,7 +42,7 @@ class TestSetupErrors:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain, errors=2, timeout=10)
         assert md
         assert md['renewal']['errors'] > 0
@@ -65,13 +65,13 @@ class TestSetupErrors:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md(domains)
         assert env.await_completion([domain], restart=False)
         staged_md_path = env.store_staged_file(domain, 'md.json')
         with open(staged_md_path, 'w') as fd:
             fd.write('garbage\n')
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.check_md_complete(domain)
         env.httpd_error_log.ignore_recent(
index aec7e89b8c2542fe7fa6dac0bc8d14f732c595cf..894ac8506ecd81f7c3156d2afcb421c605932751 100644 (file)
@@ -18,7 +18,7 @@ class TestEab:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -33,7 +33,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] == 'urn:ietf:params:acme:error:externalAccountRequired'
@@ -56,7 +56,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] == 'apache:eab-hmac-invalid'
@@ -79,7 +79,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] in [
@@ -105,7 +105,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] in [
@@ -131,7 +131,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] in [
@@ -158,7 +158,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
 
     def test_md_750_011(self, env):
@@ -174,7 +174,7 @@ class TestEab:
         conf.add_md([domain_b])
         conf.add_vhost(domains=[domain_b])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain_a], restart=False)
         md = env.await_error(domain_b)
         assert md['renewal']['errors'] > 0
@@ -202,7 +202,7 @@ class TestEab:
         conf.end_md()
         conf.add_vhost(domains=[domain_b])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain_b], restart=False)
         md = env.await_error(domain_a)
         assert md['renewal']['errors'] > 0
@@ -231,7 +231,7 @@ class TestEab:
         conf.end_md()
         conf.add_vhost(domains=[domain_b])
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain_a, domain_b])
         md_a = env.get_md_status(domain_a)
         md_b = env.get_md_status(domain_b)
@@ -247,7 +247,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         md_1 = env.get_md_status(domain)
         conf = MDConf(env)
@@ -258,7 +258,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         md_2 = env.get_md_status(domain)
         assert md_1['ca'] != md_2['ca']
@@ -273,7 +273,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         conf = MDConf(env)
         # this is another one of the values in conf/pebble-eab.json
@@ -282,7 +282,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
@@ -307,7 +307,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         conf = MDConf(env)
         # this is another one of the values in conf/pebble-eab.json
@@ -317,7 +317,7 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
@@ -343,7 +343,7 @@ class TestEab:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md['renewal']['errors'] > 0
         assert md['renewal']['last']['problem'] == 'urn:ietf:params:acme:error:externalAccountRequired'
@@ -432,5 +432,5 @@ class TestEab:
         conf.add_md(domains)
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
index 5cbd642f36e84dc9cfaacf3936b6cf8f8f712425..f5a7040c308dddbaa2be39ca6d9a2ba109792fa2 100644 (file)
@@ -46,7 +46,7 @@ class TestSectigo:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -65,7 +65,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -83,7 +83,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.get_md_status(domain)
         assert md['renewal']['errors'] > 0
@@ -101,7 +101,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.get_md_status(domain)
         assert md['renewal']['errors'] > 0
@@ -120,7 +120,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -142,7 +142,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain2}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -167,7 +167,7 @@ class TestSectigo:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
index 1884665c6704eb0e5d5cea51270e7e6c56098b92..70505a3273f60946ae15b413484165e4bd3cff00 100644 (file)
@@ -43,7 +43,7 @@ class TestZeroSSL:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -66,7 +66,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -88,7 +88,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.get_md_status(domain)
         assert md['renewal']['errors'] > 0
@@ -110,7 +110,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         md = env.get_md_status(domain)
         assert md['renewal']['errors'] > 0
@@ -134,7 +134,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -160,7 +160,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain2}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
@@ -188,7 +188,7 @@ class TestZeroSSL:
         conf.end_md()
         conf.add_vhost(domains=domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         r = env.curl_get(f"https://{domain}:{env.https_port}", options=[
             "--cacert", f"{env.test_dir}/data/sectigo-demo-root.pem"
index 27a2df474aa636254b2b3f9de8b8952e6438554f..bb218f90c18f33bc67c9778d9d69b50629054199 100644 (file)
@@ -103,7 +103,7 @@ class TestTailscale:
         acme.start(config='default')
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         yield
         faker.stop()
 
@@ -133,7 +133,7 @@ class TestTailscale:
         conf.add_vhost(domains)
         conf.install()
         # restart and watch it fail due to wrong tailscale unix socket path
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md
         assert md['renewal']['errors'] > 0
@@ -163,9 +163,9 @@ class TestTailscale:
         conf.add_vhost(domains)
         conf.install()
         # restart and watch it fail due to wrong tailscale unix socket path
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(domain)
 
     # create a MD using `tailscale` as protocol, but domain name not assigned by tailscale
@@ -184,7 +184,7 @@ class TestTailscale:
         conf.add_vhost(domains)
         conf.install()
         # restart and watch it fail due to wrong tailscale unix socket path
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md
         assert md['renewal']['errors'] > 0
index 696161fd4fd8d8050688c775deba01d905bf8431..5b9d4e2fcc6a6c294c4bc1236317cbf4c6cb9832 100644 (file)
@@ -16,7 +16,7 @@ class TestFailover:
         conf = MDConf(env)
         conf.install()
 
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -39,7 +39,7 @@ class TestFailover:
         conf.end_md()
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.check_md_complete(domain)
 
@@ -60,7 +60,7 @@ class TestFailover:
         conf.end_md()
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.check_md_complete(domain)
         #
@@ -91,7 +91,7 @@ class TestFailover:
         conf.end_md()
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.check_md_complete(domain)
         #
index 32edee33935b9d5e36171df3fa1478e96e451679..8433ca8cee5a0c6435962189cd8aa6f2d96cbcad 100644 (file)
@@ -17,7 +17,7 @@ class TestMustStaple:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -33,7 +33,7 @@ class TestMustStaple:
     # MD with default, e.g. not staple
     def test_md_800_001(self, env):
         self.configure_httpd(env, self.domain)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([self.domain])
         env.check_md_complete(self.domain)
         cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
@@ -42,7 +42,7 @@ class TestMustStaple:
     # MD that should explicitly not staple
     def test_md_800_002(self, env):
         self.configure_httpd(env, self.domain, "MDMustStaple off")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.check_md_complete(self.domain)
         cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
         assert not cert1.get_must_staple()
@@ -53,13 +53,13 @@ class TestMustStaple:
     @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_800_003(self, env):
         self.configure_httpd(env, self.domain, "MDMustStaple on")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([self.domain])
         env.check_md_complete(self.domain)
         cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
         assert cert1.get_must_staple()
         self.configure_httpd(env, self.domain, "MDMustStaple off")
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([self.domain])
         env.check_md_complete(self.domain)
         cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
@@ -78,7 +78,7 @@ class TestMustStaple:
             SSLUseStapling On
             SSLStaplingCache shmcb:stapling_cache(128000)
             """)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_ocsp_status(self.domain)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
index 7992337964327eff04272d95eb8c12de5804d2a3..33485725061e0cd20d3afaa300d1efcf745bd17d 100644 (file)
@@ -12,7 +12,6 @@ from .md_env import MDTestEnv
 
 @pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
                     reason="no ACME test server configured")
-@pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
 class TestStapling:
 
     @pytest.fixture(autouse=True, scope='class')
@@ -25,7 +24,7 @@ class TestStapling:
         mdB = "b-" + domain
         self.configure_httpd(env, [mdA, mdB]).install()
         env.apache_stop()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([mdA, mdB])
         env.check_md_complete(mdA)
         env.check_md_complete(mdB)
@@ -68,9 +67,12 @@ class TestStapling:
     def test_md_801_001(self, env):
         md = self.mdA
         self.configure_httpd(env, md).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_ocsp_status(md)
-        assert stat['ocsp'] == "no response sent" 
+        if MDTestEnv.lacks_ocsp():
+            assert 'ocsp' not in stat or stat['ocsp'] == "no response sent", f'{stat}'
+        else:
+            assert stat['ocsp'] == "no response sent"
         stat = env.get_md_status(md)
         assert not stat["stapling"]
         #
@@ -79,30 +81,43 @@ class TestStapling:
             MDStapling on
             LogLevel md:trace5
             """).install()
-        assert env.apache_restart() == 0
-        stat = env.await_ocsp_status(md)
-        assert stat['ocsp'] == "successful (0x0)" 
-        assert stat['verify'] == "0 (ok)"
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+        if MDTestEnv.lacks_ocsp():
+            stat = env.get_md_status(md)
+            assert 'ocsp' not in stat or stat['ocsp'] == "no response sent", f'{stat}'
+        else:
+            stat = env.await_ocsp_status(md)
+            assert stat['ocsp'] == "successful (0x0)"
+            assert stat['verify'] == "0 (ok)"
         stat = env.get_md_status(md)
-        assert stat["stapling"]
-        pkey = 'rsa'
-        assert stat["cert"][pkey]["ocsp"]["status"] == "good"
-        assert stat["cert"][pkey]["ocsp"]["valid"]
+        if MDTestEnv.lacks_ocsp():
+            assert stat["stapling"]
+            pkey = 'rsa'
+            assert 'ocsp' not in stat["cert"][pkey], f'{stat}'
+        else:
+            assert stat["stapling"]
+            pkey = 'rsa'
+            assert stat["cert"][pkey]["ocsp"]["status"] == "good"
+            assert stat["cert"][pkey]["ocsp"]["valid"]
         #
         # turn stapling off (explicitly) again, should disappear
         self.configure_httpd(env, md, "MDStapling off").install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_ocsp_status(md)
-        assert stat['ocsp'] == "no response sent" 
+        if MDTestEnv.lacks_ocsp():
+            assert 'ocsp' not in stat or stat['ocsp'] == "no response sent", f'{stat}'
+        else:
+            assert stat['ocsp'] == "no response sent"
         stat = env.get_md_status(md)
         assert not stat["stapling"]
         
     # MD with stapling on/off and mod_ssl stapling on
     # expect to see stapling response in all cases
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_002(self, env):
         md = self.mdA
         self.configure_httpd(env, md, ssl_stapling=True).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" if \
             env.ssl_module == "mod_ssl" else "no response sent"
@@ -111,7 +126,7 @@ class TestStapling:
         #
         # turn stapling on, wait for it to appear in connections
         self.configure_httpd(env, md, "MDStapling on", ssl_stapling=True).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
@@ -123,7 +138,7 @@ class TestStapling:
         #
         # turn stapling off (explicitly) again, should disappear
         self.configure_httpd(env, md, "MDStapling off", ssl_stapling=True).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" if \
             env.ssl_module == "mod_ssl" else "no response sent"
@@ -145,23 +160,30 @@ class TestStapling:
         conf.add_vhost(md_a)
         conf.add_vhost(md_b)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # mdA has stapling
-        stat = env.await_ocsp_status(md_a)
-        assert stat['ocsp'] == "successful (0x0)" 
+        if MDTestEnv.lacks_ocsp():
+            stat = env.get_ocsp_status(md_a)
+            assert 'ocsp' not in stat or stat['ocsp'] == "no response sent", f'{stat}'
+        else:
+            stat = env.await_ocsp_status(md_a)
+            assert stat['ocsp'] == "successful (0x0)"
         assert stat['verify'] == "0 (ok)"
         stat = env.get_md_status(md_a)
         assert stat["stapling"]
-        pkey = 'rsa'
-        assert stat["cert"][pkey]["ocsp"]["status"] == "good"
-        assert stat["cert"][pkey]["ocsp"]["valid"]
+        if not MDTestEnv.lacks_ocsp():
+            pkey = 'rsa'
+            assert stat["cert"][pkey]["ocsp"]["status"] == "good"
+            assert stat["cert"][pkey]["ocsp"]["valid"]
         # mdB has no stapling
-        stat = env.get_ocsp_status(md_b)
-        assert stat['ocsp'] == "no response sent" 
+        if not MDTestEnv.lacks_ocsp():
+            stat = env.get_ocsp_status(md_b)
+            assert stat['ocsp'] == "no response sent"
         stat = env.get_md_status(md_b)
         assert not stat["stapling"]
 
     # 2 MDs, md stapling on+off, ssl stapling on
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_004(self, env):
         md_a = self.mdA
         md_b = self.mdB
@@ -176,7 +198,7 @@ class TestStapling:
         conf.add_vhost(md_a)
         conf.add_vhost(md_b)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # mdA has stapling
         stat = env.await_ocsp_status(md_a)
         assert stat['ocsp'] == "successful (0x0)"
@@ -195,12 +217,13 @@ class TestStapling:
 
     # MD, check that restart leaves response unchanged, reconfigure keep interval, 
     # should remove the file on restart and get a new one
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_005(self, env):
         # TODO: mod_watchdog seems to have problems sometimes with fast restarts
         # turn stapling on, wait for it to appear in connections
         md = self.mdA
         self.configure_httpd(env, md, "MDStapling on").install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
@@ -215,7 +238,7 @@ class TestStapling:
         mtime1 = os.path.getmtime(ocsp_file)
         # wait a sec, restart and check that file does not change
         time.sleep(1)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         mtime2 = os.path.getmtime(ocsp_file)
@@ -227,12 +250,12 @@ class TestStapling:
             MDStapling on
             MDStaplingKeepResponse 1s
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)"
         assert not os.path.exists(ocsp_file)
         # if we restart again, a new file needs to appear
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)"
         mtime3 = os.path.getmtime(ocsp_file)
@@ -240,11 +263,12 @@ class TestStapling:
 
     # MD, check that stapling renew window works. Set a large window
     # that causes response to be retrieved all the time.
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_006(self, env):
         # turn stapling on, wait for it to appear in connections
         md = self.mdA
         self.configure_httpd(env, md, "MDStapling on").install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
@@ -257,7 +281,7 @@ class TestStapling:
                 ocsp_file = os.path.join(dirpath, name)
         assert ocsp_file
         mtime1 = os.path.getmtime(ocsp_file)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         # wait a sec, restart and check that file does not change
@@ -269,7 +293,7 @@ class TestStapling:
             MDStapling on
             MDStaplingRenewWindow 10d
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)"
         # wait a sec, restart and check that file does change
@@ -278,6 +302,7 @@ class TestStapling:
         assert mtime1 != mtime3
 
     # MD, make a MDomain with static files, check that stapling works
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_007(self, env):
         # turn stapling on, wait for it to appear in connections
         md = self.mdA
@@ -292,7 +317,7 @@ class TestStapling:
                    env.store_domain_file(md, 'pubcert.pem')))
         conf.add_vhost(md)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
@@ -306,6 +331,7 @@ class TestStapling:
         assert ocsp_file
 
     # Use certificate files in direct config, check that stapling works
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_008(self, env):
         # turn stapling on, wait for it to appear in connections
         md = self.mdA
@@ -316,7 +342,7 @@ class TestStapling:
                              env.store_domain_file(md, 'privkey.pem'))
         conf.end_vhost()
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.await_ocsp_status(md)
         assert stat['ocsp'] == "successful (0x0)" 
         assert stat['verify'] == "0 (ok)"
@@ -331,6 +357,7 @@ class TestStapling:
 
     # Turn on stapling for a certificate without OCSP responder and issuer
     # (certificates without issuer prevent mod_ssl asking around for stapling)
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_009(self, env):
         md = self.mdA
         domains = [md]
@@ -353,13 +380,14 @@ class TestStapling:
         conf.end_md()
         conf.add_vhost(md)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         time.sleep(1)
         stat = env.get_ocsp_status(md)
         assert stat['ocsp'] == "no response sent" 
 
     # Turn on stapling for an MDomain not used in any virtualhost
     # There was a crash in server-status in this case
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_010(self, env):
         env.clear_ocsp_store()
         md = self.mdA
@@ -369,7 +397,7 @@ class TestStapling:
         conf.add("MDStapling on")
         conf.end_md()
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_server_status()
         assert stat
 
@@ -379,15 +407,16 @@ class TestStapling:
     # scheduling.
     # This checks the mistaken assert() reported in
     # <https://bz.apache.org/bugzilla/show_bug.cgi?id=65567>
+    @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
     def test_md_801_011(self, env):
         domains = [ f'test-801-011-{i}-{env.DOMAIN_SUFFIX}' for i in range(7)]
         self.configure_httpd(env, domains, """
             MDStapling on
             LogLevel md:trace2 ssl:warn
             """).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains, restart=False, timeout=120)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # now the certs are installed and ocsp will be retrieved
         time.sleep(1)
         for domain in domains:
index 5c310180a85b8abd1e689ecf5133e36a64097ad9..f12fdb052e24c176410f0a90635bec448dae4e64 100644 (file)
@@ -18,7 +18,7 @@ class TestAutov2:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -33,7 +33,7 @@ class TestAutov2:
             conf.add_md(domains)
             conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
 
     def check_pkeys(self, env, domain, pkeys):
@@ -100,7 +100,7 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         md = env.await_error(domain)
         assert md
         assert md['renewal']['errors'] > 0
@@ -135,14 +135,14 @@ class TestAutov2:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion(domains)
         conf = MDConf(env)
         conf.add("MDPrivateKeys rsa3072 secp384r1")
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         mds = env.get_md_status(domain, via_domain=domain, use_https=True)
         assert 'renew' in mds and mds['renew'] is True, f"{mds}"
         assert env.await_completion(domains)
index 94369128f16ac2e521b1672e3d831903216ee466..fc55914d2d4638cbbf93ca9166db1545c6ada0f2 100644 (file)
@@ -37,7 +37,7 @@ class TestLocks:
         self.configure_httpd(env, [domain], add_lines=[
             "MDStoreLocks 1s"
         ])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
 
     # renewal, with global lock held during restert
@@ -47,26 +47,26 @@ class TestLocks:
         self.configure_httpd(env, [domain], add_lines=[
             "MDStoreLocks 1s"
         ])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         # we have a cert now, add a dns name to force renewal
         certa = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
         self.configure_httpd(env, [domain, f"x.{domain}"], add_lines=[
             "MDStoreLocks 1s"
         ])
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # await new cert, but do not restart, keeps the cert in staging
         assert env.await_completion([domain], restart=False)
         # obtain global lock and restart
         lockfile = os.path.join(env.store_dir, "store.lock")
         with FileLock(lockfile):
-            assert env.apache_restart() == 0
+            assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # lock should have prevented staging from being activated,
         # meaning we will have the same cert
         certb = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
         assert certa.same_serial_as(certb)
         # now restart without lock
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         certc = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
         assert not certa.same_serial_as(certc)
 
index 9d18da5411491911ee72a02c8b071d6a08ca2af0..15521c5964d69816053e3aded5c669facd1f3cb4 100644 (file)
@@ -45,7 +45,7 @@ class TestNotify:
         self.configure_httpd(env, self.domain, f"""
             MDNotifyCmd {command} {args}
             """)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(self.domain)
         stat = env.get_md_status(self.domain)
         assert stat["renewal"]["last"]["problem"] == "urn:org:apache:httpd:log:AH10108:"
@@ -63,7 +63,7 @@ class TestNotify:
         self.configure_httpd(env, self.domain, f"""
             MDNotifyCmd {command} {args}
             """)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(self.domain)
         stat = env.get_md_status(self.domain)
         assert stat["renewal"]["last"]["problem"] == "urn:org:apache:httpd:log:AH10108:"
@@ -83,7 +83,7 @@ class TestNotify:
         self.configure_httpd(env, self.domain, f"""
             MDNotifyCmd {command} {args}
             """)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([self.domain], restart=False)
         time.sleep(1)
         stat = env.get_md_status(self.domain)
@@ -102,7 +102,7 @@ class TestNotify:
         self.configure_httpd(env, self.domain, f"""
             MDNotifyCmd {command} {args} {extra_arg}
             """)
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([self.domain], restart=False)
         time.sleep(1)
         stat = env.get_md_status(self.domain)
@@ -125,7 +125,7 @@ class TestNotify:
         conf.add_vhost(domains1)
         conf.add_vhost(domains2)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([md1, md2], restart=False)
         time.sleep(1)
         stat = env.get_md_status(md1)
index c0018393e71d11c3e556fabfeb0a9109def00f4c..86475fc571708cb8bf4483e81d2408597de83fa6 100644 (file)
@@ -22,7 +22,7 @@ class TestMessage:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -43,7 +43,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_file(env.store_staged_file(domain, 'job.json'))
         stat = env.get_md_status(domain)
         # this command should have failed and logged an error
@@ -70,7 +70,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         stat = env.get_md_status(domain)
         # this command should have failed and logged an error
@@ -96,7 +96,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         time.sleep(1)
         stat = env.get_md_status(domain)
@@ -134,7 +134,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         # force renew
         conf = MDConf(env)
@@ -144,7 +144,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         env.get_md_status(domain)
         assert env.await_file(self.mlog)
@@ -175,7 +175,7 @@ class TestMessage:
         conf.end_md()
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert not os.path.isfile(self.mlog)
         
     def test_md_901_011(self, env):
@@ -201,13 +201,13 @@ class TestMessage:
         conf.end_md()
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_file(self.mlog)
         nlines = open(self.mlog).readlines()
         assert len(nlines) == 1
         assert nlines[0].strip() == f"['{self.mcmd}', '{self.mlog}', 'expiring', '{domain}']"
         # check that we do not get it resend right away again
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         time.sleep(1)
         nlines = open(self.mlog).readlines()
         assert len(nlines) == 1
@@ -225,7 +225,7 @@ class TestMessage:
         conf.add("MDStapling on")
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         env.await_ocsp_status(domain)
         assert env.await_file(self.mlog)
@@ -251,7 +251,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         # set the warn window that triggers right away and a failing message command
         conf = MDConf(env)
@@ -262,7 +262,7 @@ class TestMessage:
             """)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         env.get_md_status(domain)
         # this command should have failed and logged an error
         # shut down server to make sure that md has completed
@@ -285,7 +285,7 @@ class TestMessage:
             """)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_file(self.mlog)
         # we see the notification logged by the command
         nlines = open(self.mlog).readlines()
@@ -308,7 +308,7 @@ class TestMessage:
         conf.add_md(domains)
         conf.add_vhost(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_error(domain)
         assert env.await_file(self.mlog)
         time.sleep(1)
index 1971fdaecf2848bd5b8d187f38d37654eb2c5841..08578c156f1151c1062e2b8b1d43d70ad3cea7f1 100644 (file)
@@ -19,7 +19,7 @@ class TestCleanups:
         env.check_acme()
         env.clear_store()
         MDConf(env).install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
 
     @pytest.fixture(autouse=True, scope='function')
     def _method_scope(self, env, request):
@@ -45,7 +45,7 @@ class TestCleanups:
         for name in dirs_before:
             os.makedirs(os.path.join(challenges_dir, name))
 
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         # the one we use is still there
         assert os.path.isdir(os.path.join(challenges_dir, domain))
         # and the others are gone
index 306b131a16d08974753e99850b2157fe0cbf30ef..d9babb172b36c79a2884da33b4efaadfb78e7c4e 100644 (file)
@@ -36,7 +36,7 @@ class TestStatus:
         conf.add_md(domains)
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         # we started without a valid certificate, so we expect /.httpd/certificate-status
         # to not give information about one and - since we waited for the ACME signup
@@ -49,7 +49,7 @@ class TestStatus:
         assert 'sha256-fingerprint' in status['renewal']['cert']['rsa']
         # restart and activate
         # once activated, the staging must be gone and attributes exist for the active cert
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         status = env.get_certificate_status(domain)
         assert 'renewal' not in status
         assert 'sha256-fingerprint' in status['rsa']
@@ -64,7 +64,7 @@ class TestStatus:
         conf.add_md(domains)
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         # copy a real certificate from LE over to staging
         staged_cert = os.path.join(env.store_dir, 'staging', domain, 'pubcert.pem')
@@ -87,7 +87,7 @@ class TestStatus:
         conf.add("MDCertificateStatus off")
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         status = env.get_certificate_status(domain)
         assert not status
@@ -100,7 +100,7 @@ class TestStatus:
         conf.add("MDCertificateStatus off")
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain])
         status = env.get_md_status("")
         assert "version" in status
@@ -114,6 +114,7 @@ class TestStatus:
         conf = MDConf(env, std_vhosts=False, std_ports=False, text=f"""
 MDBaseServer on
 MDPortMap http:- https:{env.https_port}
+MDStapling on
 
 ServerName {domain}
 <IfModule ssl_module>
@@ -137,7 +138,7 @@ Protocols h2 http/1.1 acme-tls/1
             """)
         conf.add_md(domains)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False,
                                     via_domain=env.http_addr, use_https=False)
         status = env.get_md_status("", via_domain=env.http_addr, use_https=False)
@@ -160,6 +161,8 @@ Protocols h2 http/1.1 acme-tls/1
         assert int(m.group(1)) == 0
         m = re.search(r'ManagedCertificatesReady: (\d+)', status, re.MULTILINE)
         assert int(m.group(1)) == 1
+        m = re.search(r'ManagedDomain\[0]Stapling: (on)', status, re.MULTILINE)
+        assert m, f'{status}'
 
     def test_md_920_011(self, env):
         # MD with static cert files in base server, see issue #161
@@ -205,13 +208,13 @@ Protocols h2 http/1.1 acme-tls/1
         conf.add("SSLEngine off")
         conf.end_vhost()
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         status = env.get_md_status(domain, via_domain=env.http_addr, use_https=False)
         assert status
         assert 'renewal' not in status
         print(status)
         assert status['state'] == env.MD_S_COMPLETE
-        assert status['renew-mode'] == 1  # manual
+        assert status['renew-mode'] == 0  # manual
 
     # MD with 2 certificates
     def test_md_920_020(self, env):
@@ -223,7 +226,7 @@ Protocols h2 http/1.1 acme-tls/1
         conf.add_md(domains)
         conf.add_vhost(domain)
         conf.install()
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         assert env.await_completion([domain], restart=False)
         # In the stats JSON, we expect 2 certificates under 'renewal'
         stat = env.get_md_status(domain)
@@ -239,7 +242,7 @@ Protocols h2 http/1.1 acme-tls/1
         assert 'rsa' in status['renewal']['cert']
         # restart and activate
         # once activated, certs are listed in status
-        assert env.apache_restart() == 0
+        assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
         stat = env.get_md_status(domain)
         assert 'cert' in stat
         assert 'valid' in stat['cert']