summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2017-08-17 16:34:44 +0200
committerStefan Eissing <icing@apache.org>2017-08-17 16:34:44 +0200
commit2db09750f157432989309da930c56619671dee0c (patch)
treeb98009fd60193c957ca4d13ea3f275320a21d39a /modules
parent* Makefile is a generated file that is not version controlled (diff)
downloadapache2-2db09750f157432989309da930c56619671dee0c.tar.xz
apache2-2db09750f157432989309da930c56619671dee0c.zip
On the trunk:
mod_md v0.7.0: - LIVE: the real Let's Encrypt CA is now live by default! If you need to experiment, configure MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory - When existing, complete certificates are renewed, the activation of the new ones is delayed by 24 hours (or until the existing ones expire, whatever is earler) to accomodate for clients with weird clocks, refs #1. - Fixed store sync when MDCAChallenges was removed again from an MD. - Fixed crash when MD matched the base server, fixes #23 - Fixed watchgod resetting staging when server processes disappeared (e.g. reached max requests or other limits). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1805294 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules')
-rw-r--r--modules/md/md.h12
-rw-r--r--modules/md/md_acme_drive.c22
-rw-r--r--modules/md/md_cmd_reg.c2
-rw-r--r--modules/md/md_core.c29
-rw-r--r--modules/md/md_crypt.c12
-rw-r--r--modules/md/md_crypt.h1
-rw-r--r--modules/md/md_reg.c43
-rw-r--r--modules/md/md_reg.h19
-rw-r--r--modules/md/md_util.c11
-rw-r--r--modules/md/md_util.h8
-rw-r--r--modules/md/md_version.h8
-rw-r--r--modules/md/mod_md.c97
-rw-r--r--modules/md/mod_md_config.c15
-rw-r--r--modules/md/mod_md_config.h3
14 files changed, 190 insertions, 92 deletions
diff --git a/modules/md/md.h b/modules/md/md.h
index 91a2988ff2..d1a7b518df 100644
--- a/modules/md/md.h
+++ b/modules/md/md.h
@@ -80,7 +80,8 @@ struct md_t {
struct apr_array_header_t *ca_challenges; /* challenge types configured for this MD */
md_state_t state; /* state of this MD */
- apr_time_t expires; /* When the credentials for this domain expire. 0 if unknown */
+ apr_time_t valid_from; /* When the credentials start to be valid. 0 if unknown */
+ apr_time_t expires; /* When the credentials expire. 0 if unknown */
const char *cert_url; /* url where cert has been created, remember during drive */
const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */
@@ -123,6 +124,7 @@ struct md_t {
#define MD_KEY_TYPE "type"
#define MD_KEY_URL "url"
#define MD_KEY_URI "uri"
+#define MD_KEY_VALID_FROM "validFrom"
#define MD_KEY_VALUE "value"
#define MD_KEY_VERSION "version"
@@ -138,9 +140,6 @@ struct md_t {
#define MD_VAL_UPDATE(n,o,s) ((n)->s != (o)->s)
#define MD_SVAL_UPDATE(n,o,s) ((n)->s && (!(o)->s || strcmp((n)->s, (o)->s)))
-#define MD_SECS_PER_HOUR (60*60)
-#define MD_SECS_PER_DAY (24*MD_SECS_PER_HOUR)
-
/**
* Determine if the Managed Domain contains a specific domain name.
*/
@@ -213,6 +212,11 @@ md_t *md_clone(apr_pool_t *p, const md_t *src);
*/
md_t *md_copy(apr_pool_t *p, const md_t *src);
+/**
+ * Create a merged md with the settings of add overlaying the ones from base.
+ */
+md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base);
+
/**
* Convert the managed domain into a JSON representation and vice versa.
*
diff --git a/modules/md/md_acme_drive.c b/modules/md/md_acme_drive.c
index 533f60d0ce..fe8359be3d 100644
--- a/modules/md/md_acme_drive.c
+++ b/modules/md/md_acme_drive.c
@@ -651,7 +651,7 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
if (APR_SUCCESS == rv) {
/* So, we have a copy in staging, but is it a recent or an old one? */
- if (!md_is_newer(d->store, MD_SG_STAGING, MD_SG_DOMAINS, d->md->name, d->p)) {
+ if (md_is_newer(d->store, MD_SG_DOMAINS, MD_SG_STAGING, d->md->name, d->p)) {
reset_staging = 1;
}
}
@@ -785,6 +785,26 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
"%s: retrieving certificate chain", d->md->name);
rv = ad_chain_install(d);
}
+
+ if (APR_SUCCESS == rv && ad->cert) {
+ apr_time_t now = apr_time_now();
+ apr_interval_time_t max_delay, delay_activation;
+
+ /* determine when this cert should be activated */
+ d->stage_valid_from = md_cert_get_not_before(ad->cert);
+ if (d->md->state == MD_S_COMPLETE && d->md->expires > now) {
+ /**
+ * The MD is complete and un-expired. This is a renewal run.
+ * Give activation 24 hours leeway (if we have that time) to
+ * accomodate for clients with somewhat weird clocks.
+ */
+ delay_activation = apr_time_from_sec(MD_SECS_PER_DAY);
+ if (delay_activation > (max_delay = d->md->expires - now)) {
+ delay_activation = max_delay;
+ }
+ d->stage_valid_from += delay_activation;
+ }
+ }
}
out:
return rv;
diff --git a/modules/md/md_cmd_reg.c b/modules/md/md_cmd_reg.c
index 12f7018a5b..8dac2a89b5 100644
--- a/modules/md/md_cmd_reg.c
+++ b/modules/md/md_cmd_reg.c
@@ -273,7 +273,7 @@ static apr_status_t assess_and_drive(md_cmd_ctx *ctx, md_t *md)
}
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: %s", md->name, msg);
- if (APR_SUCCESS == (rv = md_reg_stage(ctx->reg, md, challenge, reset, ctx->p))) {
+ if (APR_SUCCESS == (rv = md_reg_stage(ctx->reg, md, challenge, reset, NULL, ctx->p))) {
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: loading", md->name);
rv = md_reg_load(ctx->reg, md->name, ctx->p);
diff --git a/modules/md/md_core.c b/modules/md/md_core.c
index 7506ff63ac..f9bbd78033 100644
--- a/modules/md/md_core.c
+++ b/modules/md/md_core.c
@@ -245,6 +245,26 @@ md_t *md_clone(apr_pool_t *p, const md_t *src)
return md;
}
+md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base)
+{
+ md_t *n = apr_pcalloc(p, sizeof(*n));
+
+ n->ca_url = add->ca_url? add->ca_url : base->ca_url;
+ n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto;
+ n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
+ n->drive_mode = (add->drive_mode != MD_DRIVE_DEFAULT)? add->drive_mode : base->drive_mode;
+ n->renew_window = (add->renew_window <= 0)? add->renew_window : base->renew_window;
+ n->transitive = (add->transitive < 0)? add->transitive : base->transitive;
+ if (add->ca_challenges) {
+ n->ca_challenges = apr_array_copy(p, add->ca_challenges);
+ }
+ else if (base->ca_challenges) {
+ n->ca_challenges = apr_array_copy(p, base->ca_challenges);
+ }
+ return n;
+}
+
+
/**************************************************************************************************/
/* format conversion */
@@ -271,6 +291,11 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
apr_rfc822_date(ts, md->expires);
md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_EXPIRES, NULL);
}
+ if (md->valid_from > 0) {
+ char *ts = apr_pcalloc(p, APR_RFC822_DATE_LEN);
+ apr_rfc822_date(ts, md->valid_from);
+ md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_VALID_FROM, NULL);
+ }
md_json_setl(apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
if (md->ca_challenges && md->ca_challenges->nelts > 0) {
apr_array_header_t *na;
@@ -303,6 +328,10 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
if (s && *s) {
md->expires = apr_date_parse_rfc(s);
}
+ s = md_json_dups(p, json, MD_KEY_CERT, MD_KEY_VALID_FROM, NULL);
+ if (s && *s) {
+ md->valid_from = apr_date_parse_rfc(s);
+ }
md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
diff --git a/modules/md/md_crypt.c b/modules/md/md_crypt.c
index 92b1f0c803..4e0b4d521f 100644
--- a/modules/md/md_crypt.c
+++ b/modules/md/md_crypt.c
@@ -587,6 +587,18 @@ apr_time_t md_cert_get_not_after(md_cert_t *cert)
return time;
}
+apr_time_t md_cert_get_not_before(md_cert_t *cert)
+{
+ int secs, days;
+ apr_time_t time = apr_time_now();
+ ASN1_TIME *not_after = X509_get_notBefore(cert->x509);
+
+ if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
+ time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs);
+ }
+ return time;
+}
+
int md_cert_covers_domain(md_cert_t *cert, const char *domain_name)
{
if (!cert->alt_names) {
diff --git a/modules/md/md_crypt.h b/modules/md/md_crypt.h
index 6f5c98826c..0437f897ea 100644
--- a/modules/md/md_crypt.h
+++ b/modules/md/md_crypt.h
@@ -88,6 +88,7 @@ int md_cert_has_expired(const md_cert_t *cert);
int md_cert_covers_domain(md_cert_t *cert, const char *domain_name);
int md_cert_covers_md(md_cert_t *cert, const struct md_t *md);
apr_time_t md_cert_get_not_after(md_cert_t *cert);
+apr_time_t md_cert_get_not_before(md_cert_t *cert);
apr_status_t md_cert_get_issuers_uri(const char **puri, md_cert_t *cert, apr_pool_t *p);
apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, md_cert_t *cert, apr_pool_t *p);
diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c
index 791565fd7d..04921c887f 100644
--- a/modules/md/md_reg.c
+++ b/modules/md/md_reg.c
@@ -156,7 +156,7 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
md_state_t state = MD_S_UNKNOWN;
const md_creds_t *creds;
const md_cert_t *cert;
- apr_time_t expires = 0;
+ apr_time_t expires = 0, valid_from = 0;
apr_status_t rv;
int i;
@@ -176,6 +176,7 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
md->name);
}
else {
+ valid_from = md_cert_get_not_before(creds->cert);
expires = md_cert_get_not_after(creds->cert);
if (md_cert_has_expired(creds->cert)) {
state = MD_S_EXPIRED;
@@ -221,6 +222,7 @@ out:
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "md{%s}: error", md->name);
}
md->state = state;
+ md->valid_from = valid_from;
md->expires = expires;
return rv;
}
@@ -774,13 +776,16 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
}
if (md->ca_challenges) {
md->ca_challenges = md_array_str_compact(p, md->ca_challenges, 0);
- if (smd->ca_challenges
- && !md_array_str_eq(md->ca_challenges, smd->ca_challenges, 0)) {
- smd->ca_challenges = (md->ca_challenges?
- apr_array_copy(ptemp, md->ca_challenges) : NULL);
+ if (!smd->ca_challenges
+ || !md_array_str_eq(md->ca_challenges, smd->ca_challenges, 0)) {
+ smd->ca_challenges = apr_array_copy(ptemp, md->ca_challenges);
fields |= MD_UPD_CA_CHALLENGES;
}
}
+ else if (smd->ca_challenges) {
+ smd->ca_challenges = NULL;
+ fields |= MD_UPD_CA_CHALLENGES;
+ }
if (fields) {
rv = md_reg_update(reg, ptemp, smd->name, smd, fields);
@@ -839,12 +844,14 @@ static apr_status_t run_stage(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_
int reset;
md_proto_driver_t *driver;
const char *challenge;
+ apr_time_t *pvalid_from;
apr_status_t rv;
proto = va_arg(ap, const md_proto_t *);
md = va_arg(ap, const md_t *);
challenge = va_arg(ap, const char *);
reset = va_arg(ap, int);
+ pvalid_from = va_arg(ap, apr_time_t*);
driver = apr_pcalloc(ptemp, sizeof(*driver));
rv = init_proto_driver(driver, proto, reg, md, challenge, reset, ptemp);
@@ -853,13 +860,17 @@ static apr_status_t run_stage(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "%s: run staging", md->name);
rv = proto->stage(driver);
+
+ if (APR_SUCCESS == rv && pvalid_from) {
+ *pvalid_from = driver->stage_valid_from;
+ }
}
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "%s: staging done", md->name);
return rv;
}
apr_status_t md_reg_stage(md_reg_t *reg, const md_t *md, const char *challenge,
- int reset, apr_pool_t *p)
+ int reset, apr_time_t *pvalid_from, apr_pool_t *p)
{
const md_proto_t *proto;
@@ -877,7 +888,7 @@ apr_status_t md_reg_stage(md_reg_t *reg, const md_t *md, const char *challenge,
return APR_EINVAL;
}
- return md_util_pool_vdo(run_stage, reg, p, proto, md, challenge, reset, NULL);
+ return md_util_pool_vdo(run_stage, reg, p, proto, md, challenge, reset, pvalid_from, NULL);
}
static apr_status_t run_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
@@ -950,21 +961,3 @@ apr_status_t md_reg_load(md_reg_t *reg, const char *name, apr_pool_t *p)
return md_util_pool_vdo(run_load, reg, p, name, NULL);
}
-apr_status_t md_reg_drive(md_reg_t *reg, md_t *md, const char *challenge,
- int reset, int force, apr_pool_t *p)
-{
- apr_status_t rv;
- int errored, renew;
-
- if (APR_SUCCESS == (rv = md_reg_assess(reg, md, &errored, &renew, p))) {
- if (errored) {
- rv = APR_EGENERAL;
- }
- else if (renew || force) {
- if (APR_SUCCESS == (rv = md_reg_stage(reg, md, challenge, reset, p))) {
- rv = md_reg_load(reg, md->name, p);
- }
- }
- }
- return rv;
-}
diff --git a/modules/md/md_reg.h b/modules/md/md_reg.h
index d3af921903..285ffc7ee1 100644
--- a/modules/md/md_reg.h
+++ b/modules/md/md_reg.h
@@ -135,6 +135,7 @@ struct md_proto_driver_t {
const md_t *md;
void *baton;
int reset;
+ apr_time_t stage_valid_from;
};
typedef apr_status_t md_proto_init_cb(md_proto_driver_t *driver);
@@ -154,7 +155,8 @@ struct md_proto_t {
* without interfering with any existing credentials.
*/
apr_status_t md_reg_stage(md_reg_t *reg, const md_t *md,
- const char *challenge, int reset, apr_pool_t *p);
+ const char *challenge, int reset,
+ apr_time_t *pvalid_from, apr_pool_t *p);
/**
* Load a staged set of new credentials for the managed domain. This will archive
@@ -164,19 +166,4 @@ apr_status_t md_reg_stage(md_reg_t *reg, const md_t *md,
*/
apr_status_t md_reg_load(md_reg_t *reg, const char *name, apr_pool_t *p);
-/**
- * Drive the given managed domain toward completeness.
- * This is a convenience method that combines staging and, on success, loading
- * of a new managed domain credentials set.
- *
- * @param reg the md registry
- * @param md the managed domain to drive
- * @param challenge the challenge type to use or NULL for auto selection
- * @param reset remove any staging information that has been collected
- * @param force force driving even though it looks unnecessary (e.g. not epxired)
- * @param p pool to use
- */
-apr_status_t md_reg_drive(md_reg_t *reg, md_t *md,
- const char *challenge, int reset, int force, apr_pool_t *p);
-
#endif /* mod_md_md_reg_h */
diff --git a/modules/md/md_util.c b/modules/md/md_util.c
index ac531157f0..01a0dfe945 100644
--- a/modules/md/md_util.c
+++ b/modules/md/md_util.c
@@ -746,6 +746,17 @@ apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs,
return rv;
}
+/* date/time encoding *****************************************************************************/
+
+const char *md_print_duration(apr_pool_t *p, apr_interval_time_t duration)
+{
+ int secs = (int)(apr_time_sec(duration) % MD_SECS_PER_DAY);
+ return apr_psprintf(p, "%2d:%02d:%02d hours",
+ (int)secs/MD_SECS_PER_HOUR, (int)(secs%(MD_SECS_PER_HOUR))/60,
+ (int)(secs%60));
+}
+
+
/* base64 url encoding ****************************************************************************/
static const int BASE64URL_UINT6[] = {
diff --git a/modules/md/md_util.h b/modules/md/md_util.h
index 02c72bcf02..dfdc8d89e7 100644
--- a/modules/md/md_util.h
+++ b/modules/md/md_util.h
@@ -130,4 +130,12 @@ apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs,
apr_interval_time_t timeout, apr_interval_time_t start_delay,
apr_interval_time_t max_delay, int backoff);
+/**************************************************************************************************/
+/* date/time related */
+
+#define MD_SECS_PER_HOUR (60*60)
+#define MD_SECS_PER_DAY (24*MD_SECS_PER_HOUR)
+
+const char *md_print_duration(apr_pool_t *p, apr_interval_time_t duration);
+
#endif /* md_util_h */
diff --git a/modules/md/md_version.h b/modules/md/md_version.h
index 7b2063837c..41474e853e 100644
--- a/modules/md/md_version.h
+++ b/modules/md/md_version.h
@@ -26,7 +26,7 @@
* @macro
* Version number of the md module as c string
*/
-#define MOD_MD_VERSION "0.6.1"
+#define MOD_MD_VERSION "0.7.0-git"
/**
* @macro
@@ -34,9 +34,9 @@
* 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 0x000601
+#define MOD_MD_VERSION_NUM 0x000700
-#define MD_EXPERIMENTAL 1
-#define MD_ACME_DEF_URL "https://acme-staging.api.letsencrypt.org/directory"
+#define MD_EXPERIMENTAL 0
+#define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory"
#endif /* mod_md_md_version_h */
diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c
index 1d4abeaf9b..075dbb35c4 100644
--- a/modules/md/mod_md.c
+++ b/modules/md/mod_md.c
@@ -128,7 +128,7 @@ static apr_status_t apply_to_servers(md_t *md, server_rec *base_server,
*/
memset(&r, 0, sizeof(r));
sc = NULL;
-
+
/* This MD may apply to 0, 1 or more sever_recs */
for (s = base_server; s; s = s->next) {
r.server = s;
@@ -145,14 +145,14 @@ static apr_status_t apply_to_servers(md_t *md, server_rec *base_server,
"Server %s:%d matches md %s (config %s)",
s->server_hostname, s->port, md->name, sc->name);
- if (sc->md == md) {
+ if (sc->assigned == md) {
/* already matched via another domain name */
goto next_server;
}
- else if (sc->md) {
+ else if (sc->assigned) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042)
"conflict: MD %s matches server %s, but MD %s also matches.",
- md->name, s->server_hostname, sc->md->name);
+ md->name, s->server_hostname, sc->assigned->name);
rv = APR_EINVAL;
goto next_server;
}
@@ -172,11 +172,12 @@ static apr_status_t apply_to_servers(md_t *md, server_rec *base_server,
s->server_admin);
}
/* remember */
- sc->md = md;
+ sc->assigned = md;
/* This server matches a managed domain. If it contains names or
* alias that are not in this md, a generated certificate will not match. */
- if (APR_SUCCESS == (rv2 = check_coverage(md, s->server_hostname, s, p))) {
+ if (APR_SUCCESS == (rv2 = check_coverage(md, s->server_hostname, s, p))
+ && s->names) {
for (j = 0; j < s->names->nelts; ++j) {
name = APR_ARRAY_IDX(s->names, j, const char*);
if (APR_SUCCESS != (rv2 = check_coverage(md, name, s, p))) {
@@ -268,7 +269,7 @@ static apr_status_t md_calc_md_list(apr_pool_t *p, apr_pool_t *plog,
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10039)
- "COmpleted MD[%s, CA=%s, Proto=%s, Agreement=%s, Drive=%d, renew=%ld]",
+ "Completed MD[%s, CA=%s, Proto=%s, Agreement=%s, Drive=%d, renew=%ld]",
md->name, md->ca_url, md->ca_proto, md->ca_agreement,
md->drive_mode, (long)md->renew_window);
}
@@ -440,11 +441,13 @@ typedef struct {
server_rec *s;
ap_watchdog_t *watchdog;
int all_valid;
+ apr_time_t valid_not_before;
int error_count;
int processed_count;
int error_runs;
apr_time_t next_change;
+ apr_time_t next_valid;
apr_array_header_t *mds;
md_reg_t *reg;
@@ -453,31 +456,38 @@ typedef struct {
static apr_status_t drive_md(md_watchdog *wd, md_t *md, apr_pool_t *ptemp)
{
apr_status_t rv = APR_SUCCESS;
- apr_time_t renew_time;
+ apr_time_t renew_time, now, valid_from;
int errored, renew;
char ts[APR_RFC822_DATE_LEN];
- if (APR_SUCCESS == (rv = md_reg_assess(wd->reg, md, &errored, &renew, wd->p))) {
+ if (md->state == MD_S_COMPLETE && !md->expires) {
+ /* This is our indicator that we did already renewed this managed domain
+ * successfully and only wait on the next restart for it to activate */
+ now = apr_time_now();
+ ap_log_error( APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10051)
+ "md(%s): has been renewed, should be activated in %s",
+ md->name, (md->valid_from <= now)? "about now" :
+ md_print_duration(ptemp, md->valid_from - now));
+ }
+ else if (APR_SUCCESS == (rv = md_reg_assess(wd->reg, md, &errored, &renew, wd->p))) {
if (errored) {
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10050)
"md(%s): in error state", md->name);
}
- else if (md->state == MD_S_COMPLETE && !md->expires) {
- /* This is our indicator that we did already renew this managed domain
- * successfully and only wait on the next restart for it to activate */
- ap_log_error( APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10051)
- "md(%s): has been renewed, will activate on next restart", md->name);
- }
else if (renew) {
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10052)
"md(%s): state=%d, driving", md->name, md->state);
- rv = md_reg_stage(wd->reg, md, NULL, 0, ptemp);
+ rv = md_reg_stage(wd->reg, md, NULL, 0, &valid_from, ptemp);
if (APR_SUCCESS == rv) {
md->state = MD_S_COMPLETE;
md->expires = 0;
+ md->valid_from = valid_from;
++wd->processed_count;
+ if (!wd->next_valid || wd->next_valid > valid_from) {
+ wd->next_valid = valid_from;
+ }
}
}
else {
@@ -498,7 +508,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
md_watchdog *wd = baton;
apr_status_t rv = APR_SUCCESS;
md_t *md;
- apr_interval_time_t interval;
+ apr_interval_time_t interval, now;
int i;
switch (state) {
@@ -513,9 +523,11 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
interval = apr_time_from_sec(MD_SECS_PER_DAY / 2);
wd->all_valid = 1;
+ wd->valid_not_before = 0;
wd->processed_count = 0;
wd->error_count = 0;
wd->next_change = 0;
+ wd->next_valid = 0;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10055)
"md watchdog run, auto drive %d mds", wd->mds->nelts);
@@ -523,6 +535,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
/* Check if all Managed Domains are ok or if we have to do something */
for (i = 0; i < wd->mds->nelts; ++i) {
md = APR_ARRAY_IDX(wd->mds, i, md_t *);
+
if (APR_SUCCESS != (rv = drive_md(wd, md, ptemp))) {
wd->all_valid = 0;
++wd->error_count;
@@ -534,7 +547,18 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
/* Determine when we want to run next */
wd->error_runs = wd->error_count? (wd->error_runs + 1) : 0;
if (wd->all_valid) {
- ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, wd->s, "all managed domains are valid");
+ now = apr_time_now();
+ if (wd->next_valid > now && (wd->next_valid - now < interval)) {
+ interval = wd->next_valid - now;
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s,
+ "Delaying activation of %d Managed Domain%s by %s",
+ wd->processed_count, (wd->processed_count > 1)? "s have" : " has",
+ md_print_duration(ptemp, interval));
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s,
+ "all managed domains are valid");
+ }
}
else {
/* back off duration, depending on the errors we encounter in a row */
@@ -542,7 +566,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
if (interval > apr_time_from_sec(60*60)) {
interval = apr_time_from_sec(60*60);
}
- ap_log_error( APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057)
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057)
"encountered errors for the %d. time, next run in %d seconds",
wd->error_runs, (int)apr_time_sec(interval));
}
@@ -561,10 +585,8 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
* runs. When you wake up a hibernated machine, the watchdog will not run right away
*/
if (APLOGdebug(wd->s)) {
- int secs = (int)(apr_time_sec(interval) % MD_SECS_PER_DAY);
- ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, "next run in %2d:%02d:%02d hours",
- (int)secs/MD_SECS_PER_HOUR, (int)(secs%(MD_SECS_PER_HOUR))/60,
- (int)(secs%60));
+ ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, "next run in %s",
+ md_print_duration(ptemp, interval));
}
wd_set_interval(wd->watchdog, interval, wd, run_watchdog);
break;
@@ -575,14 +597,21 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
}
if (wd->processed_count) {
+ now = apr_time_now();
+
if (wd->all_valid) {
- rv = md_server_graceful(ptemp, wd->s);
- if (APR_ENOTIMPL == rv) {
- /* self-graceful restart not supported in this setup */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10059)
- "%d Managed Domain%s been setup and changes will be "
- "activated on next (graceful) server restart.",
- wd->processed_count, (wd->processed_count > 1)? "s have" : " has");
+ if (wd->next_valid <= now) {
+ rv = md_server_graceful(ptemp, wd->s);
+ if (APR_ENOTIMPL == rv) {
+ /* self-graceful restart not supported in this setup */
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, wd->s, APLOGNO(10059)
+ "%d Managed Domain%s been setup and changes will be "
+ "activated on next (graceful) server restart.",
+ wd->processed_count, (wd->processed_count > 1)? "s have" : " has");
+ }
+ }
+ else {
+ /* activation is delayed */
}
}
else {
@@ -796,9 +825,9 @@ static int md_is_managed(server_rec *s)
{
md_srv_conf_t *conf = md_config_get(s);
- if (conf && conf->md) {
+ if (conf && conf->assigned) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10076)
- "%s: manages server %s", conf->md->name, s->server_hostname);
+ "%s: manages server %s", conf->assigned->name, s->server_hostname);
return 1;
}
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
@@ -821,11 +850,11 @@ static apr_status_t md_get_credentials(server_rec *s, apr_pool_t *p,
sc = md_config_get(s);
- if (sc && sc->md) {
+ if (sc && sc->assigned) {
assert(sc->mc);
assert(sc->mc->store);
if (APR_SUCCESS == (rv = md_reg_init(&reg, p, sc->mc->store))) {
- md = md_reg_get(reg, sc->md->name, p);
+ md = md_reg_get(reg, sc->assigned->name, p);
if (md->state != MD_S_COMPLETE) {
return APR_EAGAIN;
}
diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c
index b93d96db98..1b39d81d70 100644
--- a/modules/md/mod_md_config.c
+++ b/modules/md/mod_md_config.c
@@ -72,6 +72,7 @@ static md_srv_conf_t defconf = {
NULL,
NULL,
NULL,
+ NULL,
};
static md_mod_conf_t *mod_md_config;
@@ -147,7 +148,6 @@ void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
conf->name = apr_pstrcat(pool, "srv[", CONF_S_NAME(s), "]", NULL);
conf->s = s;
conf->mc = md_mod_conf_get(pool, 1);
- conf->md = apr_pcalloc(pool, sizeof(*conf->md));
srv_conf_props_clear(conf);
@@ -166,7 +166,6 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode;
- nsc->md = NULL;
nsc->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window;
nsc->ca_url = add->ca_url? add->ca_url : base->ca_url;
@@ -174,6 +173,9 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
nsc->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
nsc->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges)
: (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL));
+ nsc->current = NULL;
+ nsc->assigned = NULL;
+
return nsc;
}
@@ -247,6 +249,7 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char
name = ap_getword_white(cmd->pool, &arg);
domains = apr_array_make(cmd->pool, 5, sizeof(const char *));
+ add_domain_name(domains, name, cmd->pool);
while (*arg != '\0') {
name = ap_getword_white(cmd->pool, &arg);
if (NULL != set_transitive(&transitive, name)) {
@@ -267,14 +270,14 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char
* end of this section */
memcpy(&save, sc, sizeof(save));
srv_conf_props_clear(sc);
- sc->md = md;
+ sc->current = md;
if (NULL == (err = ap_walk_config(cmd->directive->first_child, cmd, cmd->context))) {
srv_conf_props_apply(md, sc, cmd->pool);
APR_ARRAY_PUSH(sc->mc->mds, const md_t *) = md;
}
- sc->md = NULL;
+ sc->current = NULL;
srv_conf_props_copy(sc, &save);
return err;
@@ -295,10 +298,10 @@ static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc,
return err;
}
- assert(sc->md);
+ assert(sc->current);
for (i = 0; i < argc; ++i) {
if (NULL != set_transitive(&sc->transitive, argv[i])) {
- add_domain_name(sc->md->domains, argv[i], cmd->pool);
+ add_domain_name(sc->current->domains, argv[i], cmd->pool);
}
}
return NULL;
diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h
index 3407c5512c..4996b5295f 100644
--- a/modules/md/mod_md_config.h
+++ b/modules/md/mod_md_config.h
@@ -58,7 +58,8 @@ typedef struct md_srv_conf_t {
const char *ca_agreement; /* accepted agreement uri between CA and user */
struct apr_array_header_t *ca_challenges; /* challenge types configured */
- md_t *md; /* post_config: MD that applies to this server or NULL */
+ md_t *current; /* md currently defined in <ManagedDomain xxx> section */
+ md_t *assigned; /* post_config: MD that applies to this server or NULL */
} md_srv_conf_t;
void *md_config_create_svr(apr_pool_t *pool, server_rec *s);