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/docs/manual/mod/mod_md.xml b/docs/manual/mod/mod_md.xml index b136ca0d73a..120cada0805 100644 --- a/docs/manual/mod/mod_md.xml +++ b/docs/manual/mod/mod_md.xml @@ -37,7 +37,7 @@ ACME protocol (RFC 8555). Certificates will be renewed by the module ahead of their expiration to account for disruption in internet services. There are ways to monitor the status of all - certififcates managed this way and configurations that will run your own + certificates managed this way and configurations that will run your own notification commands on renewal, expiration and errors.

Second, mod_md offers an alternate OCSP Stapling implementation. This works with @@ -495,7 +495,7 @@ MDomain example2.org auto For testing, CAs commonly offer a second service URL. The 'test' service does not give certificates valid in a browser, but are more relaxed in regard to rate limits. - This allows for verfication of your own setup before switching + This allows for verification of your own setup before switching to the production service URL.

LE Test Setup @@ -1299,7 +1299,7 @@ MDMessageCmd /etc/apache/md-message MDCertificateCheck - Set name and URL pattern for a certificate monitoring sitSet name and URL pattern for a certificate monitoring sitee + Set name and URL pattern for a certificate monitoring site. MDCertificateCheck name url server config @@ -1311,20 +1311,6 @@ MDMessageCmd /etc/apache/md-message - - MDActivationDelay - How long to delay activation of new certificates - MDActivationDelay duration - - server config - - Available in version 2.4.42 and later - -

-

-
-
- MDContactEmail Email address used for account registration @@ -1369,7 +1355,7 @@ MDMessageCmd /etc/apache/md-message

You can configure those globally or for a specific MDomain. Since these values allow anyone to register under the same account, it is - adivsable to give the configuration file restricted permissions, + advisable to give the configuration file restricted permissions, e.g. root only.

@@ -1537,4 +1523,61 @@ MDMessageCmd /etc/apache/md-message + + MDProfile + Use a specific ACME profile from the CA + MDProfile name + + server config + + Available in version 2.4.64 and later + +

+ This about a non-standard ACME extension by Let's Encrypt. +

+ Lets Encrypt announced they will add Certificate Profiles + support in their CA during 2025, beginning with their staging + servers. This, among some other details, let's you select the + lifetime of the certificates you get. The "default" profile + will keep the 90 days and a "tlsserver" profile will issue + certificates with only 6 days of validity. +

+ If you do not change your mod_md configuration, you will + continue to get the 90 days certificates. Should you believe + that a shorter lifetime is beneficial for you (and take the + risk that the renewal time is way shorter), + you can configure the profile to use via 'MDProfile tlsserver'. +

+ The profile names are defined by the CA. If a profile you + configure is not available, no profile will be used and + the certificate will be issue according to what the CA + considers default. +

+ See MDProfileMandatory + on how to disable defaults for profiles. +

+ +
+ + + MDProfileMandatory + Control if an MDProfile is mandatory. + MDProfileMandatory on|off + MDProfileMandatory off + + server config + + +

+ Controls if a MDProfile + you configure is mandatory or not. When mandatory and the CA + does not offer a configured profile, the certificate + renewal will fail. +

+ When not mandatory and a profile is not offered by the CA, + renewals will be performed without specifying a profile and + the CA will issue a certificates according to its defaults. +

+
+
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 c116bf15117..77feb55a850 100644 --- a/modules/md/md_crypt.c +++ b/modules/md/md_crypt.c @@ -1291,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; 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 79f694ad30d..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))) { 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 1fc4ba5512c..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); 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 26f295f4637..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,20 +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"] == [] - # - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10056" # None of the ACME challenge methods configured for this domain are suitable - ], - matches = [ - r'.*None of the ACME challenge types for domain. *' - ] - ) + 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 @@ -654,8 +646,7 @@ def test_md_702_042(self, env): conf.add_md(dns_list) conf.add_vhost(dns_list) conf.install() - env.httpd_error_log.clear_log() - 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 @@ -676,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) @@ -704,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) @@ -724,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. @@ -738,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( @@ -769,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) @@ -796,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 @@ -810,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) # @@ -833,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]) @@ -852,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']