Apache HTTPD
mod_md_drive.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <apr_optional.h>
19#include <apr_hash.h>
20#include <apr_strings.h>
21#include <apr_date.h>
22
23#include <httpd.h>
24#include <http_core.h>
25#include <http_protocol.h>
26#include <http_request.h>
27#include <http_log.h>
28
29#include "mod_watchdog.h"
30
31#include "md.h"
32#include "md_curl.h"
33#include "md_crypt.h"
34#include "md_event.h"
35#include "md_http.h"
36#include "md_json.h"
37#include "md_status.h"
38#include "md_store.h"
39#include "md_store_fs.h"
40#include "md_log.h"
41#include "md_result.h"
42#include "md_reg.h"
43#include "md_util.h"
44#include "md_version.h"
45#include "md_acme.h"
46#include "md_acme_authz.h"
47
48#include "mod_md.h"
49#include "mod_md_private.h"
50#include "mod_md_config.h"
51#include "mod_md_status.h"
52#include "mod_md_drive.h"
53
54/**************************************************************************************************/
55/* watchdog based impl. */
56
57#define MD_RENEW_WATCHDOG_NAME "_md_renew_"
58
62
71
73{
74 const md_t *md;
76 apr_status_t rv;
77
78 md_job_load(job);
79 /* Evaluate again on loaded value. Values will change when watchdog switches child process */
80 if (apr_time_now() < job->next_run) return;
81
82 job->next_run = 0;
83 if (job->finished && job->notified_renewed) {
84 /* finished and notification handled, nothing to do. */
85 goto leave;
86 }
87
88 md = md_get_by_name(dctx->mc->mds, job->mdomain);
90
91 result = md_result_md_make(ptemp, md->name);
93
95 /* Missing information, this will not change until configuration
96 * is changed and server reloaded. */
97 job->fatal_error = 1;
98 job->next_run = 0;
99 goto leave;
100 }
101
102 if (md_will_renew_cert(md)) {
103 /* Renew the MDs credentials in a STAGING area. Might be invoked repeatedly
104 * without discarding previous/intermediate results.
105 * Only returns SUCCESS when the renewal is complete, e.g. STAGING has a
106 * complete set of new credentials.
107 */
109 "md(%s): state=%d, driving", job->mdomain, md->state);
110
111 if (md->stapling && dctx->mc->ocsp &&
112 md_reg_has_revoked_certs(dctx->mc->reg, dctx->mc->ocsp, md, dctx->p)) {
114 "md(%s): has revoked certificates", job->mdomain);
115 }
116 else if (!md_reg_should_renew(dctx->mc->reg, md, dctx->p)) {
118 "md(%s): no need to renew", job->mdomain);
119 goto expiry;
120 }
121
122 /* The (possibly configured) event handler may veto renewals. This
123 * is used in cluster installtations, see #233. */
124 rv = md_event_raise("renewing", md->name, job, result, ptemp);
125 if (APR_SUCCESS != rv) {
127 "%s: event-handler for 'renewing' returned %d, preventing renewal to proceed.",
128 job->mdomain, rv);
129 goto leave;
130 }
131
133 md_reg_renew(dctx->mc->reg, md, dctx->mc->env, 0, job->error_runs, result, ptemp);
135
136 if (APR_SUCCESS == result->status) {
137 /* Finished jobs might take a while before the results become valid.
138 * If that is in the future, request to run then */
139 if (apr_time_now() < result->ready_at) {
140 md_job_retry_at(job, result->ready_at);
141 goto leave;
142 }
143
144 if (!job->notified_renewed) {
145 md_job_save(job, result, ptemp);
146 md_job_notify(job, "renewed", result);
147 }
148 }
149 else {
150 ap_log_error( APLOG_MARK, APLOG_ERR, result->status, dctx->s, APLOGNO(10056)
151 "processing %s: %s", job->mdomain, result->detail);
152 md_job_log_append(job, "renewal-error", result->problem, result->detail);
153 md_event_holler("errored", job->mdomain, job, result, ptemp);
155 "%s: encountered error for the %d. time, next run in %s",
156 job->mdomain, job->error_runs,
157 md_duration_print(ptemp, job->next_run - apr_time_now()));
158 }
159 }
160
161expiry:
162 if (!job->finished && md_reg_should_warn(dctx->mc->reg, md, dctx->p)) {
164 "md(%s): warn about expiration", md->name);
166 md_job_notify(job, "expiring", result);
168 }
169
170leave:
171 if (job->dirty && result) {
172 rv = md_job_save(job, result, ptemp);
173 ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, dctx->s, "%s: saving job props", job->mdomain);
174 }
175}
176
178{
179 if (md->renew_mode == MD_RENEW_MANUAL) {
180 return 0;
181 }
182 else if (md->renew_mode == MD_RENEW_AUTO && md->cert_files && md->cert_files->nelts) {
183 return 0;
184 }
185 return 1;
186}
187
189{
190 unsigned char c;
191 apr_time_t delay = dctx->mc->check_interval;
192
193 md_rand_bytes(&c, sizeof(c), dctx->p);
194 return apr_time_now() + delay + (delay * (c - 128) / 256);
195}
196
197static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
198{
200 md_job_t *job;
201 apr_time_t next_run, wait_time;
202 int i;
203
204 /* mod_watchdog invoked us as a single thread inside the whole server (on this machine).
205 * This might be a repeated run inside the same child (mod_watchdog keeps affinity as
206 * long as the child lives) or another/new child.
207 */
208 switch (state) {
211 "md watchdog start, auto drive %d mds", dctx->jobs->nelts);
212 break;
213
216 "md watchdog run, auto drive %d mds", dctx->jobs->nelts);
217
218 /* Process all drive jobs. They will update their next_run property
219 * and we schedule ourself at the earliest of all. A job may specify 0
220 * as next_run to indicate that it wants to participate in the normal
221 * regular runs. */
222 next_run = next_run_default(dctx);
223 for (i = 0; i < dctx->jobs->nelts; ++i) {
224 job = APR_ARRAY_IDX(dctx->jobs, i, md_job_t *);
225
226 if (apr_time_now() >= job->next_run) {
227 process_drive_job(dctx, job, ptemp);
228 }
229
230 if (job->next_run && job->next_run < next_run) {
231 next_run = job->next_run;
232 }
233 }
234
235 wait_time = next_run - apr_time_now();
236 if (APLOGdebug(dctx->s)) {
238 "next run in %s", md_duration_print(ptemp, wait_time));
239 }
241 break;
242
245 "md watchdog stopping");
246 break;
247 }
248
249 return APR_SUCCESS;
250}
251
253{
257 apr_status_t rv;
258 md_t *md;
259 md_job_t *job;
260 int i;
261
262 /* We use mod_watchdog to run a single thread in one of the child processes
263 * to monitor the MDs marked as watched, using the const data in the list
264 * mc->mds of our MD structures.
265 *
266 * The data in mc cannot be changed, as we may spawn copies in new child processes
267 * of the original data at any time. The child which hosts the watchdog thread
268 * may also die or be recycled, which causes a new watchdog thread to run
269 * in another process with the original data.
270 *
271 * Instead, we use our store to persist changes in group STAGING. This is
272 * kept writable to child processes, but the data stored there is not live.
273 * However, mod_watchdog makes sure that we only ever have a single thread in
274 * our server (on this machine) that writes there. Other processes, e.g. informing
275 * the user about progress, only read from there.
276 *
277 * All changes during driving an MD are stored as files in MG_SG_STAGING/<MD.name>.
278 * All will have "md.json" and "job.json". There may be a range of other files used
279 * by the protocol obtaining the certificate/keys.
280 *
281 *
282 */
286
288 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(10061) "mod_watchdog is required");
289 return !OK;
290 }
291
292 /* We want our own pool with own allocator to keep data across watchdog invocations.
293 * Since we'll run in a single watchdog thread, using our own allocator will prevent
294 * any confusion in the parent pool. */
298 if (rv != APR_SUCCESS) {
299 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10062) "md_renew_watchdog: create pool");
300 return rv;
301 }
303 apr_pool_tag(dctxp, "md_renew_watchdog");
304
305 dctx = apr_pcalloc(dctxp, sizeof(*dctx));
306 dctx->p = dctxp;
307 dctx->s = s;
308 dctx->mc = mc;
309
310 dctx->jobs = apr_array_make(dctx->p, mc->mds->nelts, sizeof(md_job_t *));
311 for (i = 0; i < mc->mds->nelts; ++i) {
312 md = APR_ARRAY_IDX(mc->mds, i, md_t*);
313 if (!md || !md->watched) continue;
314
315 job = md_reg_job_make(mc->reg, md->name, p);
316 APR_ARRAY_PUSH(dctx->jobs, md_job_t*) = job;
318 "md(%s): state=%d, created drive job", md->name, md->state);
319
320 md_job_load(job);
321 if (job->error_runs) {
322 /* Server has just restarted. If we encounter an MD job with errors
323 * on a previous driving, we purge its STAGING area.
324 * This will reset the driving for the MD. It may run into the same
325 * error again, or in case of race/confusion/our error/CA error, it
326 * might allow the MD to succeed by a fresh start.
327 */
329 "md(%s): previous drive job showed %d errors, purging STAGING "
330 "area to reset.", md->name, job->error_runs);
333 job->error_runs = 0;
334 }
335 }
336
337 if (!dctx->jobs->nelts) {
339 "no managed domain to drive, no watchdog needed.");
341 return APR_SUCCESS;
342 }
343
344 if (APR_SUCCESS != (rv = wd_get_instance(&dctx->watchdog, MD_RENEW_WATCHDOG_NAME, 0, 1, dctx->p))) {
346 "create md renew watchdog(%s)", MD_RENEW_WATCHDOG_NAME);
347 return rv;
348 }
349 rv = wd_register_callback(dctx->watchdog, 0, dctx, run_watchdog);
351 "register md renew watchdog(%s)", MD_RENEW_WATCHDOG_NAME);
352 return rv;
353}
APR-UTIL date routines.
APR Hash Tables.
APR-UTIL registration of functions exported by modules.
APR Strings library.
#define OK
Definition httpd.h:456
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_NOTICE
Definition http_log.h:69
#define APLOG_INFO
Definition http_log.h:70
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOGdebug(s)
Definition http_log.h:234
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
apr_memcache_t * mc
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define AP_WATCHDOG_STATE_STARTING
#define AP_WATCHDOG_STATE_STOPPING
#define AP_WATCHDOG_STATE_RUNNING
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_array_header_t ** result
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_abortfunc_t apr_allocator_t * allocator
Definition apr_pools.h:208
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
#define APR_ARRAY_PUSH(ary, type)
Definition apr_tables.h:150
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
apr_int64_t apr_time_t
Definition apr_time.h:45
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
@ MD_S_MISSING_INFORMATION
Definition md.h:58
md_t * md_get_by_name(struct apr_array_header_t *mds, const char *name)
Definition md_core.c:151
@ MD_RENEW_AUTO
Definition md.h:71
@ MD_RENEW_MANUAL
Definition md.h:70
apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p)
Definition md_crypt.c:162
apr_pool_t * p
Definition md_event.c:32
apr_status_t md_event_raise(const char *event, const char *mdomain, struct md_job_t *job, struct md_result_t *result, apr_pool_t *p)
Definition md_event.c:61
void md_event_holler(const char *event, const char *mdomain, struct md_job_t *job, struct md_result_t *result, apr_pool_t *p)
Definition md_event.c:78
int md_reg_should_renew(md_reg_t *reg, const md_t *md, apr_pool_t *p)
Definition md_reg.c:706
apr_status_t md_reg_renew(md_reg_t *reg, const md_t *md, apr_table_t *env, int reset, int attempt, md_result_t *result, apr_pool_t *p)
Definition md_reg.c:1168
int md_reg_has_revoked_certs(md_reg_t *reg, struct md_ocsp_reg_t *ocsp, const md_t *md, apr_pool_t *p)
Definition md_reg.c:1334
md_job_t * md_reg_job_make(md_reg_t *reg, const char *mdomain, apr_pool_t *p)
Definition md_reg.c:1321
int md_reg_should_warn(md_reg_t *reg, const md_t *md, apr_pool_t *p)
Definition md_reg.c:714
struct md_store_t * md_reg_store_get(md_reg_t *reg)
Definition md_reg.c:122
void md_result_assign(md_result_t *dest, const md_result_t *src)
Definition md_result.c:206
md_result_t * md_result_md_make(apr_pool_t *p, const char *md_name)
Definition md_result.c:50
void md_job_start_run(md_job_t *job, md_result_t *result, md_store_t *store)
Definition md_status.c:570
apr_status_t md_job_load(md_job_t *job)
Definition md_status.c:358
void md_job_end_run(md_job_t *job, md_result_t *result)
Definition md_status.c:611
apr_status_t md_job_save(md_job_t *job, md_result_t *result, apr_pool_t *p)
Definition md_status.c:370
void md_job_retry_at(md_job_t *job, apr_time_t later)
Definition md_status.c:628
void md_job_log_append(md_job_t *job, const char *type, const char *status, const char *detail)
Definition md_status.c:382
apr_status_t md_job_notify(md_job_t *job, const char *reason, md_result_t *result)
Definition md_status.c:634
apr_status_t md_store_purge(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name)
Definition md_store.c:93
@ MD_SG_STAGING
Definition md_store.h:67
@ MD_SG_CHALLENGES
Definition md_store.h:65
const char * md_duration_print(apr_pool_t *p, apr_interval_time_t duration)
Definition md_time.c:108
#define MD_RENEW_WATCHDOG_NAME
static apr_OFN_ap_watchdog_register_callback_t * wd_register_callback
static apr_OFN_ap_watchdog_set_callback_interval_t * wd_set_interval
int md_will_renew_cert(const md_t *md)
static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
static void process_drive_job(md_renew_ctx_t *dctx, md_job_t *job, apr_pool_t *ptemp)
apr_status_t md_renew_start_watching(md_mod_conf_t *mc, server_rec *s, apr_pool_t *p)
static apr_OFN_ap_watchdog_get_instance_t * wd_get_instance
static apr_time_t next_run_default(void)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
static apr_status_t ap_watchdog_register_callback(ap_watchdog_t *w, apr_interval_time_t interval, const void *data, ap_watchdog_callback_fn_t *callback)
static apr_status_t ap_watchdog_get_instance(ap_watchdog_t **watchdog, const char *name, int parent, int singleton, apr_pool_t *p)
static apr_status_t ap_watchdog_set_callback_interval(ap_watchdog_t *w, apr_interval_time_t interval, const void *data, ap_watchdog_callback_fn_t *callback)
Watchdog module for Apache.
int fatal_error
Definition md_status.h:65
int error_runs
Definition md_status.h:64
int dirty
Definition md_status.h:69
const char * mdomain
Definition md_status.h:54
apr_time_t next_run
Definition md_status.h:57
int finished
Definition md_status.h:60
int notified_renewed
Definition md_status.h:62
struct md_result_t * last_result
Definition md_status.h:59
ap_watchdog_t * watchdog
apr_pool_t * p
md_mod_conf_t * mc
server_rec * s
apr_array_header_t * jobs
Definition md.h:76
const char * name
Definition md.h:77
md_state_t state
Definition md.h:108
int watched
Definition md.h:112
int renew_mode
Definition md.h:106
int stapling
Definition md.h:111
struct apr_array_header_t * cert_files
Definition md.h:91
A structure to store information for each virtual server.
Definition httpd.h:1322