You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/10/16 12:31:44 UTC
svn commit: r1868506 [5/6] - in /httpd/httpd/trunk: ./ build/
docs/manual/mod/ modules/md/
Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Wed Oct 16 12:31:43 2019
@@ -37,8 +37,10 @@
#include "md_store.h"
#include "md_store_fs.h"
#include "md_log.h"
+#include "md_ocsp.h"
#include "md_result.h"
#include "md_reg.h"
+#include "md_status.h"
#include "md_util.h"
#include "md_version.h"
#include "md_acme.h"
@@ -47,6 +49,7 @@
#include "mod_md.h"
#include "mod_md_config.h"
#include "mod_md_drive.h"
+#include "mod_md_ocsp.h"
#include "mod_md_os.h"
#include "mod_md_status.h"
#include "mod_ssl_openssl.h"
@@ -129,7 +132,92 @@ static void init_setups(apr_pool_t *p, s
}
/**************************************************************************************************/
-/* store & registry setup */
+/* notification handling */
+
+typedef struct {
+ const char *reason; /* what the notification is about */
+ apr_time_t min_interim; /* minimum time between notifying for this reason */
+} notify_rate;
+
+static notify_rate notify_rates[] = {
+ { "renewed", apr_time_from_sec(28 * MD_SECS_PER_DAY) }, /* once per month */
+ { "installed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
+ { "expiring", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
+ { "errored", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
+ { "ocsp-renewed", apr_time_from_sec(MD_SECS_PER_DAY) }, /* once per day */
+ { "ocsp-errored", apr_time_from_sec(MD_SECS_PER_HOUR) }, /* once per hour */
+};
+
+static apr_status_t notify(md_job_t *job, const char *reason,
+ md_result_t *result, apr_pool_t *p, void *baton)
+{
+ md_mod_conf_t *mc = baton;
+ const char * const *argv;
+ const char *cmdline;
+ int exit_code;
+ apr_status_t rv = APR_SUCCESS;
+ apr_time_t min_interim = 0;
+ md_timeperiod_t since_last;
+ const char *log_msg_reason;
+ int i;
+
+ log_msg_reason = apr_psprintf(p, "message-%s", reason);
+ for (i = 0; i < (int)(sizeof(notify_rates)/sizeof(notify_rates[0])); ++i) {
+ if (!strcmp(reason, notify_rates[i].reason)) {
+ min_interim = notify_rates[i].min_interim;
+ }
+ }
+ if (min_interim > 0) {
+ since_last.start = md_job_log_get_time_of_latest(job, log_msg_reason);
+ since_last.end = apr_time_now();
+ if (md_timeperiod_length(&since_last) < min_interim) {
+ /* not enough time has passed since we sent the last notification
+ * for this reason. */
+ return APR_SUCCESS;
+ }
+ }
+
+ if (!strcmp("renewed", reason)) {
+ if (mc->notify_cmd) {
+ cmdline = apr_psprintf(p, "%s %s", mc->notify_cmd, job->mdomain);
+ apr_tokenize_to_argv(cmdline, (char***)&argv, p);
+ rv = md_util_exec(p, argv[0], argv, &exit_code);
+
+ if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
+ if (APR_SUCCESS != rv) {
+ md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10108)),
+ "MDNotifyCmd %s failed with exit code %d.",
+ mc->notify_cmd, exit_code);
+ md_result_log(result, MD_LOG_ERR);
+ md_job_log_append(job, "notify-error", result->problem, result->detail);
+ return rv;
+ }
+ }
+ md_log_perror(MD_LOG_MARK, MD_LOG_NOTICE, 0, p, APLOGNO(10059)
+ "The Managed Domain %s has been setup and changes "
+ "will be activated on next (graceful) server restart.", job->mdomain);
+ }
+ if (mc->message_cmd) {
+ cmdline = apr_psprintf(p, "%s %s %s", mc->message_cmd, reason, job->mdomain);
+ apr_tokenize_to_argv(cmdline, (char***)&argv, p);
+ rv = md_util_exec(p, argv[0], argv, &exit_code);
+
+ if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
+ if (APR_SUCCESS != rv) {
+ md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10109)),
+ "MDMessageCmd %s failed with exit code %d.",
+ mc->message_cmd, exit_code);
+ md_result_log(result, MD_LOG_ERR);
+ md_job_log_append(job, "message-error", reason, result->detail);
+ return rv;
+ }
+ }
+ md_job_log_append(job, log_msg_reason, NULL, NULL);
+ return APR_SUCCESS;
+}
+
+/**************************************************************************************************/
+/* store setup */
static apr_status_t store_file_ev(void *baton, struct md_store_t *store,
md_store_fs_ev_t ev, unsigned int group,
@@ -150,6 +238,7 @@ static apr_status_t store_file_ev(void *
switch (group) {
case MD_SG_CHALLENGES:
case MD_SG_STAGING:
+ case MD_SG_OCSP:
rv = md_make_worker_accessible(fname, p);
if (APR_ENOTIMPL != rv) {
return rv;
@@ -185,19 +274,21 @@ static apr_status_t setup_store(md_store
if (APR_SUCCESS != (rv = md_store_fs_init(pstore, p, base_dir))) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10046)"setup store for %s", base_dir);
- goto out;
+ goto leave;
}
md_store_fs_set_event_cb(*pstore, store_file_ev, s);
if (APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_CHALLENGES, p, s))
|| APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_STAGING, p, s))
|| APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_ACCOUNTS, p, s))
+ || APR_SUCCESS != (rv = check_group_dir(*pstore, MD_SG_OCSP, p, s))
) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10047)
"setup challenges directory");
+ goto leave;
}
-out:
+leave:
return rv;
}
@@ -245,15 +336,20 @@ static void merge_srv_config(md_t *md, m
if (md->must_staple < 0) {
md->must_staple = md_config_geti(md->sc, MD_CONFIG_MUST_STAPLE);
}
+ if (md->stapling < 0) {
+ md->stapling = md_config_geti(md->sc, MD_CONFIG_STAPLING);
+ }
}
-static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, apr_pool_t *p)
+static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s,
+ int *pupdates, apr_pool_t *p)
{
if (md_contains(md, domain, 0)) {
return APR_SUCCESS;
}
else if (md->transitive) {
APR_ARRAY_PUSH(md->domains, const char*) = apr_pstrdup(p, domain);
+ *pupdates |= MD_UPD_DOMAINS;
return APR_SUCCESS;
}
else {
@@ -266,40 +362,27 @@ static apr_status_t check_coverage(md_t
}
}
-static apr_status_t md_covers_server(md_t *md, server_rec *s, apr_pool_t *p)
+static apr_status_t md_cover_server(md_t *md, server_rec *s, int *pupdates, apr_pool_t *p)
{
apr_status_t rv;
const char *name;
int i;
- if (APR_SUCCESS == (rv = check_coverage(md, s->server_hostname, s, p)) && s->names) {
- for (i = 0; i < s->names->nelts; ++i) {
+ if (APR_SUCCESS == (rv = check_coverage(md, s->server_hostname, s, pupdates, p))) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+ "md[%s]: auto add, covers name %s", md->name, s->server_hostname);
+ for (i = 0; s->names && i < s->names->nelts; ++i) {
name = APR_ARRAY_IDX(s->names, i, const char*);
- if (APR_SUCCESS != (rv = check_coverage(md, name, s, p))) {
+ if (APR_SUCCESS != (rv = check_coverage(md, name, s, pupdates, p))) {
break;
}
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+ "md[%s]: auto add, covers alias %s", md->name, name);
}
}
return rv;
}
-static int matches_port_somewhere(server_rec *s, int port)
-{
- server_addr_rec *sa;
-
- for (sa = s->addrs; sa; sa = sa->next) {
- if (sa->host_port == port) {
- /* host_addr might be general (0.0.0.0) or specific, we count this as match */
- return 1;
- }
- if (sa->host_port == 0) {
- /* wildcard port, answers to all ports. Rare, but may work. */
- return 1;
- }
- }
- return 0;
-}
-
static int uses_port(server_rec *s, int port)
{
server_addr_rec *sa;
@@ -317,61 +400,94 @@ static int uses_port(server_rec *s, int
return match;
}
-static apr_status_t detect_supported_ports(md_mod_conf_t *mc, server_rec *s,
- apr_pool_t *p, int log_level)
+static apr_status_t detect_supported_protocols(md_mod_conf_t *mc, server_rec *s,
+ apr_pool_t *p, int log_level)
{
ap_listen_rec *lr;
apr_sockaddr_t *sa;
+ int can_http, can_https;
- mc->can_http = 0;
- mc->can_https = 0;
+ if (mc->can_http >= 0 && mc->can_https >= 0) goto set_and_leave;
+
+ can_http = can_https = 0;
for (lr = ap_listeners; lr; lr = lr->next) {
for (sa = lr->bind_addr; sa; sa = sa->next) {
if (sa->port == mc->local_80
&& (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
- mc->can_http = 1;
+ can_http = 1;
}
else if (sa->port == mc->local_443
&& (!lr->protocol || !strncmp("http", lr->protocol, 4))) {
- mc->can_https = 1;
+ can_https = 1;
}
}
}
-
+ if (mc->can_http < 0) mc->can_http = can_http;
+ if (mc->can_https < 0) mc->can_https = can_https;
ap_log_error(APLOG_MARK, log_level, 0, s, APLOGNO(10037)
- "server seems%s reachable via http: (port 80->%d) "
- "and%s reachable via https: (port 443->%d) ",
- mc->can_http? "" : " not", mc->local_80,
- mc->can_https? "" : " not", mc->local_443);
+ "server seems%s reachable via http: and%s reachable via https:",
+ mc->can_http? "" : " not", mc->can_https? "" : " not");
+set_and_leave:
return md_reg_set_props(mc->reg, p, mc->can_http, mc->can_https);
}
-static server_rec *get_https_server(const char *domain, server_rec *base_server)
+static server_rec *get_public_https_server(md_t *md, const char *domain, server_rec *base_server)
{
md_srv_conf_t *sc;
md_mod_conf_t *mc;
server_rec *s;
request_rec r;
+ int i;
sc = md_config_get(base_server);
mc = sc->mc;
memset(&r, 0, sizeof(r));
- for (s = base_server; s && (mc->local_443 > 0); s = s->next) {
- if (!mc->manage_base_server && s == base_server) {
- /* we shall not assign ourselves to the base server */
- continue;
- }
- r.server = s;
- if (ap_matches_request_vhost(&r, domain, s->port) && uses_port(s, mc->local_443)) {
- return s;
+ if (!mc->can_https) return NULL;
+ /* find an ssl server matching domain from MD */
+ for (s = base_server; s; s = s->next) {
+ sc = md_config_get(s);
+ if (!sc || !sc->is_ssl || !sc->assigned) continue;
+ if (base_server == s && !mc->manage_base_server) continue;
+ if (base_server != s && mc->local_443 > 0 && !uses_port(s, mc->local_443)) continue;
+ for (i = 0; i < sc->assigned->nelts; ++i) {
+ if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
+ r.server = s;
+ if (ap_matches_request_vhost(&r, domain, s->port)) {
+ return s;
+ }
+ }
}
}
return NULL;
}
+static apr_status_t auto_add_domains(md_t *md, server_rec *base_server, apr_pool_t *p)
+{
+ md_srv_conf_t *sc;
+ server_rec *s;
+ apr_status_t rv = APR_SUCCESS;
+ int updates;
+
+ /* Ad all domain names used in SSL VirtualHosts, if not already there */
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, base_server,
+ "md[%s]: auto add domains", md->name);
+ updates = 0;
+ for (s = base_server; s; s = s->next) {
+ sc = md_config_get(s);
+ if (!sc || !sc->is_ssl || !sc->assigned || sc->assigned->nelts != 1) continue;
+ if (md != APR_ARRAY_IDX(sc->assigned, 0, md_t*)) continue;
+ if (APR_SUCCESS != (rv = md_cover_server(md, s, &updates, p))) {
+ return rv;
+ }
+ }
+ return rv;
+}
+
static void init_acme_tls_1_domains(md_t *md, server_rec *base_server)
{
+ md_srv_conf_t *sc;
+ md_mod_conf_t *mc;
server_rec *s;
int i;
const char *domain;
@@ -379,10 +495,16 @@ static void init_acme_tls_1_domains(md_t
/* Collect those domains that support the "acme-tls/1" protocol. This
* is part of the MD (and not tested dynamically), since challenge selection
* may be done outside the server, e.g. in the a2md command. */
- apr_array_clear(md->acme_tls_1_domains);
+ sc = md_config_get(base_server);
+ mc = sc->mc;
+ apr_array_clear(md->acme_tls_1_domains);
for (i = 0; i < md->domains->nelts; ++i) {
domain = APR_ARRAY_IDX(md->domains, i, const char*);
- if (NULL == (s = get_https_server(domain, base_server))) {
+ s = get_public_https_server(md, domain, base_server);
+ /* If we did not find a specific virtualhost for md and manage
+ * the base_server, that one is inspected */
+ if (NULL == s && mc->manage_base_server) s = base_server;
+ if (NULL == s) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10168)
"%s: no https server_rec found for %s", md->name, domain);
continue;
@@ -398,15 +520,13 @@ static void init_acme_tls_1_domains(md_t
}
static apr_status_t link_md_to_servers(md_mod_conf_t *mc, md_t *md, server_rec *base_server,
- apr_pool_t *p, apr_pool_t *ptemp)
+ apr_pool_t *p)
{
- server_rec *s, *s_https;
+ server_rec *s;
request_rec r;
md_srv_conf_t *sc;
- apr_status_t rv = APR_SUCCESS;
int i;
- const char *domain;
- apr_array_header_t *servers;
+ const char *domain, *uri;
sc = md_config_get(base_server);
@@ -414,8 +534,6 @@ static apr_status_t link_md_to_servers(m
* is an assigned MD not equal this one, the configuration is in error.
*/
memset(&r, 0, sizeof(r));
- servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
-
for (s = base_server; s; s = s->next) {
if (!mc->manage_base_server && s == base_server) {
/* we shall not assign ourselves to the base server */
@@ -429,65 +547,15 @@ static apr_status_t link_md_to_servers(m
if (ap_matches_request_vhost(&r, domain, s->port)) {
/* Create a unique md_srv_conf_t record for this server, if there is none yet */
sc = md_config_get_unique(s, p);
+ if (!sc->assigned) sc->assigned = apr_array_make(p, 2, sizeof(md_t*));
+ APR_ARRAY_PUSH(sc->assigned, md_t*) = md;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10041)
- "Server %s:%d matches md %s (config %s)",
- s->server_hostname, s->port, md->name, sc->name);
+ "Server %s:%d matches md %s (config %s) for domain %s, "
+ "has now %d MDs",
+ s->server_hostname, s->port, md->name, sc->name,
+ domain, (int)sc->assigned->nelts);
- if (sc->assigned == md) {
- /* already matched via another domain name */
- goto next_server;
- }
- 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->assigned->name);
- return APR_EINVAL;
- }
-
- /* If this server_rec is only for http: requests. Defined
- * alias names do not matter for this MD.
- * (see gh issue https://github.com/icing/mod_md/issues/57)
- * Otherwise, if server has name or an alias not covered,
- * it is by default auto-added (config transitive).
- * If mode is "manual", a generated certificate will not match
- * all necessary names. */
- if (!mc->local_80 || !uses_port(s, mc->local_80)) {
- if (APR_SUCCESS != (rv = md_covers_server(md, s, p))) {
- return rv;
- }
- }
-
- sc->assigned = md;
- APR_ARRAY_PUSH(servers, server_rec*) = s;
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(10043)
- "Managed Domain %s applies to vhost %s:%d", md->name,
- s->server_hostname, s->port);
-
- goto next_server;
- }
- }
- next_server:
- continue;
- }
-
- if (APR_SUCCESS == rv) {
- if (apr_is_empty_array(servers)) {
- if (md->renew_mode != MD_RENEW_ALWAYS) {
- /* Not an error, but looks suspicious */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10045)
- "No VirtualHost matches Managed Domain %s", md->name);
- APR_ARRAY_PUSH(mc->unused_names, const char*) = md->name;
- }
- }
- else {
- const char *uri;
-
- /* Found matching server_rec's. Collect all 'ServerAdmin's into MD's contact list */
- apr_array_clear(md->contacts);
- for (i = 0; i < servers->nelts; ++i) {
- s = APR_ARRAY_IDX(servers, i, server_rec*);
if (s->server_admin && strcmp(DEFAULT_ADMIN, s->server_admin)) {
uri = md_util_schemify(p, s->server_admin, "mailto");
if (md_array_str_index(md->contacts, uri, 0, 0) < 0) {
@@ -496,50 +564,14 @@ static apr_status_t link_md_to_servers(m
"%s: added contact %s", md->name, uri);
}
}
+ break;
}
-
- if (md->require_https > MD_REQUIRE_OFF) {
- /* We require https for this MD, but do we have port 443 (or a mapped one)
- * available? */
- if (mc->local_443 <= 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10105)
- "MDPortMap says there is no port for https (443), "
- "but MD %s is configured to require https. This "
- "only works when a 443 port is available.", md->name);
- return APR_EINVAL;
-
- }
-
- /* Ok, we know which local port represents 443, do we have a server_rec
- * for MD that has addresses with port 443? */
- s_https = NULL;
- for (i = 0; i < servers->nelts; ++i) {
- s = APR_ARRAY_IDX(servers, i, server_rec*);
- if (matches_port_somewhere(s, mc->local_443)) {
- s_https = s;
- break;
- }
- }
-
- if (!s_https) {
- /* Did not find any server_rec that matches this MD *and* has an
- * s->addrs match for the https port. Suspicious. */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10106)
- "MD %s is configured to require https, but there seems to be "
- "no VirtualHost for it that has port %d in its address list. "
- "This looks as if it will not work.",
- md->name, mc->local_443);
- }
- }
-
}
-
}
- return rv;
+ return APR_SUCCESS;
}
-static apr_status_t link_mds_to_servers(md_mod_conf_t *mc, server_rec *s,
- apr_pool_t *p, apr_pool_t *ptemp)
+static apr_status_t link_mds_to_servers(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
{
int i;
md_t *md;
@@ -548,7 +580,7 @@ static apr_status_t link_mds_to_servers(
apr_array_clear(mc->unused_names);
for (i = 0; i < mc->mds->nelts; ++i) {
md = APR_ARRAY_IDX(mc->mds, i, md_t*);
- if (APR_SUCCESS != (rv = link_md_to_servers(mc, md, s, p, ptemp))) {
+ if (APR_SUCCESS != (rv = link_md_to_servers(mc, md, s, p))) {
goto leave;
}
}
@@ -562,7 +594,7 @@ static apr_status_t merge_mds_with_conf(
md_srv_conf_t *base_conf;
md_t *md, *omd;
const char *domain;
- const md_timeslice_t *ts;
+ md_timeslice_t *ts;
apr_status_t rv = APR_SUCCESS;
int i, j;
@@ -612,8 +644,6 @@ static apr_status_t merge_mds_with_conf(
return APR_EINVAL;
}
- init_acme_tls_1_domains(md, base_server);
-
if (APLOG_IS_LEVEL(base_server, log_level)) {
ap_log_error(APLOG_MARK, log_level, 0, base_server, APLOGNO(10039)
"Completed MD[%s, CA=%s, Proto=%s, Agreement=%s, renew-mode=%d "
@@ -635,7 +665,7 @@ static void load_staged_data(md_mod_conf
for (i = 0; i < mc->mds->nelts; ++i) {
md = APR_ARRAY_IDX(mc->mds, i, md_t *);
- result = md_result_md_make(p, md);
+ result = md_result_md_make(p, md->name);
if (APR_SUCCESS == (rv = md_reg_load_staging(mc->reg, md, mc->env, result, p))) {
ap_log_error( APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(10068)
"%s: staged set activated", md->name);
@@ -647,39 +677,84 @@ static void load_staged_data(md_mod_conf
}
}
-static apr_status_t reinit_mds(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
+static apr_status_t check_invalid_duplicates(server_rec *base_server)
{
- md_t *md;
- apr_status_t rv = APR_SUCCESS;
- int i;
+ server_rec *s;
+ md_srv_conf_t *sc;
- for (i = 0; i < mc->mds->nelts; ++i) {
- md = APR_ARRAY_IDX(mc->mds, i, md_t *);
- if (APR_SUCCESS != (rv = md_reg_reinit_state(mc->reg, (md_t*)md, p))) {
- ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10172)
- "%s: error reinitiazing from store", md->name);
- break;
+ ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, base_server,
+ "cecking duplicate ssl assignments");
+ for (s = base_server; s; s = s->next) {
+ sc = md_config_get(s);
+ if (!sc || !sc->assigned) continue;
+
+ if (sc->assigned->nelts > 1 && sc->is_ssl) {
+ /* duplicate assignment to SSL VirtualHost, not allowed */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO(10042)
+ "conflict: %d MDs match to SSL VirtualHost %s, there can at most be one.",
+ (int)sc->assigned->nelts, s->server_hostname);
+ return APR_EINVAL;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t check_usage(md_mod_conf_t *mc, md_t *md, server_rec *base_server,
+ apr_pool_t *p, apr_pool_t *ptemp)
+{
+ server_rec *s;
+ md_srv_conf_t *sc;
+ apr_status_t rv = APR_SUCCESS;
+ int i, has_ssl;
+ apr_array_header_t *servers;
+
+ (void)p;
+ servers = apr_array_make(ptemp, 5, sizeof(server_rec*));
+ has_ssl = 0;
+ for (s = base_server; s; s = s->next) {
+ sc = md_config_get(s);
+ if (!sc || !sc->assigned) continue;
+ for (i = 0; i < sc->assigned->nelts; ++i) {
+ if (md == APR_ARRAY_IDX(sc->assigned, i, md_t*)) {
+ APR_ARRAY_PUSH(servers, server_rec*) = s;
+ if (sc->is_ssl) has_ssl = 1;
+ }
+ }
+ }
+
+ if (!has_ssl && md->require_https > MD_REQUIRE_OFF) {
+ /* We require https for this MD, but do we have a SSL vhost? */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10105)
+ "MD %s does not match any VirtualHost with 'SSLEngine on', "
+ "but is configured to require https. This cannot work.", md->name);
+ }
+ if (apr_is_empty_array(servers)) {
+ if (md->renew_mode != MD_RENEW_ALWAYS) {
+ /* Not an error, but looks suspicious */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(10045)
+ "No VirtualHost matches Managed Domain %s", md->name);
+ APR_ARRAY_PUSH(mc->unused_names, const char*) = md->name;
}
}
return rv;
}
-static void init_watched_names(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
+static int init_cert_watch_status(md_mod_conf_t *mc, apr_pool_t *p, apr_pool_t *ptemp, server_rec *s)
{
- const md_t *md;
+ md_t *md;
md_result_t *result;
- int i;
+ int i, count;
/* Calculate the list of MD names which we need to watch:
* - all MDs that are used somewhere
* - all MDs in drive mode 'AUTO' that are not in 'unused_names'
*/
+ count = 0;
result = md_result_make(ptemp, APR_SUCCESS);
- apr_array_clear(mc->watched_names);
for (i = 0; i < mc->mds->nelts; ++i) {
- md = APR_ARRAY_IDX(mc->mds, i, const md_t *);
+ md = APR_ARRAY_IDX(mc->mds, i, md_t*);
md_result_set(result, APR_SUCCESS, NULL);
-
+ md->watched = 0;
if (md->state == MD_S_ERROR) {
md_result_set(result, APR_EGENERAL,
"in error state, unable to drive forward. This "
@@ -704,8 +779,10 @@ static void init_watched_names(md_mod_co
}
}
- APR_ARRAY_PUSH(mc->watched_names, const char *) = md->name;
+ md->watched = 1;
+ ++count;
}
+ return count;
}
static apr_status_t md_post_config_before_ssl(apr_pool_t *p, apr_pool_t *plog,
@@ -755,11 +832,21 @@ static apr_status_t md_post_config_befor
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10072) "setup md registry");
goto leave;
}
+ md_reg_set_notify_cb(mc->reg, notify, mc);
+ /* renew on 30% remaining /*/
+ rv = md_ocsp_reg_make(&mc->ocsp, p, store, mc->ocsp_renew_window,
+ AP_SERVER_BASEVERSION, mc->proxy_url);
+ if (APR_SUCCESS != rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10196) "setup ocsp registry");
+ goto leave;
+ }
+ md_ocsp_set_notify_cb(mc->ocsp, notify, mc);
+
init_ssl();
/* How to bootstrap this module:
- * 1. find out if we know where http: and https: requests will arrive
+ * 1. find out if we know if http: and/or https: requests will arrive
* 2. apply the now complete configuration setttings to the MDs
* 3. Link MDs to the server_recs they are used in. Detect unused MDs.
* 4. Update the store with the MDs. Change domain names, create new MDs, etc.
@@ -778,25 +865,63 @@ static apr_status_t md_post_config_befor
* 10. If this list is non-empty, setup a watchdog to run.
*/
/*1*/
- if (APR_SUCCESS != (rv = detect_supported_ports(mc, s, p, log_level))) goto leave;
+ if (APR_SUCCESS != (rv = detect_supported_protocols(mc, s, p, log_level))) goto leave;
/*2*/
if (APR_SUCCESS != (rv = merge_mds_with_conf(mc, p, s, log_level))) goto leave;
/*3*/
- if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p, ptemp))) goto leave;
+ if (APR_SUCCESS != (rv = link_mds_to_servers(mc, s, p))) goto leave;
/*4*/
- if (APR_SUCCESS != (rv = md_reg_sync(mc->reg, p, ptemp, mc->mds))) {
+ if (APR_SUCCESS != (rv = md_reg_sync_start(mc->reg, mc->mds, ptemp))) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10073)
"synching %d mds to registry", mc->mds->nelts);
goto leave;
}
/*5*/
load_staged_data(mc, s, p);
+leave:
+ return rv;
+}
+
+static apr_status_t md_post_config_after_ssl(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ md_srv_conf_t *sc;
+ apr_status_t rv = APR_SUCCESS;
+ md_mod_conf_t *mc;
+ int watched, i;
+ md_t *md;
+
+ (void)ptemp;
+ (void)plog;
+ sc = md_config_get(s);
+
/*6*/
- if (dry_run) goto leave;
+ if (!sc || !sc->mc || sc->mc->dry_run) goto leave;
+ mc = sc->mc;
+
/*7*/
- if (APR_SUCCESS != (rv = reinit_mds(mc, s, p))) goto leave;
+ if (APR_SUCCESS != (rv = check_invalid_duplicates(s))) {
+ goto leave;
+ }
+ apr_array_clear(mc->unused_names);
+ for (i = 0; i < mc->mds->nelts; ++i) {
+ md = APR_ARRAY_IDX(mc->mds, i, md_t *);
+
+ if (APR_SUCCESS != (rv = auto_add_domains(md, s, p))) {
+ goto leave;
+ }
+ init_acme_tls_1_domains(md, s);
+ if (APR_SUCCESS != (rv = check_usage(mc, md, s, p, ptemp))) {
+ goto leave;
+ }
+ if (APR_SUCCESS != (rv = md_reg_sync_finish(mc->reg, md, p, ptemp))) {
+ ap_log_error( APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10172)
+ "md[%s]: error synching to store", md->name);
+ goto leave;
+ }
+ }
/*8*/
- init_watched_names(mc, p, ptemp, s);
+ watched = init_cert_watch_status(mc, p, ptemp, s);
/*9*/
md_reg_cleanup_challenges(mc->reg, p, ptemp, mc->mds);
@@ -804,18 +929,23 @@ static apr_status_t md_post_config_befor
* and only staging/challenges may be manipulated */
md_reg_freeze_domains(mc->reg, mc->mds);
- if (mc->watched_names->nelts > 0) {
+ if (watched) {
/*10*/
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10074)
- "%d out of %d mds need watching",
- mc->watched_names->nelts, mc->mds->nelts);
+ "%d out of %d mds need watching", watched, mc->mds->nelts);
md_http_use_implementation(md_curl_get_impl(p));
- rv = md_start_watching(mc, s, p);
+ rv = md_renew_start_watching(mc, s, p);
}
else {
- ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075) "no mds to drive");
+ ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075) "no mds to supervise");
}
+
+ if (!mc->ocsp || md_ocsp_count(mc->ocsp) == 0) goto leave;
+
+ md_http_use_implementation(md_curl_get_impl(p));
+ rv = md_ocsp_start_watching(mc, s, p);
+
leave:
return rv;
}
@@ -877,20 +1007,6 @@ static int md_protocol_switch(conn_rec *
/**************************************************************************************************/
/* Access API to other httpd components */
-static int md_is_managed(server_rec *s)
-{
- md_srv_conf_t *conf = md_config_get(s);
-
- if (conf && conf->assigned) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10076)
- "%s: manages server %s", conf->assigned->name, s->server_hostname);
- return 1;
- }
- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
- "server %s is not managed", s->server_hostname);
- return 0;
-}
-
static apr_status_t setup_fallback_cert(md_store_t *store, const md_t *md,
server_rec *s, apr_pool_t *p)
{
@@ -938,23 +1054,27 @@ static apr_status_t get_certificate(serv
return APR_ENOENT;
}
+ assert(sc->mc);
+ reg = sc->mc->reg;
+ assert(reg);
+
+ sc->is_ssl = 1;
+
if (!sc->assigned) {
/* With the new hooks in mod_ssl, we are invoked for all server_rec. It is
* therefore normal, when we have nothing to add here. */
return APR_ENOENT;
}
-
- assert(sc->mc);
- reg = sc->mc->reg;
- assert(reg);
-
- md = sc->assigned;
- if (!md) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10115)
- "unable to hand out certificates, as registry can no longer "
- "find MD '%s'.", sc->assigned->name);
- return APR_ENOENT;
+ else if (sc->assigned->nelts != 1) {
+ if (!fallback) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10042)
+ "conflict: %d MDs match Virtualhost %s which uses SSL, however "
+ "there can be at most 1.",
+ (int)sc->assigned->nelts, s->server_hostname);
+ }
+ return APR_EINVAL;
}
+ md = APR_ARRAY_IDX(sc->assigned, 0, const md_t*);
rv = md_reg_get_cred_files(pkeyfile, pcertfile, reg, MD_SG_DOMAINS, md, p);
if (APR_STATUS_IS_ENOENT(rv)) {
@@ -990,12 +1110,6 @@ static apr_status_t get_certificate(serv
return rv;
}
-static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
- const char **pkeyfile, const char **pcertfile)
-{
- return get_certificate(s, p, 1, pcertfile, pkeyfile);
-}
-
static int md_add_cert_files(server_rec *s, apr_pool_t *p,
apr_array_header_t *cert_files,
apr_array_header_t *key_files)
@@ -1176,51 +1290,62 @@ static int md_require_https_maybe(reques
{
const md_srv_conf_t *sc;
apr_uri_t uri;
- const char *s;
+ const char *s, *host;
+ const md_t *md;
int status;
- if (opt_ssl_is_https && r->parsed_uri.path
- && strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) {
+ /* Requests outside the /.well-known path are subject to possible
+ * https: redirects or HSTS header additions.
+ */
+ sc = ap_get_module_config(r->server->module_config, &md_module);
+ if (!sc || !sc->assigned || !sc->assigned->nelts
+ || !opt_ssl_is_https || !r->parsed_uri.path
+ || !strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) {
+ goto declined;
+ }
- sc = ap_get_module_config(r->server->module_config, &md_module);
- if (sc && sc->assigned && sc->assigned->require_https > MD_REQUIRE_OFF) {
- if (opt_ssl_is_https(r->connection)) {
- /* Using https:
- * if 'permanent' and no one else set a HSTS header already, do it */
- if (sc->assigned->require_https == MD_REQUIRE_PERMANENT
- && sc->mc->hsts_header && !apr_table_get(r->headers_out, MD_HSTS_HEADER)) {
- apr_table_setn(r->headers_out, MD_HSTS_HEADER, sc->mc->hsts_header);
- }
+ host = ap_get_server_name_for_url(r);
+ md = md_get_for_domain(r->server, host);
+ if (!md) goto declined;
+
+ if (opt_ssl_is_https(r->connection)) {
+ /* Using https:
+ * if 'permanent' and no one else set a HSTS header already, do it */
+ if (md->require_https == MD_REQUIRE_PERMANENT
+ && sc->mc->hsts_header && !apr_table_get(r->headers_out, MD_HSTS_HEADER)) {
+ apr_table_setn(r->headers_out, MD_HSTS_HEADER, sc->mc->hsts_header);
+ }
+ }
+ else {
+ if (md->require_https > MD_REQUIRE_OFF) {
+ /* Not using https:, but require it. Redirect. */
+ if (r->method_number == M_GET) {
+ /* safe to use the old-fashioned codes */
+ status = ((MD_REQUIRE_PERMANENT == md->require_https)?
+ HTTP_MOVED_PERMANENTLY : HTTP_MOVED_TEMPORARILY);
}
else {
- /* Not using https:, but require it. Redirect. */
- if (r->method_number == M_GET) {
- /* safe to use the old-fashioned codes */
- status = ((MD_REQUIRE_PERMANENT == sc->assigned->require_https)?
- HTTP_MOVED_PERMANENTLY : HTTP_MOVED_TEMPORARILY);
- }
- else {
- /* these should keep the method unchanged on retry */
- status = ((MD_REQUIRE_PERMANENT == sc->assigned->require_https)?
- HTTP_PERMANENT_REDIRECT : HTTP_TEMPORARY_REDIRECT);
- }
-
- s = ap_construct_url(r->pool, r->uri, r);
- if (APR_SUCCESS == apr_uri_parse(r->pool, s, &uri)) {
- uri.scheme = (char*)"https";
- uri.port = 443;
- uri.port_str = (char*)"443";
- uri.query = r->parsed_uri.query;
- uri.fragment = r->parsed_uri.fragment;
- s = apr_uri_unparse(r->pool, &uri, APR_URI_UNP_OMITUSERINFO);
- if (s && *s) {
- apr_table_setn(r->headers_out, "Location", s);
- return status;
- }
+ /* these should keep the method unchanged on retry */
+ status = ((MD_REQUIRE_PERMANENT == md->require_https)?
+ HTTP_PERMANENT_REDIRECT : HTTP_TEMPORARY_REDIRECT);
+ }
+
+ s = ap_construct_url(r->pool, r->uri, r);
+ if (APR_SUCCESS == apr_uri_parse(r->pool, s, &uri)) {
+ uri.scheme = (char*)"https";
+ uri.port = 443;
+ uri.port_str = (char*)"443";
+ uri.query = r->parsed_uri.query;
+ uri.fragment = r->parsed_uri.fragment;
+ s = apr_uri_unparse(r->pool, &uri, APR_URI_UNP_OMITUSERINFO);
+ if (s && *s) {
+ apr_table_setn(r->headers_out, "Location", s);
+ return status;
}
}
}
}
+declined:
return DECLINED;
}
@@ -1248,6 +1373,7 @@ static void md_hooks(apr_pool_t *pool)
* 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_after_ssl, mod_ssl, NULL, APR_HOOK_MIDDLE);
/* Run once after a child process has been created.
*/
@@ -1263,22 +1389,18 @@ static void md_hooks(apr_pool_t *pool)
/* Status request handlers and contributors */
ap_hook_post_read_request(md_http_cert_status, NULL, mod_ssl, APR_HOOK_MIDDLE);
- APR_OPTIONAL_HOOK(ap, status_hook, md_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
+ APR_OPTIONAL_HOOK(ap, status_hook, md_domains_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
+ APR_OPTIONAL_HOOK(ap, status_hook, md_ocsp_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(md_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
-#ifdef SSL_CERT_HOOKS
- (void)md_is_managed;
- (void)md_get_certificate;
+
+#ifndef SSL_CERT_HOOKS
+#error "This version of mod_md requires Apache httpd 2.4.41 or newer."
+#endif
APR_OPTIONAL_HOOK(ssl, add_cert_files, md_add_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
APR_OPTIONAL_HOOK(ssl, add_fallback_cert_files, md_add_fallback_cert_files, NULL, NULL, APR_HOOK_MIDDLE);
APR_OPTIONAL_HOOK(ssl, answer_challenge, md_answer_challenge, NULL, NULL, APR_HOOK_MIDDLE);
-#else
- (void)md_add_cert_files;
- (void)md_add_fallback_cert_files;
- (void)md_answer_challenge;
- APR_REGISTER_OPTIONAL_FN(md_is_challenge);
- APR_REGISTER_OPTIONAL_FN(md_is_managed);
- APR_REGISTER_OPTIONAL_FN(md_get_certificate);
-#endif
+ APR_OPTIONAL_HOOK(ssl, init_stapling_status, md_ocsp_init_stapling_status, NULL, NULL, APR_HOOK_MIDDLE);
+ APR_OPTIONAL_HOOK(ssl, get_stapling_status, md_ocsp_get_stapling_status, NULL, NULL, APR_HOOK_MIDDLE);
}
Modified: httpd/httpd/trunk/modules/md/mod_md.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.dsp?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.dsp (original)
+++ httpd/httpd/trunk/modules/md/mod_md.dsp Wed Oct 16 12:31:43 2019
@@ -113,6 +113,10 @@ SOURCE=./mod_md_drive.c
# End Source File
# Begin Source File
+SOURCE=./mod_md_ocsp.c
+# End Source File
+# Begin Source File
+
SOURCE=./mod_md_os.c
# End Source File
# Begin Source File
@@ -177,6 +181,10 @@ SOURCE=./md_log.c
# End Source File
# Begin Source File
+SOURCE=./md_ocsp.c
+# End Source File
+# Begin Source File
+
SOURCE=./md_reg.c
# End Source File
# Begin Source File
Modified: httpd/httpd/trunk/modules/md/mod_md_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.c?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.c Wed Oct 16 12:31:43 2019
@@ -41,25 +41,35 @@
#define MD_DEFAULT_BASE_DIR "md"
#endif
+static md_timeslice_t def_ocsp_keep_window = {
+ 0,
+ MD_TIME_OCSP_KEEP_NORM,
+};
+
+static md_timeslice_t def_ocsp_renew_window = {
+ MD_TIME_LIFE_NORM,
+ MD_TIME_RENEW_WINDOW_DEF,
+};
+
/* Default settings for the global conf */
static md_mod_conf_t defmc = {
NULL, /* list of mds */
#if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
- NULL, /* base dir by default state-dir-relative */
+ NULL, /* base dirm by default state-dir-relative */
#else
MD_DEFAULT_BASE_DIR,
#endif
NULL, /* proxy url for outgoing http */
- NULL, /* md_reg */
+ NULL, /* md_reg_t */
+ NULL, /* md_ocsp_reg_t */
80, /* local http: port */
443, /* local https: port */
- 0, /* can http: */
- 0, /* can https: */
+ -1, /* can http: */
+ -1, /* can https: */
0, /* manage base server */
MD_HSTS_MAX_AGE_DEFAULT, /* hsts max-age */
NULL, /* hsts headers */
NULL, /* unused names */
- NULL, /* watched names */
NULL, /* init errors hash */
NULL, /* notify cmd */
NULL, /* message cmd */
@@ -67,6 +77,10 @@ static md_mod_conf_t defmc = {
0, /* dry_run flag */
1, /* server_status_enabled */
1, /* certificate_status_enabled */
+ &def_ocsp_keep_window, /* default time to keep ocsp responses */
+ &def_ocsp_renew_window, /* default time to renew ocsp responses */
+ "crt.sh", /* default cert checker site name */
+ "https://crt.sh?q=", /* default cert checker site url */
};
static md_timeslice_t def_renew_window = {
@@ -94,8 +108,11 @@ static md_srv_conf_t defconf = {
"ACME", /* ca protocol */
NULL, /* ca agreemnent */
NULL, /* ca challenges array */
+ 0, /* stapling */
+ 1, /* staple others */
NULL, /* currently defined md */
NULL, /* assigned md, post config */
+ 0, /* is_ssl, set during mod_ssl post_config */
};
static md_mod_conf_t *mod_md_config;
@@ -118,7 +135,6 @@ static md_mod_conf_t *md_mod_conf_get(ap
memcpy(mod_md_config, &defmc, sizeof(*mod_md_config));
mod_md_config->mds = apr_array_make(pool, 5, sizeof(const md_t *));
mod_md_config->unused_names = apr_array_make(pool, 5, sizeof(const md_t *));
- mod_md_config->watched_names = apr_array_make(pool, 5, sizeof(const md_t *));
mod_md_config->env = apr_table_make(pool, 10);
mod_md_config->init_errors = apr_hash_make(pool);
@@ -143,6 +159,8 @@ static void srv_conf_props_clear(md_srv_
sc->ca_proto = NULL;
sc->ca_agreement = NULL;
sc->ca_challenges = NULL;
+ sc->stapling = DEF_VAL;
+ sc->staple_others = DEF_VAL;
}
static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
@@ -158,6 +176,8 @@ static void srv_conf_props_copy(md_srv_c
to->ca_proto = from->ca_proto;
to->ca_agreement = from->ca_agreement;
to->ca_challenges = from->ca_challenges;
+ to->stapling = from->stapling;
+ to->staple_others = from->staple_others;
}
static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p)
@@ -173,6 +193,7 @@ static void srv_conf_props_apply(md_t *m
if (from->ca_proto) md->ca_proto = from->ca_proto;
if (from->ca_agreement) md->ca_agreement = from->ca_agreement;
if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges);
+ if (from->stapling != DEF_VAL) md->stapling = from->stapling;
}
void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
@@ -198,7 +219,6 @@ static void *md_config_merge(apr_pool_t
nsc = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t));
nsc->name = name;
nsc->mc = add->mc? add->mc : base->mc;
- nsc->assigned = add->assigned? add->assigned : base->assigned;
nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
nsc->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
@@ -213,8 +233,9 @@ static void *md_config_merge(apr_pool_t
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->stapling = (add->stapling != DEF_VAL)? add->stapling : base->stapling;
+ nsc->staple_others = (add->staple_others != DEF_VAL)? add->staple_others : base->staple_others;
nsc->current = NULL;
- nsc->assigned = NULL;
return nsc;
}
@@ -487,6 +508,30 @@ static const char *md_config_set_must_st
return set_on_off(&config->must_staple, value, cmd->pool);
}
+static const char *md_config_set_stapling(cmd_parms *cmd, void *dc, const char *value)
+{
+ md_srv_conf_t *config = md_config_get(cmd->server);
+ const char *err;
+
+ (void)dc;
+ if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+ return set_on_off(&config->stapling, value, cmd->pool);
+}
+
+static const char *md_config_set_staple_others(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 = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+ return set_on_off(&config->staple_others, value, cmd->pool);
+}
+
static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const char *value)
{
md_srv_conf_t *config = md_config_get(cmd->server);
@@ -820,6 +865,69 @@ static const char *md_config_set_certifi
return set_on_off(&sc->mc->certificate_status_enabled, value, cmd->pool);
}
+static const char *md_config_set_ocsp_keep_window(cmd_parms *cmd, void *dc, const char *value)
+{
+ md_srv_conf_t *sc = md_config_get(cmd->server);
+ const char *err;
+
+ (void)dc;
+ if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+ err = md_timeslice_parse(&sc->mc->ocsp_keep_window, cmd->pool, value, MD_TIME_OCSP_KEEP_NORM);
+ if (err) return apr_psprintf(cmd->pool, "MDStaplingKeepResponse %s", err);
+ return NULL;
+}
+
+static const char *md_config_set_ocsp_renew_window(cmd_parms *cmd, void *dc, const char *value)
+{
+ md_srv_conf_t *sc = md_config_get(cmd->server);
+ const char *err;
+
+ (void)dc;
+ if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+ err = md_timeslice_parse(&sc->mc->ocsp_renew_window, cmd->pool, value, MD_TIME_LIFE_NORM);
+ if (!err && sc->mc->ocsp_renew_window->norm
+ && (sc->mc->ocsp_renew_window->len >= sc->mc->ocsp_renew_window->norm)) {
+ err = "with a length of 100% or more is not allowed.";
+ }
+ if (err) return apr_psprintf(cmd->pool, "MDStaplingRenewWindow %s", err);
+ return NULL;
+}
+
+static const char *md_config_set_cert_check(cmd_parms *cmd, void *dc,
+ const char *name, const char *url)
+{
+ md_srv_conf_t *sc = md_config_get(cmd->server);
+ const char *err;
+
+ (void)dc;
+ if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+ sc->mc->cert_check_name = name;
+ sc->mc->cert_check_url = url;
+ return NULL;
+}
+
+static const char *md_config_set_activation_delay(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ md_srv_conf_t *sc = md_config_get(cmd->server);
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ apr_interval_time_t delay;
+
+ (void)mconfig;
+ if (err) {
+ return err;
+ }
+ if (md_duration_parse(&delay, arg, "d") != APR_SUCCESS) {
+ return "unrecognized duration format";
+ }
+ apr_table_set(sc->mc->env, MD_KEY_ACTIVATION_DELAY, md_duration_format(cmd->pool, delay));
+ return NULL;
+}
const command_rec md_cmds[] = {
AP_INIT_TAKE1("MDCertificateAuthority", md_config_set_ca, NULL, RSRC_CONF,
@@ -860,7 +968,7 @@ const command_rec md_cmds[] = {
AP_INIT_TAKE1("MDStoreDir", md_config_set_store_dir, NULL, RSRC_CONF,
"the directory for file system storage of managed domain data."),
AP_INIT_TAKE1("MDRenewWindow", md_config_set_renew_window, NULL, RSRC_CONF,
- "Time length for renewal before certificate expires (defaults to days)"),
+ "Time length for renewal before certificate expires (defaults to days)."),
AP_INIT_TAKE1("MDRequireHttps", md_config_set_require_https, NULL, RSRC_CONF,
"Redirect non-secure requests to the https: equivalent."),
AP_INIT_RAW_ARGS("MDNotifyCmd", md_config_set_notify_cmd, NULL, RSRC_CONF,
@@ -881,6 +989,18 @@ const command_rec md_cmds[] = {
"When less time remains for a certificate, send our/log a warning (defaults to days)"),
AP_INIT_RAW_ARGS("MDMessageCmd", md_config_set_msg_cmd, NULL, RSRC_CONF,
"Set the command run when a message about a domain is issued."),
+ AP_INIT_TAKE1("MDStapling", md_config_set_stapling, NULL, RSRC_CONF,
+ "Enable/Disable OCSP Stapling for this/all Managed Domain(s)."),
+ AP_INIT_TAKE1("MDStapleOthers", md_config_set_staple_others, NULL, RSRC_CONF,
+ "Enable/Disable OCSP Stapling for certificates not in Managed Domains."),
+ AP_INIT_TAKE1("MDStaplingKeepResponse", md_config_set_ocsp_keep_window, NULL, RSRC_CONF,
+ "The amount of time to keep an OCSP response in the store."),
+ AP_INIT_TAKE1("MDStaplingRenewWindow", md_config_set_ocsp_renew_window, NULL, RSRC_CONF,
+ "Time length for renewal before OCSP responses expire (defaults to days)."),
+ AP_INIT_TAKE2("MDCertificateCheck", md_config_set_cert_check, NULL, RSRC_CONF,
+ "Set name and URL pattern for a certificate monitoring site."),
+ AP_INIT_TAKE1("MDActivationDelay", md_config_set_activation_delay, NULL, RSRC_CONF,
+ "How long to delay activation of new certificates"),
AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
};
@@ -897,7 +1017,7 @@ apr_status_t md_config_post_config(serve
if (mc->hsts_max_age > 0) {
mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age);
}
-
+
#if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
if (mc->base_dir == NULL) {
mc->base_dir = ap_state_dir_relative(p, MD_DEFAULT_BASE_DIR);
@@ -913,6 +1033,7 @@ static md_srv_conf_t *config_get_int(ser
ap_assert(sc);
if (sc->s != s && p) {
sc = md_config_merge(p, &defconf, sc);
+ sc->s = s;
sc->name = apr_pstrcat(p, CONF_S_NAME(s), sc->name, NULL);
sc->mc = md_mod_conf_get(p, 1);
ap_set_module_config(s->module_config, &md_module, sc);
@@ -961,22 +1082,22 @@ int md_config_geti(const md_srv_conf_t *
switch (var) {
case MD_CONFIG_DRIVE_MODE:
return (sc->renew_mode != DEF_VAL)? sc->renew_mode : defconf.renew_mode;
- case MD_CONFIG_LOCAL_80:
- return sc->mc->local_80;
- case MD_CONFIG_LOCAL_443:
- return sc->mc->local_443;
case MD_CONFIG_TRANSITIVE:
return (sc->transitive != DEF_VAL)? sc->transitive : defconf.transitive;
case MD_CONFIG_REQUIRE_HTTPS:
return (sc->require_https != MD_REQUIRE_UNSET)? sc->require_https : defconf.require_https;
case MD_CONFIG_MUST_STAPLE:
return (sc->must_staple != DEF_VAL)? sc->must_staple : defconf.must_staple;
+ case MD_CONFIG_STAPLING:
+ 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;
default:
return 0;
}
}
-void md_config_get_timespan(const md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var)
+void md_config_get_timespan(md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var)
{
switch (var) {
case MD_CONFIG_RENEW_WINDOW:
@@ -990,3 +1111,19 @@ void md_config_get_timespan(const md_tim
}
}
+const md_t *md_get_for_domain(server_rec *s, const char *domain)
+{
+ md_srv_conf_t *sc;
+ const md_t *md;
+ int i;
+
+ sc = md_config_get(s);
+ for (i = 0; sc && sc->assigned && i < sc->assigned->nelts; ++i) {
+ md = APR_ARRAY_IDX(sc->assigned, i, const md_t*);
+ if (md_contains(md, domain, 0)) goto leave;
+ }
+ md = NULL;
+leave:
+ return md;
+}
+
Modified: httpd/httpd/trunk/modules/md/mod_md_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_config.h?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md_config.h Wed Oct 16 12:31:43 2019
@@ -20,6 +20,7 @@
struct apr_hash_t;
struct md_store_t;
struct md_reg_t;
+struct md_ocsp_reg_t;
struct md_pkey_spec_t;
typedef enum {
@@ -28,8 +29,6 @@ typedef enum {
MD_CONFIG_BASE_DIR,
MD_CONFIG_CA_AGREEMENT,
MD_CONFIG_DRIVE_MODE,
- MD_CONFIG_LOCAL_80,
- MD_CONFIG_LOCAL_443,
MD_CONFIG_RENEW_WINDOW,
MD_CONFIG_WARN_WINDOW,
MD_CONFIG_TRANSITIVE,
@@ -38,6 +37,8 @@ typedef enum {
MD_CONFIG_MUST_STAPLE,
MD_CONFIG_NOTIFY_CMD,
MD_CONFIG_MESSGE_CMD,
+ MD_CONFIG_STAPLING,
+ MD_CONFIG_STAPLE_OTHERS,
} md_config_var_t;
typedef struct md_mod_conf_t md_mod_conf_t;
@@ -45,7 +46,8 @@ struct md_mod_conf_t {
apr_array_header_t *mds; /* all md_t* defined in the config, shared */
const char *base_dir; /* base dir for store */
const char *proxy_url; /* proxy url to use (or NULL) */
- struct md_reg_t *reg; /* md registry instance, singleton, shared */
+ struct md_reg_t *reg; /* md registry instance */
+ struct md_ocsp_reg_t *ocsp; /* ocsp status registry */
int local_80; /* On which port http:80 arrives */
int local_443; /* On which port https:443 arrives */
@@ -55,7 +57,6 @@ struct md_mod_conf_t {
int hsts_max_age; /* max-age of HSTS (rfc6797) header */
const char *hsts_header; /* computed HTST header to use or NULL */
apr_array_header_t *unused_names; /* post config, names of all MDs not assigned to a vhost */
- apr_array_header_t *watched_names; /* post config, names of all MDs that we need to watch */
struct apr_hash_t *init_errors; /* init errors reported with MD name as key */
const char *notify_cmd; /* notification command to execute on signup/renew */
@@ -64,6 +65,10 @@ struct md_mod_conf_t {
int dry_run; /* != 0 iff config dry run */
int server_status_enabled; /* if module should add to server-status handler */
int certificate_status_enabled; /* if module should expose /.httpd/certificate-status */
+ md_timeslice_t *ocsp_keep_window; /* time that we keep ocsp responses around */
+ md_timeslice_t *ocsp_renew_window; /* time before exp. that we start renewing ocsp resp. */
+ const char *cert_check_name; /* name of the linked certificate check site */
+ const char *cert_check_url; /* url "template for" checking a certificate */
};
typedef struct md_srv_conf_t {
@@ -76,16 +81,20 @@ typedef struct md_srv_conf_t {
int renew_mode; /* mode of obtaining credentials */
int must_staple; /* certificates should set the OCSP Must Staple extension */
struct md_pkey_spec_t *pkey_spec; /* specification for generating private keys */
- const md_timeslice_t *renew_window; /* time before expiration that starts renewal */
- const md_timeslice_t *warn_window; /* time before expiration that warning are sent out */
+ md_timeslice_t *renew_window; /* time before expiration that starts renewal */
+ md_timeslice_t *warn_window; /* time before expiration that warning are sent out */
const char *ca_url; /* url of CA certificate service */
const char *ca_proto; /* protocol used vs CA (e.g. ACME) */
const char *ca_agreement; /* accepted agreement uri between CA and user */
struct apr_array_header_t *ca_challenges; /* challenge types configured */
+
+ int stapling; /* OCSP stapling enabled */
+ int staple_others; /* Provide OCSP stapling for non-MD certificates */
md_t *current; /* md currently defined in <MDomainSet xxx> section */
- md_t *assigned; /* post_config: MD that applies to this server or NULL */
+ struct apr_array_header_t *assigned; /* post_config: MDs that apply to this server */
+ int is_ssl; /* SSLEngine is enabled here */
} md_srv_conf_t;
void *md_config_create_svr(apr_pool_t *pool, server_rec *s);
@@ -106,7 +115,8 @@ md_srv_conf_t *md_config_get_unique(serv
const char *md_config_gets(const md_srv_conf_t *config, md_config_var_t var);
int md_config_geti(const md_srv_conf_t *config, md_config_var_t var);
-void md_config_get_timespan(const md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var);
+void md_config_get_timespan(md_timeslice_t **pspan, const md_srv_conf_t *sc, md_config_var_t var);
+const md_t *md_get_for_domain(server_rec *s, const char *domain);
#endif /* md_config_h */
Modified: httpd/httpd/trunk/modules/md/mod_md_drive.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_drive.c?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_drive.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md_drive.c Wed Oct 16 12:31:43 2019
@@ -53,13 +53,13 @@
/**************************************************************************************************/
/* watchdog based impl. */
-#define MD_WATCHDOG_NAME "_md_"
+#define MD_RENEW_WATCHDOG_NAME "_md_renew_"
static APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *wd_get_instance;
static APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *wd_register_callback;
static APR_OPTIONAL_FN_TYPE(ap_watchdog_set_callback_interval) *wd_set_interval;
-struct md_drive_ctx {
+struct md_renew_ctx_t {
apr_pool_t *p;
server_rec *s;
md_mod_conf_t *mc;
@@ -68,253 +68,92 @@ struct md_drive_ctx {
apr_array_header_t *jobs;
};
-typedef struct {
- apr_pool_t *p;
- md_job_t *job;
- md_reg_t *reg;
- md_result_t *last;
- apr_time_t last_save;
-} md_job_result_ctx;
-
-static void job_result_update(md_result_t *result, void *data)
-{
- md_job_result_ctx *ctx = data;
- apr_time_t now;
- const char *msg, *sep;
-
- if (md_result_cmp(ctx->last, result)) {
- now = apr_time_now();
- md_result_assign(ctx->last, result);
- if (result->activity || result->problem || result->detail) {
- msg = sep = "";
- if (result->activity) {
- msg = apr_psprintf(result->p, "%s", result->activity);
- sep = ": ";
- }
- if (result->detail) {
- msg = apr_psprintf(result->p, "%s%s%s", msg, sep, result->detail);
- sep = ", ";
- }
- if (result->problem) {
- msg = apr_psprintf(result->p, "%s%sproblem: %s", msg, sep, result->problem);
- sep = " ";
- }
- md_job_log_append(ctx->job, "progress", NULL, msg);
-
- if (apr_time_msec(now - ctx->last_save) > 500) {
- md_job_save(ctx->job, ctx->reg, MD_SG_STAGING, result, ctx->p);
- ctx->last_save = now;
- }
- }
- }
-}
-
-static void job_result_observation_start(md_job_t *job, md_result_t *result,
- md_reg_t *reg, apr_pool_t *p)
-{
- md_job_result_ctx *ctx;
-
- ctx = apr_pcalloc(p, sizeof(*ctx));
- ctx->p = p;
- ctx->job = job;
- ctx->reg = reg;
- ctx->last = md_result_md_make(p, APR_SUCCESS);
- md_result_assign(ctx->last, result);
- md_result_on_change(result, job_result_update, ctx);
-}
-
-static void job_result_observation_end(md_job_t *job, md_result_t *result)
-{
- (void)job;
- md_result_on_change(result, NULL, NULL);
-}
-
-static apr_time_t calc_err_delay(int err_count)
-{
- apr_time_t delay = 0;
-
- if (err_count > 0) {
- /* back off duration, depending on the errors we encounter in a row */
- delay = apr_time_from_sec(5 << (err_count - 1));
- if (delay > apr_time_from_sec(60*60)) {
- delay = apr_time_from_sec(60*60);
- }
- }
- return delay;
-}
-
-static apr_status_t send_notification(md_drive_ctx *dctx, md_job_t *job, const md_t *md,
- const char *reason, md_result_t *result, apr_pool_t *ptemp)
-{
- const char * const *argv;
- const char *cmdline;
- int exit_code;
- apr_status_t rv = APR_SUCCESS;
-
- if (!strcmp("renewed", reason)) {
- if (dctx->mc->notify_cmd) {
- cmdline = apr_psprintf(ptemp, "%s %s", dctx->mc->notify_cmd, md->name);
- apr_tokenize_to_argv(cmdline, (char***)&argv, ptemp);
- rv = md_util_exec(ptemp, argv[0], argv, &exit_code);
-
- if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
- if (APR_SUCCESS != rv) {
- if (!result) result = md_result_make(ptemp, rv);
- md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10108)),
- "MDNotifyCmd %s failed with exit code %d.",
- dctx->mc->notify_cmd, exit_code);
- md_result_log(result, MD_LOG_ERR);
- md_job_log_append(job, "notify-error", result->problem, result->detail);
- goto leave;
- }
- }
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, dctx->s, APLOGNO(10059)
- "The Managed Domain %s has been setup and changes "
- "will be activated on next (graceful) server restart.", md->name);
- }
- if (dctx->mc->message_cmd) {
- cmdline = apr_psprintf(ptemp, "%s %s %s", dctx->mc->message_cmd, reason, md->name);
- ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, dctx->s, "Message command: %s", cmdline);
- apr_tokenize_to_argv(cmdline, (char***)&argv, ptemp);
- rv = md_util_exec(ptemp, argv[0], argv, &exit_code);
-
- if (APR_SUCCESS == rv && exit_code) rv = APR_EGENERAL;
- if (APR_SUCCESS != rv) {
- if (!result) result = md_result_make(ptemp, rv);
- md_result_problem_printf(result, rv, MD_RESULT_LOG_ID(APLOGNO(10109)),
- "MDMessageCmd %s failed with exit code %d.",
- dctx->mc->notify_cmd, exit_code);
- md_result_log(result, MD_LOG_ERR);
- md_job_log_append(job, "message-error", reason, result->detail);
- goto leave;
- }
- }
-leave:
- return rv;
-}
-
-static void check_expiration(md_drive_ctx *dctx, md_job_t *job, const md_t *md, apr_pool_t *ptemp)
-{
- md_timeperiod_t since_last;
-
- ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, dctx->s, "md(%s): check expiration", md->name);
- if (!md_reg_should_warn(dctx->mc->reg, md, dctx->p)) return;
-
- /* Sends these out at most once per day */
- since_last.start = md_job_log_get_time_of_latest(job, "message-expiring");
- since_last.end = apr_time_now();
-
- if (md_timeperiod_length(&since_last) >= apr_time_from_sec(MD_SECS_PER_DAY)) {
- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, dctx->s,
- "md(%s): message expiration warning", md->name);
- send_notification(dctx, job, md, "expiring", NULL, ptemp);
- }
-}
-
-static void process_drive_job(md_drive_ctx *dctx, md_job_t *job, apr_pool_t *ptemp)
+static void process_drive_job(md_renew_ctx_t *dctx, md_job_t *job, apr_pool_t *ptemp)
{
const md_t *md;
md_result_t *result;
- int error_run = 0, fatal_run = 0, save = 0;
apr_status_t rv;
- md_job_load(job, dctx->mc->reg, MD_SG_STAGING, ptemp);
+ md_job_load(job);
/* Evaluate again on loaded value. Values will change when watchdog switches child process */
if (apr_time_now() < job->next_run) return;
- md = md_get_by_name(dctx->mc->mds, job->name);
+ job->next_run = 0;
+ if (job->finished && job->notified) {
+ /* finished and notification handled, nothing to do. */
+ goto leave;
+ }
+
+ md = md_get_by_name(dctx->mc->mds, job->mdomain);
AP_DEBUG_ASSERT(md);
- result = md_result_md_make(ptemp, md);
- if (job->last_result) md_result_assign(result, job->last_result);
+ result = md_result_md_make(ptemp, md->name);
+ if (job->last_result) md_result_assign(result, job->last_result);
if (md->state == MD_S_MISSING_INFORMATION) {
/* Missing information, this will not change until configuration
* is changed and server reloaded. */
- fatal_run = 1;
+ job->fatal_error = 1;
+ job->next_run = 0;
goto leave;
}
- while (md_will_renew_cert(md)) {
- if (job->finished) {
- job->next_run = 0;
- /* Finished jobs might take a while before the results become valid.
- * If that is in the future, request to run then */
- if (apr_time_now() < job->valid_from) {
- job->next_run = job->valid_from;
- }
- else if (md_job_log_get_time_of_latest(job, "notified") == 0) {
- rv = send_notification(dctx, job, md, "renewed", result, ptemp);
- if (APR_SUCCESS == rv) {
- md_job_log_append(job, "notified", NULL, NULL);
- save = 1;
- }
- else {
- /* we treat this as an error that triggers retries */
- error_run = 1;
- }
- }
- goto leave;
- }
-
- if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) {
- ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10053)
- "md(%s): no need to renew yet", job->name);
- job->next_run = 0;
- goto leave;
- }
-
+ if (md_will_renew_cert(md)) {
/* Renew the MDs credentials in a STAGING area. Might be invoked repeatedly
* without discarding previous/intermediate results.
* Only returns SUCCESS when the renewal is complete, e.g. STAGING as a
* complete set of new credentials.
*/
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10052)
- "md(%s): state=%d, driving", job->name, md->state);
- md_job_log_append(job, "renewal-start", NULL, NULL);
- /* observe result changes and persist them with limited frequency */
- job_result_observation_start(job, result, dctx->mc->reg, ptemp);
-
+ "md(%s): state=%d, driving", job->mdomain, md->state);
+
+ if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) {
+ ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, dctx->s, APLOGNO(10053)
+ "md(%s): no need to renew", job->mdomain);
+ goto expiry;
+ }
+
+ md_job_start_run(job, result, md_reg_store_get(dctx->mc->reg));
md_reg_renew(dctx->mc->reg, md, dctx->mc->env, 0, result, ptemp);
+ md_job_end_run(job, result);
- job_result_observation_end(job, result);
- if (APR_SUCCESS != result->status) {
+ if (APR_SUCCESS == result->status) {
+ /* Finished jobs might take a while before the results become valid.
+ * If that is in the future, request to run then */
+ if (apr_time_now() < result->ready_at) {
+ md_job_retry_at(job, result->ready_at);
+ goto leave;
+ }
+
+ if (!job->notified) md_job_notify(job, "renewed", result);
+ }
+ else {
ap_log_error( APLOG_MARK, APLOG_ERR, result->status, dctx->s, APLOGNO(10056)
- "processing %s: %s", job->name, result->detail);
- error_run = 1;
+ "processing %s: %s", job->mdomain, result->detail);
md_job_log_append(job, "renewal-error", result->problem, result->detail);
- send_notification(dctx, job, md, "errored", result, ptemp);
- goto leave;
+ md_job_holler(job, "errored");
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, dctx->s, APLOGNO(10057)
+ "%s: encountered error for the %d. time, next run in %s",
+ job->mdomain, job->error_runs,
+ md_duration_print(ptemp, job->next_run - apr_time_now()));
}
-
- job->finished = 1;
- job->valid_from = result->ready_at;
- job->error_runs = 0;
- md_job_log_append(job, "renewal-finish", NULL, NULL);
- save = 1;
}
-
-leave:
- if (!job->finished) {
- check_expiration(dctx, job, md, ptemp);
- }
-
- if (fatal_run) {
- save = 1;
- job->next_run = 0;
+
+expiry:
+ if (!job->finished && md_reg_should_warn(dctx->mc->reg, md, dctx->p)) {
+ ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, dctx->s,
+ "md(%s): warn about expiration", md->name);
+ md_job_start_run(job, result, md_reg_store_get(dctx->mc->reg));
+ if (APR_SUCCESS == md_job_notify(job, "expiring", result)) {
+ md_result_set(result, APR_SUCCESS, NULL);
+ }
+ md_job_end_run(job, result);
}
- if (error_run) {
- ++job->error_runs;
- save = 1;
- job->next_run = apr_time_now() + calc_err_delay(job->error_runs);
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, dctx->s, APLOGNO(10057)
- "%s: encountered error for the %d. time, next run in %s",
- job->name, job->error_runs,
- md_duration_print(ptemp, job->next_run - apr_time_now()));
- }
- if (save) {
- apr_status_t rv2 = md_job_save(job, dctx->mc->reg, MD_SG_STAGING, result, ptemp);
- ap_log_error(APLOG_MARK, APLOG_TRACE1, rv2, dctx->s, "%s: saving job props", job->name);
+
+leave:
+ if (job->dirty) {
+ rv = md_job_save(job, result, ptemp);
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, dctx->s, "%s: saving job props", job->mdomain);
}
}
@@ -337,7 +176,7 @@ static apr_time_t next_run_default(void)
static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
{
- md_drive_ctx *dctx = baton;
+ md_renew_ctx_t *dctx = baton;
md_job_t *job;
apr_time_t next_run, wait_time;
int i;
@@ -390,19 +229,18 @@ static apr_status_t run_watchdog(int sta
return APR_SUCCESS;
}
-apr_status_t md_start_watching(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
+apr_status_t md_renew_start_watching(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
{
apr_allocator_t *allocator;
- md_drive_ctx *dctx;
+ md_renew_ctx_t *dctx;
apr_pool_t *dctxp;
apr_status_t rv;
- const char *name;
md_t *md;
md_job_t *job;
int i;
/* We use mod_watchdog to run a single thread in one of the child processes
- * to monitor the MDs in mc->watched_names, using the const data in the list
+ * to monitor the MDs marked as watched, using the const data in the list
* mc->mds of our MD structures.
*
* The data in mc cannot be changed, as we may spawn copies in new child processes
@@ -438,29 +276,28 @@ apr_status_t md_start_watching(md_mod_co
apr_allocator_max_free_set(allocator, 1);
rv = apr_pool_create_ex(&dctxp, p, NULL, allocator);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10062) "md_drive_ctx: create pool");
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10062) "md_renew_watchdog: create pool");
return rv;
}
apr_allocator_owner_set(allocator, dctxp);
- apr_pool_tag(dctxp, "md_drive_ctx");
+ apr_pool_tag(dctxp, "md_renew_watchdog");
dctx = apr_pcalloc(dctxp, sizeof(*dctx));
dctx->p = dctxp;
dctx->s = s;
dctx->mc = mc;
- dctx->jobs = apr_array_make(dctx->p, mc->watched_names->nelts, sizeof(md_job_t *));
- for (i = 0; i < mc->watched_names->nelts; ++i) {
- name = APR_ARRAY_IDX(mc->watched_names, i, const char *);
- md = md_get_by_name(mc->mds, name);
- if (!md) continue;
+ dctx->jobs = apr_array_make(dctx->p, mc->mds->nelts, sizeof(md_job_t *));
+ for (i = 0; i < mc->mds->nelts; ++i) {
+ md = APR_ARRAY_IDX(mc->mds, i, md_t*);
+ if (!md || !md->watched) continue;
- job = md_job_make(p, md->name);
+ job = md_reg_job_make(mc->reg, md->name, p);
APR_ARRAY_PUSH(dctx->jobs, md_job_t*) = job;
ap_log_error( APLOG_MARK, APLOG_TRACE1, 0, dctx->s,
- "md(%s): state=%d, created drive job", name, md->state);
+ "md(%s): state=%d, created drive job", md->name, md->state);
- md_job_load(job, mc->reg, MD_SG_STAGING, dctx->p);
+ md_job_load(job);
if (job->error_runs) {
/* Server has just restarted. If we encounter an MD job with errors
* on a previous driving, we purge its STAGING area.
@@ -470,7 +307,7 @@ apr_status_t md_start_watching(md_mod_co
*/
ap_log_error( APLOG_MARK, APLOG_NOTICE, 0, dctx->s, APLOGNO(10064)
"md(%s): previous drive job showed %d errors, purging STAGING "
- "area to reset.", name, job->error_runs);
+ "area to reset.", md->name, job->error_runs);
md_store_purge(md_reg_store_get(dctx->mc->reg), p, MD_SG_STAGING, md->name);
md_store_purge(md_reg_store_get(dctx->mc->reg), p, MD_SG_CHALLENGES, md->name);
job->error_runs = 0;
@@ -484,13 +321,13 @@ apr_status_t md_start_watching(md_mod_co
return APR_SUCCESS;
}
- if (APR_SUCCESS != (rv = wd_get_instance(&dctx->watchdog, MD_WATCHDOG_NAME, 0, 1, dctx->p))) {
+ if (APR_SUCCESS != (rv = wd_get_instance(&dctx->watchdog, MD_RENEW_WATCHDOG_NAME, 0, 1, dctx->p))) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10066)
- "create md watchdog(%s)", MD_WATCHDOG_NAME);
+ "create md renew watchdog(%s)", MD_RENEW_WATCHDOG_NAME);
return rv;
}
rv = wd_register_callback(dctx->watchdog, 0, dctx, run_watchdog);
ap_log_error(APLOG_MARK, rv? APLOG_CRIT : APLOG_DEBUG, rv, s, APLOGNO(10067)
- "register md watchdog(%s)", MD_WATCHDOG_NAME);
+ "register md renew watchdog(%s)", MD_RENEW_WATCHDOG_NAME);
return rv;
}
Modified: httpd/httpd/trunk/modules/md/mod_md_drive.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_drive.h?rev=1868506&r1=1868505&r2=1868506&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_drive.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md_drive.h Wed Oct 16 12:31:43 2019
@@ -20,14 +20,14 @@
struct md_mod_conf_t;
struct md_reg_t;
-typedef struct md_drive_ctx md_drive_ctx;
+typedef struct md_renew_ctx_t md_renew_ctx_t;
int md_will_renew_cert(const md_t *md);
/**
- * Start driving the certificate procotol for the domains mentioned in mc->watched_names.
+ * Start driving the certificate renewal for MDs marked with watched.
*/
-apr_status_t md_start_watching(struct md_mod_conf_t *mc, server_rec *s, apr_pool_t *p);
+apr_status_t md_renew_start_watching(struct md_mod_conf_t *mc, server_rec *s, apr_pool_t *p);
Added: httpd/httpd/trunk/modules/md/mod_md_ocsp.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md_ocsp.c?rev=1868506&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_ocsp.c (added)
+++ httpd/httpd/trunk/modules/md/mod_md_ocsp.c Wed Oct 16 12:31:43 2019
@@ -0,0 +1,250 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <apr_optional.h>
+#include <apr_time.h>
+#include <apr_date.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "mod_watchdog.h"
+
+#include "md.h"
+#include "md_crypt.h"
+#include "md_http.h"
+#include "md_json.h"
+#include "md_ocsp.h"
+#include "md_store.h"
+#include "md_log.h"
+#include "md_reg.h"
+#include "md_time.h"
+#include "md_util.h"
+
+#include "mod_md.h"
+#include "mod_md_config.h"
+#include "mod_md_private.h"
+#include "mod_md_ocsp.h"
+
+static int staple_here(md_srv_conf_t *sc)
+{
+ if (!sc || !sc->mc->ocsp) return 0;
+ if (sc->assigned
+ && sc->assigned->nelts == 1
+ && APR_ARRAY_IDX(sc->assigned, 0, const md_t*)->stapling) return 1;
+ return (md_config_geti(sc, MD_CONFIG_STAPLING)
+ && md_config_geti(sc, MD_CONFIG_STAPLE_OTHERS));
+}
+
+apr_status_t md_ocsp_init_stapling_status(server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer)
+{
+ md_srv_conf_t *sc;
+ const md_t *md;
+ apr_status_t rv;
+
+ sc = md_config_get(s);
+ if (!staple_here(sc)) goto declined;
+
+ md = ((sc->assigned || sc->assigned->nelts == 1)?
+ APR_ARRAY_IDX(sc->assigned, 0, const md_t*) : NULL);
+ rv = md_ocsp_prime(sc->mc->ocsp, md_cert_wrap(p, cert),
+ md_cert_wrap(p, issuer), md);
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s, "init stapling for: %s",
+ md? md->name : s->server_hostname);
+ if (APR_SUCCESS == rv) {
+ return OK;
+ }
+declined:
+ return DECLINED;
+}
+
+apr_status_t md_ocsp_get_stapling_status(unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert)
+{
+ md_srv_conf_t *sc;
+ const md_t *md;
+ apr_status_t rv;
+
+ sc = md_config_get(s);
+ if (!staple_here(sc)) goto declined;
+
+ md = ((sc->assigned || sc->assigned->nelts == 1)?
+ APR_ARRAY_IDX(sc->assigned, 0, const md_t*) : NULL);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "get stapling for: %s",
+ md? md->name : s->server_hostname);
+ rv = md_ocsp_get_status(pder, pderlen, sc->mc->ocsp,
+ md_cert_wrap(c->pool, cert), c->pool, md);
+ if (APR_STATUS_IS_ENOENT(rv)) goto declined;
+ return rv;
+
+declined:
+ return DECLINED;
+}
+
+/**************************************************************************************************/
+/* watchdog based impl. */
+
+#define MD_OCSP_WATCHDOG_NAME "_md_ocsp_"
+
+static APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *wd_get_instance;
+static APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *wd_register_callback;
+static APR_OPTIONAL_FN_TYPE(ap_watchdog_set_callback_interval) *wd_set_interval;
+
+typedef struct md_ocsp_ctx_t md_ocsp_ctx_t;
+
+struct md_ocsp_ctx_t {
+ apr_pool_t *p;
+ server_rec *s;
+ md_mod_conf_t *mc;
+ ap_watchdog_t *watchdog;
+};
+
+static apr_time_t next_run_default(void)
+{
+ /* we'd like to run at least hourly */
+ return apr_time_now() + apr_time_from_sec(MD_SECS_PER_HOUR);
+}
+
+static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
+{
+ md_ocsp_ctx_t *octx = baton;
+ apr_time_t next_run, wait_time;
+
+ /* mod_watchdog invoked us as a single thread inside the whole server (on this machine).
+ * This might be a repeated run inside the same child (mod_watchdog keeps affinity as
+ * long as the child lives) or another/new child.
+ */
+ switch (state) {
+ case AP_WATCHDOG_STATE_STARTING:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, octx->s, APLOGNO(10197)
+ "md ocsp watchdog start, ocsp stapling %d certificates",
+ (int)md_ocsp_count(octx->mc->ocsp));
+ break;
+
+ case AP_WATCHDOG_STATE_RUNNING:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, octx->s, APLOGNO(10198)
+ "md ocsp watchdog run, ocsp stapling %d certificates",
+ (int)md_ocsp_count(octx->mc->ocsp));
+
+ /* Process all drive jobs. They will update their next_run property
+ * and we schedule ourself at the earliest of all. A job may specify 0
+ * as next_run to indicate that it wants to participate in the normal
+ * regular runs. */
+ next_run = next_run_default();
+
+ md_ocsp_renew(octx->mc->ocsp, octx->p, ptemp, &next_run);
+
+ wait_time = next_run - apr_time_now();
+ if (APLOGdebug(octx->s)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, octx->s, APLOGNO(10199)
+ "md ocsp watchdog next run in %s",
+ md_duration_print(ptemp, wait_time));
+ }
+ wd_set_interval(octx->watchdog, wait_time, octx, run_watchdog);
+ break;
+
+ case AP_WATCHDOG_STATE_STOPPING:
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, octx->s, APLOGNO(10200)
+ "md ocsp watchdog stopping");
+ break;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ocsp_remove_old_responses(md_mod_conf_t *mc, apr_pool_t *p)
+{
+ md_timeperiod_t keep_norm, keep;
+
+ keep_norm.end = apr_time_now();
+ keep_norm.start = keep_norm.end - MD_TIME_OCSP_KEEP_NORM;
+ keep = md_timeperiod_slice_before_end(&keep_norm, mc->ocsp_keep_window);
+ /* remove any ocsp response older than keep.start */
+ return md_ocsp_remove_responses_older_than(mc->ocsp, p, keep.start);
+}
+
+apr_status_t md_ocsp_start_watching(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
+{
+ apr_allocator_t *allocator;
+ md_ocsp_ctx_t *octx;
+ apr_pool_t *octxp;
+ apr_status_t rv;
+
+ wd_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
+ wd_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback);
+ wd_set_interval = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_set_callback_interval);
+
+ if (!wd_get_instance || !wd_register_callback || !wd_set_interval) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(10201)
+ "mod_watchdog is required for OCSP stapling");
+ return APR_EGENERAL;
+ }
+
+ /* We want our own pool with own allocator to keep data across watchdog invocations.
+ * Since we'll run in a single watchdog thread, using our own allocator will prevent
+ * any confusion in the parent pool. */
+ apr_allocator_create(&allocator);
+ apr_allocator_max_free_set(allocator, 1);
+ rv = apr_pool_create_ex(&octxp, p, NULL, allocator);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10205) "md_ocsp_watchdog: create pool");
+ return rv;
+ }
+ apr_allocator_owner_set(allocator, octxp);
+ apr_pool_tag(octxp, "md_ocsp_watchdog");
+
+ octx = apr_pcalloc(octxp, sizeof(*octx));
+ octx->p = octxp;
+ octx->s = s;
+ octx->mc = mc;
+
+ /* Time for some house keeping, before the server goes live (again):
+ * - we store OCSP responses for each certificate individually by its SHA-1 id
+ * - this means, as long as certificate do not change, the number of response
+ * files remains stable.
+ * - But when a certificate changes (is replaced), the response is obsolete
+ * - we do not get notified when a certificate is no longer used. An admin
+ * might just reconfigure or change the content of a file (backup/restore etc.)
+ * - also, certificates might be added by some openssl config commands or other
+ * modules that we do not immediately see right at startup. We cannot assume
+ * that any OCSP response we cannot relate to a certificate RIGHT NOW, is no
+ * longer needed.
+ * - since the response files are relatively small, we have no problem with
+ * keeping them around for a while. We just do not want an ever growing store.
+ * - The simplest and effective way seems to be to just remove files older
+ * a certain amount of time. Take a 7 day default and let the admin configure
+ * it for very special setups.
+ */
+ ocsp_remove_old_responses(mc, octx->p);
+
+ rv = wd_get_instance(&octx->watchdog, MD_OCSP_WATCHDOG_NAME, 0, 1, octx->p);
+ if (APR_SUCCESS != rv) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10202)
+ "create md ocsp watchdog(%s)", MD_OCSP_WATCHDOG_NAME);
+ return rv;
+ }
+ rv = wd_register_callback(octx->watchdog, 0, octx, run_watchdog);
+ ap_log_error(APLOG_MARK, rv? APLOG_CRIT : APLOG_DEBUG, rv, s, APLOGNO(10203)
+ "register md ocsp watchdog(%s)", MD_OCSP_WATCHDOG_NAME);
+ return rv;
+}
+
+
+