From: Stefan Eissing Date: Fri, 15 Sep 2017 10:35:53 +0000 (+0000) Subject: On the trunk: X-Git-Tag: 2.5.0-alpha~122 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=800974edc3b6fc0cb94dafe4aa238c1caea189e2;p=apache On the trunk: mod_md: v0.9.6: a "MDRequireHttps permament" configured domain automatically sends out HSTS (rfc 6797) headers in https: responses. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1808444 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 692116d936..52c460fab4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_md: v0.9.6: a "MDRequireHttps permament" configured domain automatically sends out + HSTS (rfc 6797) headers in https: responses. [Stefan Eissing] + *) mod_ssl: adding ssl_policies.h[.in] for policy cipher/protocol definitions. Use update_policies.py to update manually from Mozilla JSON definitions at https://statics.tls.security.mozilla.org/server-side-tls-conf.json diff --git a/docs/manual/mod/mod_md.xml b/docs/manual/mod/mod_md.xml index 140c7f8cb3..4825b244f4 100644 --- a/docs/manual/mod/mod_md.xml +++ b/docs/manual/mod/mod_md.xml @@ -501,6 +501,17 @@ MDRequireHttps permanent </ManagedDomain> +

When you configure MDRequireHttps permanent, an additional security + feature is automatically applied: HSTS. This adds the header + Strict-Transport-Security to responses sent out via https:. + Basically, this instructs the browser to only perform secure + communications with that domain. This instruction holds for the + amount of time specified in the header as 'max-age'. + This is about half a year as generated by mod_md. +

+ It is therefore advisable to first test the MDRequireHttps temporary + configuration and switch to permanent only once that works satisfactory. +

diff --git a/modules/md/md.h b/modules/md/md.h index fc192df977..af15ad27f8 100644 --- a/modules/md/md.h +++ b/modules/md/md.h @@ -32,6 +32,10 @@ struct md_pkey_spec_t; #define MD_PKEY_RSA_BITS_MIN 2048 #define MD_PKEY_RSA_BITS_DEF 2048 +/* Minimum age for the HSTS header (RFC 6797), considered appropriate by Mozilla Security */ +#define MD_HSTS_HEADER "Strict-Transport-Security" +#define MD_HSTS_MAX_AGE_DEFAULT 15768000 + typedef enum { MD_S_UNKNOWN, /* MD has not been analysed yet */ MD_S_INCOMPLETE, /* MD is missing necessary information, cannot go live */ diff --git a/modules/md/md_version.h b/modules/md/md_version.h index f345721813..c60f48a09e 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.9.5" +#define MOD_MD_VERSION "0.9.6" /** * @macro @@ -34,7 +34,7 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define MOD_MD_VERSION_NUM 0x000905 +#define MOD_MD_VERSION_NUM 0x000906 #define MD_EXPERIMENTAL 0 #define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory" diff --git a/modules/md/mod_md.c b/modules/md/mod_md.c index 8a37de757a..06fccb1438 100644 --- a/modules/md/mod_md.c +++ b/modules/md/mod_md.c @@ -253,7 +253,7 @@ static apr_status_t assign_to_servers(md_t *md, server_rec *base_server, /* 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(10089) + ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, APLOGNO() "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); @@ -275,7 +275,7 @@ static apr_status_t assign_to_servers(md_t *md, server_rec *base_server, 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(10090) + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO() "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.", @@ -697,7 +697,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp) now = apr_time_now(); if (APLOGdebug(wd->s)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10091) + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO() "next run in %s", md_print_duration(ptemp, next_run - now)); } wd_set_interval(wd->watchdog, next_run - now, wd, run_watchdog); @@ -881,6 +881,7 @@ static apr_status_t md_post_config(apr_pool_t *p, apr_pool_t *plog, apr_status_t rv = APR_SUCCESS; int i; + md_config_post_config(s, p); sc = md_config_get(s); mc = sc->mc; @@ -1005,14 +1006,14 @@ static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p, assert(sc->mc); assert(sc->mc->store); if (APR_SUCCESS != (rv = md_reg_init(®, p, sc->mc->store, sc->mc->proxy_url))) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10092) "init registry"); + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO() "init registry"); return rv; } md = md_reg_get(reg, sc->assigned->name, p); if (APR_SUCCESS != (rv = md_reg_get_cred_files(reg, md, p, pkeyfile, pcertfile))) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10093) + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO() "retrieving credentials for MD %s", md->name); return rv; } @@ -1169,34 +1170,44 @@ static int md_require_https_maybe(request_rec *r) const char *s; int status; - if (strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) { + if (opt_ssl_is_https + && strncmp(WELL_KNOWN_PREFIX, r->parsed_uri.path, sizeof(WELL_KNOWN_PREFIX)-1)) { + sc = ap_get_module_config(r->server->module_config, &md_module); - if (sc && sc->assigned && sc->assigned->require_https > MD_REQUIRE_OFF - && opt_ssl_is_https && !opt_ssl_is_https(r->connection)) { - /* Do not have https:, but require it. Redirect the request accordingly. - */ - 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); + 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); + } } 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; + /* 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; + } } } } @@ -1233,7 +1244,7 @@ static void md_hooks(apr_pool_t *pool) /* answer challenges *very* early, before any configured authentication may strike */ ap_hook_post_read_request(md_http_challenge_pr, NULL, NULL, APR_HOOK_MIDDLE); /* redirect to https if configured */ - ap_hook_fixups(md_require_https_maybe, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_fixups(md_require_https_maybe, NULL, NULL, APR_HOOK_LAST); APR_REGISTER_OPTIONAL_FN(md_is_managed); APR_REGISTER_OPTIONAL_FN(md_get_certificate); diff --git a/modules/md/mod_md_config.c b/modules/md/mod_md_config.c index 36feec620b..63f11930d4 100644 --- a/modules/md/mod_md_config.c +++ b/modules/md/mod_md_config.c @@ -59,7 +59,9 @@ static md_mod_conf_t defmc = { 443, 0, 0, - NULL + MD_HSTS_MAX_AGE_DEFAULT, + NULL, + NULL, }; /* Default server specific setting */ @@ -770,6 +772,21 @@ const command_rec md_cmds[] = { AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) }; +apr_status_t md_config_post_config(server_rec *s, apr_pool_t *p) +{ + md_srv_conf_t *sc; + md_mod_conf_t *mc; + + sc = md_config_get(s); + mc = sc->mc; + + mc->hsts_header = NULL; + if (mc->hsts_max_age > 0) { + mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age); + } + + return APR_SUCCESS; +} static md_srv_conf_t *config_get_int(server_rec *s, apr_pool_t *p) { diff --git a/modules/md/mod_md_config.h b/modules/md/mod_md_config.h index 99593c35bc..bfe84c125e 100644 --- a/modules/md/mod_md_config.h +++ b/modules/md/mod_md_config.h @@ -45,8 +45,9 @@ typedef struct { int local_443; /* On which port https:443 arrives */ int can_http; /* Does someone listen to the local port 80 equivalent? */ int can_https; /* Does someone listen to the local port 443 equivalent? */ - - apr_array_header_t *unused_names; /* post config, names of all MDs not linked to a vhost */ + 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 */ } md_mod_conf_t; typedef struct md_srv_conf_t { @@ -78,6 +79,8 @@ void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv); extern const command_rec md_cmds[]; +apr_status_t md_config_post_config(server_rec *s, apr_pool_t *p); + /* Get the effective md configuration for the connection */ md_srv_conf_t *md_config_cget(conn_rec *c); /* Get the effective md configuration for the server */