--- /dev/null
+ *) 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.
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 */
#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"
#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"
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;
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;
const char *key_change;
const char *revoke_cert;
const char *new_nonce;
+ struct apr_array_header_t *profiles;
} v2;
} api;
const char *ca_agreement;
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,
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 */
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)
{
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);
}
}
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;
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);
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);
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);
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);
/* 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);
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);
}
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) {
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;
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;
|| LIBRESSL_VERSION_NUMBER >= 0x3050000fL)
/* Missing from LibreSSL < 3.5.0 and only available since OpenSSL v1.1.x */
#include <openssl/ct.h>
+#define MD_HAVE_CT 1
+#endif
+#ifndef MD_HAVE_CT
+#define MD_HAVE_CT 0
#endif
static int initialized;
const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p)
{
+ const char *e64 = NULL;
+
#if OPENSSL_VERSION_NUMBER < 0x30000000L
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+#else
const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+#endif
if (rsa) {
const BIGNUM *e;
RSA_get0_key(rsa, NULL, &e, NULL);
- return bn64(e, p);
+ e64 = bn64(e, p);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ RSA_free(rsa);
+#endif
}
-#else
+
+#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
BIGNUM *e = NULL;
if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_E, &e)) {
- const char *e64 = bn64(e, p);
+ e64 = bn64(e, p);
BN_free(e);
- return e64;
}
#endif
- return NULL;
+
+ return e64;
}
const char *md_pkey_get_rsa_n64(md_pkey_t *pkey, apr_pool_t *p)
{
+ const char *n64 = NULL;
+
#if OPENSSL_VERSION_NUMBER < 0x30000000L
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+#else
const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+#endif
if (rsa) {
const BIGNUM *n;
RSA_get0_key(rsa, &n, NULL, NULL);
- return bn64(n, p);
+ n64 = bn64(n, p);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ RSA_free(rsa);
+#endif
}
-#else
+
+#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
BIGNUM *n = NULL;
if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_N, &n)) {
- const char *n64 = bn64(n, p);
+ n64 = bn64(n, p);
BN_free(n);
- return n64;
}
#endif
- return NULL;
+
+ return n64;
}
apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *p,
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;
return rv;
}
+#if MD_HAVE_CT
#define MD_OID_CT_SCTS_NUM "1.3.6.1.4.1.11129.2.4.2"
#define MD_OID_CT_SCTS_SNAME "CT-SCTs"
#define MD_OID_CT_SCTS_LNAME "CT Certificate SCTs"
-
-#ifndef OPENSSL_NO_CT
static int get_ct_scts_nid(void)
{
int nid = OBJ_txt2nid(MD_OID_CT_SCTS_NUM);
apr_status_t md_cert_get_ct_scts(apr_array_header_t *scts, apr_pool_t *p, const md_cert_t *cert)
{
-#ifndef OPENSSL_NO_CT
+#if MD_HAVE_CT
int nid, i, idx, critical;
STACK_OF(SCT) *sct_list;
SCT *sct_handle;
*/
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);
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) {
}
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);
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;
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;
}
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);
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 "
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);
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))) {
perms = gperms(s_fs, group);
- *pdir = NULL;
rv = fs_get_dname(pdir, &s_fs->s, group, name, p);
if ((APR_SUCCESS != rv) || (MD_SG_NONE == group)) goto cleanup;
cleanup:
if (APR_SUCCESS != rv) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "mk_group_dir %d %s",
- group, (*pdir? *pdir : (name? name : "(null)")));
+ group, (*pdir? *pdir : (name? name : "(null)")));
}
return rv;
}
* @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
* 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"
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,
/*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;
}
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);
/* Run once after configuration is set, before mod_ssl.
* Run again after mod_ssl is done.
*/
- ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_MIDDLE);
+ ap_hook_post_config(md_post_config_before_ssl, NULL, mod_ssl, APR_HOOK_FIRST);
ap_hook_post_config(md_post_config_after_ssl, mod_ssl, mod_wd, APR_HOOK_LAST);
/* Run once after a child process has been created.
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 */
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;
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;
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;
}
: (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;
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);
"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)
};
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;
}
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;
}
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 {
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 */
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,
"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
+ }
+ }
}
}
"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
+ }
+ }
}
}
@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):
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):
# test mod_md basic configurations
+import os
import re
import time
@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 = [
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 = [
MDomain example2.org www.example2.org www.example3.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
MDomain example2.org www.example2.org www.example3.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
ServerName www.example2.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
ServerAlias example2.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
</VirtualHost>
""")
conf.install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# test case: MDomain, misses one ServerAlias
def test_md_300_011a(self, env):
ServerAlias test4.not-forbidden.org
</VirtualHost>
""" % env.https_port).install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# test case: MDomain does not match any vhost
def test_md_300_012(self, env):
ServerAlias test3.not-forbidden.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
ServerName test-b.example2.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# test case: global server name as managed domain name
def test_md_300_014(self, env):
MDConf(env, text=f"""
MDomain www.{env.http_tld} www.example2.org
+ MDRenewMode manual
<VirtualHost *:12346>
ServerName www.example2.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# test case: valid pkey specification
def test_md_300_015(self, env):
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", [
ServerName secret.com
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
#
env.httpd_error_log.ignore_recent(
lognos = [
</VirtualHost>
""")
conf.install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# test case: configure more than 1 CA
@pytest.mark.parametrize("cas, should_work", [
# 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
# 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
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
# 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"])
])
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)
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)
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
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",
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="""
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")
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"])
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"])
ServerAdmin mailto:admin@testdomain2.org
</VirtualHost>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
name1 = "testdomain.org"
name2 = "testdomain2.org"
env.check_md([name1, "www." + name1, "mail." + name1], state=1, contacts=["mailto:admin@" + name1])
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
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
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
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
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
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
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
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
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
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
"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']
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'
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
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
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
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
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)
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)
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)
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)
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")
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"])
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%'
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)
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
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
MDRequireHttps %s
</MDomainSet>
""" % mode).install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
assert env.a2md(["list"]).json['output'][0]['require-https'] == mode, \
"Unexpected HTTPS require mode in store. config: {}".format(mode)
#
MDConf(env, text="""
MDomain testdomain.org www.testdomain.org mail.testdomain.org
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
assert "require-https" not in env.a2md(["list"]).json['output'][0], \
"HTTPS require still persisted in store. config: {}".format(mode)
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
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)
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)
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="""
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",
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
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"])
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
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%'
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
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
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
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
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="""
MDRequireHttps permanent
</MDomainSet>
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
assert env.a2md(["list"]).json['output'][0]['require-https'] == "permanent"
# test case: change OCSP stapling settings on existing md
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
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
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])
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
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)
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):
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
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
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
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):
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])
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)
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
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
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)
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])
# 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"
# 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
# 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
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'] == \
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)
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)
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 ---------
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)
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])
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])
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)
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)
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()
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)
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)
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
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)
import os
+import time
from datetime import timedelta
import pytest
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):
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
# 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)
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)
#
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()
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)
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)
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
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)
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
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
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):
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
#
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])
#
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
#
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])
#
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)
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'])
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)
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])
#
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])
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])
#
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)
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'])
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)
#
# restart (-> drive), check that MD job shows errors
# and that missing proto is detected
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
env.check_md(domains)
# check that acme-tls/1 is available for none of the domains
stat = env.get_md_status(domain)
assert stat["proto"]["acme-tls/1"] == []
+ MDConf(env).install()
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
+ env.httpd_error_log.ignore_recent(matches=[
+ r'.*None of offered challenge types for domain.*',
+ ], lognos=[
+ "AH10056" # challenges not available
+ ])
# test case: 2.4.40 mod_ssl stumbles over a SSLCertificateChainFile when installing
# a fallback certificate
conf.add_md(dns_list)
conf.add_vhost(dns_list)
conf.install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
assert env.await_completion([domain])
# test case: test "tls-alpn-01" without enabling 'acme-tls/1' challenge protocol
#
# 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)
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)
""")
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.
""")
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(
])
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)
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
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)
#
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])
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])
--- /dev/null
+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
+ ])
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):
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)
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)
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])
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)
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])
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])
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])
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])
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)
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)
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):
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)
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
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):
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
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
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):
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
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(
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):
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'
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'
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 [
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 [
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 [
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):
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
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
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)
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)
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']
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
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
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
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
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'
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)
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):
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"
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
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
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"
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"
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"
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):
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"
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
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
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"
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"
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"
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()
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
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
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
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):
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)
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)
#
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)
#
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):
# 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'))
# 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()
@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'))
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)"
@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')
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)
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"]
#
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"
#
# 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)"
#
# 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"
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
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)"
# 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)"
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)
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)
# 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)"
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
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
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
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)"
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
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)"
# 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]
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
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
# scheduling.
# This checks the mistaken assert() reported in
# <https://bz.apache.org/bugzilla/show_bug.cgi?id=65567>
+ @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
def test_md_801_011(self, env):
domains = [ f'test-801-011-{i}-{env.DOMAIN_SUFFIX}' for i in range(7)]
self.configure_httpd(env, domains, """
MDStapling on
LogLevel md:trace2 ssl:warn
""").install()
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
assert env.await_completion(domains, restart=False, timeout=120)
- assert env.apache_restart() == 0
+ assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
# now the certs are installed and ocsp will be retrieved
time.sleep(1)
for domain in domains:
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):
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):
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
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)
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
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)
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:"
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:"
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)
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)
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)
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):
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
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
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)
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)
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)
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):
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
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)
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)
""")
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
""")
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()
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)
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):
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
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
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']
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')
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
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
conf = MDConf(env, std_vhosts=False, std_ports=False, text=f"""
MDBaseServer on
MDPortMap http:- https:{env.https_port}
+MDStapling on
ServerName {domain}
<IfModule ssl_module>
""")
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)
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
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):
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)
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']