diff --git a/changes-entries/md_v2.5.1.txt b/changes-entries/md_v2.5.1.txt new file mode 100644 index 00000000000..051f2aa940c --- /dev/null +++ b/changes-entries/md_v2.5.1.txt @@ -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. diff --git a/modules/md/md.h b/modules/md/md.h index 035ccba7837..3f298eaa6f3 100644 --- a/modules/md/md.h +++ b/modules/md/md.h @@ -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" diff --git a/modules/md/md_acme.c b/modules/md/md_acme.c index 4366bf695c2..f8624513ba8 100644 --- a/modules/md/md_acme.c +++ b/modules/md/md_acme.c @@ -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; diff --git a/modules/md/md_acme.h b/modules/md/md_acme.h index f28f2b6c617..9931f92493f 100644 --- a/modules/md/md_acme.h +++ b/modules/md/md_acme.h @@ -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; diff --git a/modules/md/md_acme_drive.c b/modules/md/md_acme_drive.c index 45c39b37ad1..f5cd08c5214 100644 --- a/modules/md/md_acme_drive.c +++ b/modules/md/md_acme_drive.c @@ -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, diff --git a/modules/md/md_acme_drive.h b/modules/md/md_acme_drive.h index 88761fab95d..986b49e8f4a 100644 --- a/modules/md/md_acme_drive.h +++ b/modules/md/md_acme_drive.h @@ -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 */ diff --git a/modules/md/md_acme_order.c b/modules/md/md_acme_order.c index 061093a4132..0a0ad7ff0ae 100644 --- a/modules/md/md_acme_order.c +++ b/modules/md/md_acme_order.c @@ -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 @@ static apr_status_t on_order_upd(md_acme_t *acme, apr_pool_t *p, const apr_table } 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); diff --git a/modules/md/md_acme_order.h b/modules/md/md_acme_order.h index 417044018f5..01d73d41b9f 100644 --- a/modules/md/md_acme_order.h +++ b/modules/md/md_acme_order.h @@ -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); diff --git a/modules/md/md_acmev2_drive.c b/modules/md/md_acmev2_drive.c index 1eda1dc15bf..e5821e560ae 100644 --- a/modules/md/md_acmev2_drive.c +++ b/modules/md/md_acmev2_drive.c @@ -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) { diff --git a/modules/md/md_core.c b/modules/md/md_core.c index 7aacff0497a..70f20c4bebe 100644 --- a/modules/md/md_core.c +++ b/modules/md/md_core.c @@ -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; diff --git a/modules/md/md_crypt.c b/modules/md/md_crypt.c index ca44fab064c..77feb55a850 100644 --- a/modules/md/md_crypt.c +++ b/modules/md/md_crypt.c @@ -63,6 +63,10 @@ || LIBRESSL_VERSION_NUMBER >= 0x3050000fL) /* Missing from LibreSSL < 3.5.0 and only available since OpenSSL v1.1.x */ #include +#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 @@ apr_status_t md_cert_make_tls_alpn_01(md_cert_t **pcert, const char *domain, 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; diff --git a/modules/md/md_crypt.h b/modules/md/md_crypt.h index a892e00f1e6..e6b3ac2e783 100644 --- a/modules/md/md_crypt.h +++ b/modules/md/md_crypt.h @@ -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); diff --git a/modules/md/md_curl.c b/modules/md/md_curl.c index 217e8579dd8..3105d31ef8b 100644 --- a/modules/md/md_curl.c +++ b/modules/md/md_curl.c @@ -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; diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c index dc49446ae45..d0a41de177d 100644 --- a/modules/md/md_reg.c +++ b/modules/md/md_reg.c @@ -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 " diff --git a/modules/md/md_status.c b/modules/md/md_status.c index 936c65349f9..5490e770108 100644 --- a/modules/md/md_status.c +++ b/modules/md/md_status.c @@ -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); diff --git a/modules/md/md_store_fs.c b/modules/md/md_store_fs.c index 35c24b41804..77063bff703 100644 --- a/modules/md/md_store_fs.c +++ b/modules/md/md_store_fs.c @@ -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; } diff --git a/modules/md/md_version.h b/modules/md/md_version.h index 2c3b66bc28e..609cc93ec3d 100644 --- a/modules/md/md_version.h +++ b/modules/md/md_version.h @@ -27,7 +27,7 @@ * @macro * Version number of the md module as c string */ -#define MOD_MD_VERSION "2.4.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" diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 6d3f5b791d1..c34aeb2909a 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -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. diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c index cdd1e297c74..413f1e8e99b 100644 --- a/modules/md/mod_md_config.c +++ b/modules/md/mod_md_config.c @@ -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; } diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h index 1ce2375f00d..48272cfdf13 100644 --- a/modules/md/mod_md_config.h +++ b/modules/md/mod_md_config.h @@ -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 */ diff --git a/test/modules/md/conftest.py b/test/modules/md/conftest.py index 0118de5e133..defa649376f 100755 --- a/test/modules/md/conftest.py +++ b/test/modules/md/conftest.py @@ -5,12 +5,11 @@ 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, diff --git a/test/modules/md/pebble/pebble-eab.json.template b/test/modules/md/pebble/pebble-eab.json.template index dd5bee5ea82..972e1b4e667 100644 --- a/test/modules/md/pebble/pebble-eab.json.template +++ b/test/modules/md/pebble/pebble-eab.json.template @@ -11,6 +11,16 @@ "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 + } + } } } diff --git a/test/modules/md/pebble/pebble.json.template b/test/modules/md/pebble/pebble.json.template index 9c41271785a..2b97289c537 100644 --- a/test/modules/md/pebble/pebble.json.template +++ b/test/modules/md/pebble/pebble.json.template @@ -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 + } + } } } diff --git a/test/modules/md/test_010_store_migrate.py b/test/modules/md/test_010_store_migrate.py index d734b29b38e..1872f471077 100644 --- a/test/modules/md/test_010_store_migrate.py +++ b/test/modules/md/test_010_store_migrate.py @@ -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): diff --git a/test/modules/md/test_202_acmev2_regs.py b/test/modules/md/test_202_acmev2_regs.py index 97f093ebc8e..b99c745f2a5 100644 --- a/test/modules/md/test_202_acmev2_regs.py +++ b/test/modules/md/test_202_acmev2_regs.py @@ -19,7 +19,7 @@ def _class_scope(self, env, acme): 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): diff --git a/test/modules/md/test_300_conf_validate.py b/test/modules/md/test_300_conf_validate.py index 88df1683413..dc65aa20690 100644 --- a/test/modules/md/test_300_conf_validate.py +++ b/test/modules/md/test_300_conf_validate.py @@ -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 @@ def test_md_300_002(self, env): 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 @@ def test_md_300_005(self, env): MDomain example2.org www.example2.org www.example3.org """).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 @@ def test_md_300_006(self, env): MDomain example2.org www.example2.org www.example3.org """).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 @@ def test_md_300_007(self, env): ServerName www.example2.org """).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 @@ def test_md_300_008(self, env): ServerAlias example2.org """).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 @@ def test_md_300_010(self, env): """) 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 @@ def test_md_300_011b(self, env): ServerAlias test4.not-forbidden.org """ % 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 @@ def test_md_300_012(self, env): ServerAlias test3.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 = [ @@ -246,18 +252,19 @@ def test_md_300_013(self, env): ServerName test-b.example2.org """).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 ServerName www.example2.org """).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 @@ def test_md_300_015(self, env): 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 @@ def test_md_300_022(self, env): ServerName secret.com """).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 @@ def test_md_300_026(self, env): """) 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 @@ def test_md_300_028(self, env): # 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 @@ def test_md_300_029(self, env): # 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 @@ def test_md_300_029(self, env): 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 diff --git a/test/modules/md/test_310_conf_store.py b/test/modules/md/test_310_conf_store.py index f2bb9c723ac..992a6302772 100644 --- a/test/modules/md/test_310_conf_store.py +++ b/test/modules/md/test_310_conf_store.py @@ -30,7 +30,7 @@ def _method_scope(self, env, request): # 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 @@ def test_md_310_001(self, env): ]) 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 @@ def test_md_310_101(self, env): 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 @@ def test_md_310_102(self, env): 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 @@ def test_md_310_103(self, env): 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 @@ def test_md_310_104(self, env): 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 @@ def test_md_310_104(self, env): 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 @@ def test_md_310_105(self, env): 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 @@ def test_md_310_106(self, env): 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 @@ def test_md_310_107(self, env): ServerAdmin mailto:admin@testdomain2.org """).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 @@ def test_md_310_108(self, env): 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 @@ def test_md_310_109(self, env): 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 @@ def test_md_310_110(self, env): 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 @@ def test_md_310_111(self, env): 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 @@ def test_md_310_112(self, env): 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 @@ def test_md_310_113a(self, env): 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 @@ def test_md_310_113b(self, env): 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 @@ def test_md_310_114(self, env): 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 @@ def test_md_310_115(self, env): 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 @@ def test_md_310_116(self, env): 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 @@ def test_md_310_117(self, env): "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 @@ def test_md_310_118(self, env): 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 @@ def test_md_310_119(self, env): 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 @@ def test_md_310_120(self, env): 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 @@ def test_md_310_121(self, env): 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 @@ def test_md_310_122(self, env): 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 @@ def test_md_310_200(self, env): 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 @@ def test_md_310_201(self, env): 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 @@ def test_md_310_202(self, env): 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 @@ def test_md_310_203(self, env): 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 @@ def test_md_310_204(self, env): 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 @@ def test_md_310_205(self, env): 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 @@ def test_md_310_206(self, env): 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 @@ def test_md_310_207(self, env, renew_mode, exp_code): 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 @@ def test_md_310_208(self, env): 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 @@ def test_md_310_209(self, env, key_size): 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 @@ def test_md_310_210(self, env, mode): MDRequireHttps %s """ % 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 @@ def test_md_310_211(self, env): 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 @@ def test_md_310_300(self, env): 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 @@ def test_md_310_301(self, env): 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 @@ def test_md_310_302(self, env): 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 @@ def test_md_310_302(self, env): 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 @@ def test_md_310_303(self, env): 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 @@ def test_md_310_303(self, env): 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 @@ def test_md_310_304(self, env): 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 @@ def test_md_310_305(self, env): 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 @@ def test_md_310_306(self, env): 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 @@ def test_md_310_307(self, env): 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 @@ def test_md_310_307(self, env): 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 @@ def test_md_310_307(self, env): 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 @@ def test_md_310_308(self, env): 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 @@ def test_md_310_308(self, env): MDRequireHttps permanent """).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 @@ def test_md_310_309(self, env): 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 @@ def test_md_310_310(self, env, window): 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 @@ def test_md_310_400(self, env): 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 @@ def test_md_310_401(self, env): 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 @@ def test_md_310_500(self, env): 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 @@ def test_md_310_501(self, env): 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 @@ def test_md_310_601(self, env): 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 @@ def test_md_310_601(self, env): 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 @@ def test_md_310_601(self, env): 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 diff --git a/test/modules/md/test_502_acmev2_drive.py b/test/modules/md/test_502_acmev2_drive.py index b064647450e..484e4d4bd95 100644 --- a/test/modules/md/test_502_acmev2_drive.py +++ b/test/modules/md/test_502_acmev2_drive.py @@ -25,7 +25,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_502_100(self, env): 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 @@ def test_md_502_101(self, env): 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 @@ def test_md_502_103(self, env): 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 @@ def test_md_502_105(self, env): 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 @@ def test_md_502_107(self, env): 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 @@ def test_md_502_108(self, env): 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 @@ def test_md_502_109(self, env): # 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 @@ def test_md_502_109(self, env): # 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 @@ def test_md_502_109(self, env): # 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 @@ def test_md_502_110(self, env): 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 @@ def test_md_502_110(self, env): 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 @@ def test_md_502_111(self, env): 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 @@ def test_md_502_120(self, env): 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 @@ def test_md_502_200(self, env): 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 @@ def test_md_502_201(self, env, renew_window, test_data_list): 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 @@ def test_md_502_202(self, env, key_type, key_params, exp_key_length): 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 @@ def test_md_502_300(self, env): 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 @@ def test_md_502_301(self, env): 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) diff --git a/test/modules/md/test_602_roundtrip.py b/test/modules/md/test_602_roundtrip.py index 9ff87e5df7e..2d050ae2e0f 100644 --- a/test/modules/md/test_602_roundtrip.py +++ b/test/modules/md/test_602_roundtrip.py @@ -39,16 +39,16 @@ def test_md_602_000(self, env): 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 @@ def test_md_602_001(self, env): 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 @@ def test_md_602_001(self, env): 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 @@ def test_md_602_002(self, env): 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 @@ def test_md_602_002(self, env): 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) diff --git a/test/modules/md/test_702_auto.py b/test/modules/md/test_702_auto.py index 90103e3aff7..df24290f576 100644 --- a/test/modules/md/test_702_auto.py +++ b/test/modules/md/test_702_auto.py @@ -1,4 +1,5 @@ import os +import time from datetime import timedelta import pytest @@ -21,7 +22,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_702_001(self, env): 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 @@ def test_md_702_001(self, env): # 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 @@ def test_md_702_002(self, env): 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 @@ def test_md_702_002(self, env): 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 @@ def test_md_702_003(self, env): 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 @@ def test_md_702_004(self, env, challenge_type): 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 @@ def test_md_702_005(self, env): 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 @@ def test_md_702_006(self, env): 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 @@ def test_md_702_008(self, env): 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 @@ def test_md_702_008a(self, env): 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 @@ def test_md_702_009(self, env): 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 @@ def test_md_702_010(self, env): 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 @@ def test_md_702_010(self, env): 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 @@ def test_md_702_011(self, env): 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 @@ def test_md_702_011(self, env): 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 @@ def test_md_702_030(self, env): 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 @@ def test_md_702_030(self, env): 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 @@ def test_md_702_031(self, env): 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 @@ def test_md_702_031(self, env): 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 @@ def test_md_702_032(self, env): 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 @@ def test_md_702_032(self, env): 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 @@ def test_md_702_033(self, env): 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 @@ def test_md_702_033(self, env): 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 @@ def test_md_702_040(self, env): 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 @@ def test_md_702_041(self, env): # # 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 @@ def test_md_702_042(self, env): 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 @@ def test_md_702_043(self, env): # # 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 @@ def test_md_702_044(self, env, challenge_type): 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 @@ def test_md_702_050(self, env): """) 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 @@ def test_md_702_051(self, env): """) 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 @@ def test_md_702_052(self, env): ]) 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 @@ def test_md_702_060(self, env, challenge_type): 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 @@ def test_md_702_060(self, env, challenge_type): 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 @@ def test_md_702_070(self, env): 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 @@ def test_md_702_071(self, env): 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 index 00000000000..d8fc1a4adcc --- /dev/null +++ b/test/modules/md/test_710_profiles.py @@ -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 + ]) diff --git a/test/modules/md/test_720_wildcard.py b/test/modules/md/test_720_wildcard.py index 916c47a5d85..1930f72fba8 100644 --- a/test/modules/md/test_720_wildcard.py +++ b/test/modules/md/test_720_wildcard.py @@ -18,7 +18,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_720_001(self, env): 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 @@ def test_md_720_002(self, env): 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 @@ def test_md_720_002b(self, env): 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 @@ def test_md_720_003(self, env): 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 @@ def test_md_720_004(self, env): 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 @@ def test_md_720_005(self, env): 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 @@ def test_md_720_006(self, env): 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 @@ def test_md_720_007(self, env): 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 @@ def test_md_720_008(self, env): 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 @@ def test_md_720_008(self, env): 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) diff --git a/test/modules/md/test_730_static.py b/test/modules/md/test_730_static.py index 91a5f4445d2..3c4d26e88c9 100644 --- a/test/modules/md/test_730_static.py +++ b/test/modules/md/test_730_static.py @@ -19,7 +19,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_730_001(self, env): 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 @@ def test_md_730_002(self, env): 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 diff --git a/test/modules/md/test_740_acme_errors.py b/test/modules/md/test_740_acme_errors.py index 364aaca6c8d..130723ba11b 100644 --- a/test/modules/md/test_740_acme_errors.py +++ b/test/modules/md/test_740_acme_errors.py @@ -16,7 +16,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_740_000(self, env): 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 @@ def test_md_740_001(self, env): 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 diff --git a/test/modules/md/test_741_setup_errors.py b/test/modules/md/test_741_setup_errors.py index 958f13f4d13..d041e970772 100644 --- a/test/modules/md/test_741_setup_errors.py +++ b/test/modules/md/test_741_setup_errors.py @@ -18,7 +18,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_741_001(self, env): 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 @@ def test_md_741_002(self, env): 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( diff --git a/test/modules/md/test_750_eab.py b/test/modules/md/test_750_eab.py index aec7e89b8c2..894ac8506ec 100644 --- a/test/modules/md/test_750_eab.py +++ b/test/modules/md/test_750_eab.py @@ -18,7 +18,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_750_001(self, env): 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 @@ def test_md_750_002(self, env): 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 @@ def test_md_750_003(self, env): 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 @@ def test_md_750_004(self, env): 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 @@ def test_md_750_005(self, env): 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 @@ def test_md_750_010(self, env): 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 @@ def test_md_750_011(self, env): 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 @@ def test_md_750_012(self, env): 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 @@ def test_md_750_013(self, env): 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 @@ def test_md_750_014(self, env): 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 @@ def test_md_750_014(self, env): 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 @@ def test_md_750_015(self, env): 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 @@ def test_md_750_015(self, env): 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 @@ def test_md_750_016(self, env): 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 @@ def test_md_750_016(self, env): 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 @@ def test_md_750_017(self, env): 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 @@ def test_md_750_022(self, env): 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) diff --git a/test/modules/md/test_751_sectigo.py b/test/modules/md/test_751_sectigo.py index 5cbd642f36e..f5a7040c308 100644 --- a/test/modules/md/test_751_sectigo.py +++ b/test/modules/md/test_751_sectigo.py @@ -46,7 +46,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_751_001(self, env): 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 @@ def test_md_751_002(self, env): 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 @@ def test_md_751_003(self, env): 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 @@ def test_md_751_004(self, env): 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 @@ def test_md_751_004(self, env): 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 @@ def test_md_751_020(self, env): 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" diff --git a/test/modules/md/test_752_zerossl.py b/test/modules/md/test_752_zerossl.py index 1884665c670..70505a3273f 100644 --- a/test/modules/md/test_752_zerossl.py +++ b/test/modules/md/test_752_zerossl.py @@ -43,7 +43,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_752_001(self, env): 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 @@ def test_md_752_002(self, env): 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 @@ def test_md_752_003(self, env): 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 @@ def test_md_752_004(self, env): 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 @@ def test_md_752_004(self, env): 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 @@ def test_md_752_020(self, env): 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" diff --git a/test/modules/md/test_780_tailscale.py b/test/modules/md/test_780_tailscale.py index 27a2df474aa..bb218f90c18 100644 --- a/test/modules/md/test_780_tailscale.py +++ b/test/modules/md/test_780_tailscale.py @@ -103,7 +103,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_780_001(self, env): 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 @@ def test_md_780_002(self, env): 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 @@ def test_md_780_003(self, env): 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 diff --git a/test/modules/md/test_790_failover.py b/test/modules/md/test_790_failover.py index 696161fd4fd..5b9d4e2fcc6 100644 --- a/test/modules/md/test_790_failover.py +++ b/test/modules/md/test_790_failover.py @@ -16,7 +16,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_790_001(self, env): 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 @@ def test_md_790_002(self, env): 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 @@ def test_md_790_003(self, env): 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) # diff --git a/test/modules/md/test_800_must_staple.py b/test/modules/md/test_800_must_staple.py index 32edee33935..8433ca8cee5 100644 --- a/test/modules/md/test_800_must_staple.py +++ b/test/modules/md/test_800_must_staple.py @@ -17,7 +17,7 @@ def _class_scope(self, env, acme): 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 @@ def configure_httpd(self, env, domain, add_lines=""): # 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 @@ def test_md_800_001(self, env): # 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 @@ def test_md_800_002(self, env): @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 @@ def test_md_800_004(self, env): 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)" diff --git a/test/modules/md/test_801_stapling.py b/test/modules/md/test_801_stapling.py index 79923379643..33485725061 100644 --- a/test/modules/md/test_801_stapling.py +++ b/test/modules/md/test_801_stapling.py @@ -12,7 +12,6 @@ @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 @@ def _class_scope(self, env, acme): 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 @@ def configure_httpd(self, env, domains=None, add_lines="", ssl_stapling=False): 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 @@ def test_md_801_001(self, env): 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 @@ def test_md_801_002(self, env): # # 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 @@ def test_md_801_002(self, env): # # 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 @@ def test_md_801_003(self, env): 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 @@ def test_md_801_004(self, env): 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 @@ def test_md_801_004(self, env): # 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 @@ def test_md_801_005(self, env): 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 @@ def test_md_801_005(self, env): 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 @@ def test_md_801_005(self, env): # 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 @@ def test_md_801_006(self, env): 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 @@ def test_md_801_006(self, env): 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 @@ def test_md_801_006(self, env): 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 @@ def test_md_801_007(self, env): 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 @@ def test_md_801_007(self, env): 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 @@ def test_md_801_008(self, env): 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 @@ def test_md_801_008(self, env): # 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 @@ def test_md_801_009(self, env): 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 @@ def test_md_801_010(self, env): 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 @@ def test_md_801_010(self, env): # scheduling. # This checks the mistaken assert() reported in # + @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: diff --git a/test/modules/md/test_810_ec.py b/test/modules/md/test_810_ec.py index 5c310180a85..f12fdb052e2 100644 --- a/test/modules/md/test_810_ec.py +++ b/test/modules/md/test_810_ec.py @@ -18,7 +18,7 @@ def _class_scope(self, env, acme): 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 @@ def set_get_pkeys(self, env, domain, pkeys, conf=None): 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 @@ def test_md_810_004(self, env): 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 @@ def test_md_810_007(self, env): 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) diff --git a/test/modules/md/test_820_locks.py b/test/modules/md/test_820_locks.py index 94369128f16..fc55914d2d4 100644 --- a/test/modules/md/test_820_locks.py +++ b/test/modules/md/test_820_locks.py @@ -37,7 +37,7 @@ def test_md_820_001(self, env): 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 @@ def test_md_820_002(self, env): 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) diff --git a/test/modules/md/test_900_notify.py b/test/modules/md/test_900_notify.py index 9d18da54114..15521c5964d 100644 --- a/test/modules/md/test_900_notify.py +++ b/test/modules/md/test_900_notify.py @@ -45,7 +45,7 @@ def test_md_900_001(self, env): 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 @@ def test_md_900_002(self, env): 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 @@ def test_md_900_010(self, env): 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 @@ def test_md_900_011(self, env): 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 @@ def test_md_900_012(self, env): 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) diff --git a/test/modules/md/test_901_message.py b/test/modules/md/test_901_message.py index c0018393e71..86475fc5717 100644 --- a/test/modules/md/test_901_message.py +++ b/test/modules/md/test_901_message.py @@ -22,7 +22,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_901_001(self, env): 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 @@ def test_md_901_002(self, env): 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 @@ def test_md_901_003(self, env): 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 @@ def test_md_901_004(self, env): 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 @@ def test_md_901_004(self, env): 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 @@ def test_md_901_010(self, env): 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 @@ def test_md_901_011(self, env): 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 @@ def test_md_901_020(self, env): 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 @@ def test_md_901_030(self, env): 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 @@ def test_md_901_030(self, env): """) 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 @@ def test_md_901_030(self, env): """) 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 @@ def test_md_901_040(self, env): 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) diff --git a/test/modules/md/test_910_cleanups.py b/test/modules/md/test_910_cleanups.py index 1971fdaecf2..08578c156f1 100644 --- a/test/modules/md/test_910_cleanups.py +++ b/test/modules/md/test_910_cleanups.py @@ -19,7 +19,7 @@ def _class_scope(self, env, acme): 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 @@ def test_md_910_01(self, env): 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 diff --git a/test/modules/md/test_920_status.py b/test/modules/md/test_920_status.py index 306b131a16d..d9babb172b3 100644 --- a/test/modules/md/test_920_status.py +++ b/test/modules/md/test_920_status.py @@ -36,7 +36,7 @@ def test_md_920_001(self, env): 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 @@ def test_md_920_001(self, env): 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 @@ def test_md_920_002(self, env): 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 @@ def test_md_920_003(self, env): 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 @@ def test_md_920_004(self, env): 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 @@ def test_md_920_010(self, env): conf = MDConf(env, std_vhosts=False, std_ports=False, text=f""" MDBaseServer on MDPortMap http:- https:{env.https_port} +MDStapling on ServerName {domain} @@ -137,7 +138,7 @@ def test_md_920_010(self, env): """) 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 @@ def test_md_920_010(self, env): 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 @@ def test_md_920_011(self, env): 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 @@ def test_md_920_020(self, env): 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 @@ def test_md_920_020(self, env): 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']