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 2017/09/04 14:07:30 UTC

svn commit: r1807228 - in /httpd/httpd/trunk: CHANGES modules/md/md.h modules/md/md_acme_drive.c modules/md/md_reg.c modules/md/md_reg.h modules/md/md_store_fs.c modules/md/md_version.h modules/md/mod_md.c modules/md/mod_md.h

Author: icing
Date: Mon Sep  4 14:07:29 2017
New Revision: 1807228

URL: http://svn.apache.org/viewvc?rev=1807228&view=rev
Log:
On the trunk:

mod_md: 
 * Improved interface to mod_ssl for fallback handling. Backward compatible to previous mod_ssl
   patch, but fallbacks will not work.
 * Provide a temporary, self-signed certificate with a speaking command and domain name if we
   have no other cert for a Managed Domain, yet. Refs github issue #32
 * Continue to provide expired or not-completely matching, existing certificate for a Managed
   Domain until the renewal was successful. This is helpful when one adds a DNS name to
   a MD, so the previous domains can be served while a new cert is requested.
 * All files necessary to run tests are not in the release package.
 * Making "http-01" the preferred challenge type again, as people "tls-sni-01" requires at least
   one working certificate vhost right now - which not everyone has.
 * moved part of the MD sanity checks from post_config to check_config phase, allowing for error
   detection in check-only runs.


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/md/md.h
    httpd/httpd/trunk/modules/md/md_acme_drive.c
    httpd/httpd/trunk/modules/md/md_reg.c
    httpd/httpd/trunk/modules/md/md_reg.h
    httpd/httpd/trunk/modules/md/md_store_fs.c
    httpd/httpd/trunk/modules/md/md_version.h
    httpd/httpd/trunk/modules/md/mod_md.c
    httpd/httpd/trunk/modules/md/mod_md.h

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Sep  4 14:07:29 2017
@@ -1,39 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
-  *) mod_md: v0.8.1:
-     - New directive ```MDPrivateKeys``` to specify the type and parameter to private key generation.
-       Currently only 'RSA' is supported as type with an option number of bits >= 2048 as parameter.
-       Simple test cases for config handling added.
-     - Private RSA keys are now generated with 2048 bits by default. Use ```MDPrivateKeys``` for
-       higher security. 
-     - IMPORTANT: store format change. The following changes will be made to an existing md store on 
-       first start with a new version (be it by mod_md in the server or a run by a new 'a2md'):
-         - pkey.pem will be renamed to privkey.pem
-         - cert.pem and chain.pem will be concatenated to pubcert.pem. The former files will remain,
-           but no longer be used. They will disappear on next renewal.
-       ADVICE: If the current store data is vital to you, please make a backup first!
-     - Fixed test case clearing of store to keep key alive, enabling true random store key again.
-     - Removed pun "Something, like certbot" from the User-Agent request header. Refs issue #34
-     - Cleaned up reporting of missing/mismatched MDCertificateAgreement in the logs. This will
-       no longer trigger early retries.
-     - badNonce encounters are no longer reported as errors. Retries are attempted now silently.
-       Refs github issue #35
-     - new default MDRenewWindow. Instead of 14 days, the default is now a third before the end of
-       the certificates lifetime. For the usual 90 days of Let's Encrypt certificates, this makes
-       an effective renewal window of 30 days - as recommended by LE. Refs issue #30
-     - Enabled conversion warnings if supported by compiler, eliminated several signed/unsigned
-       warnings.
-     - LIVE: the real Let's Encrypt CA is now live by default! If you need to experiment, configure
-           MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory   
-     - When existing, complete certificates are renewed, the activation of the new ones is
-       delayed by 24 hours (or until the existing ones expire, whatever is earler) to accomodate
-       for clients with weird clocks, refs #1. 
-     - Fixed store sync when MDCAChallenges was removed again from an MD. 
-     - Fixed crash when MD matched the base server, fixes #23
-     - Fixed watchgod resetting staging when server processes disappeared (e.g. reached
-       max requests or other limits).
-    [Stefan Eissing]
+  *) mod_md: v0.9.0:
+     Certificate provisioning from Let's Encrypt (and other ACME CAs) for mod_ssl virtual hosts.
+     [Stefan Eissing]
   
   *) mod_proxy: loadfactor parameter can now be a decimal number (eg: 1.25).
      [Jim Jagielski]

Modified: httpd/httpd/trunk/modules/md/md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md.h?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h (original)
+++ httpd/httpd/trunk/modules/md/md.h Mon Sep  4 14:07:29 2017
@@ -143,6 +143,9 @@ struct md_t {
 #define MD_FN_CHAIN             "chain.pem"
 #define MD_FN_HTTPD_JSON        "httpd.json"
 
+#define MD_FN_FALLBACK_PKEY     "fallback-privkey.pem"
+#define MD_FN_FALLBACK_CERT     "fallback-cert.pem"
+
 /* Check if a string member of a new MD (n) has 
  * a value and if it differs from the old MD o
  */

Modified: httpd/httpd/trunk/modules/md/md_acme_drive.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_acme_drive.c?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme_drive.c (original)
+++ httpd/httpd/trunk/modules/md/md_acme_drive.c Mon Sep  4 14:07:29 2017
@@ -617,8 +617,8 @@ static apr_status_t acme_driver_init(md_
     }
     else {
         /* free to chose. Add all we support and see what we get offered */
-        APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_TLSSNI01;
         APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_HTTP01;
+        APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_TLSSNI01;
     }
     
     if (!d->can_http && !d->can_https) {

Modified: httpd/httpd/trunk/modules/md/md_reg.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.c?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.c (original)
+++ httpd/httpd/trunk/modules/md/md_reg.c Mon Sep  4 14:07:29 2017
@@ -424,12 +424,10 @@ md_t *md_reg_find_overlap(md_reg_t *reg,
 }
 
 apr_status_t md_reg_get_cred_files(md_reg_t *reg, const md_t *md, apr_pool_t *p,
-                                   const char **pkeyfile, const char **pcertfile,
-                                   const char **pchainfile)
+                                   const char **pkeyfile, const char **pcertfile)
 {
     apr_status_t rv;
     
-    *pchainfile = NULL;
     rv = md_store_get_fname(pkeyfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PRIVKEY, p);
     if (APR_SUCCESS == rv) {
         rv = md_store_get_fname(pcertfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PUBCERT, p);

Modified: httpd/httpd/trunk/modules/md/md_reg.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_reg.h?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.h (original)
+++ httpd/httpd/trunk/modules/md/md_reg.h Mon Sep  4 14:07:29 2017
@@ -108,8 +108,7 @@ apr_status_t md_reg_creds_get(const md_c
                               md_store_group_t group, const md_t *md, apr_pool_t *p);
 
 apr_status_t md_reg_get_cred_files(md_reg_t *reg, const md_t *md, apr_pool_t *p,
-                                   const char **pkeyfile, const char **pcertfile,
-                                   const char **pchainfile);
+                                   const char **pkeyfile, const char **pcertfile);
 
 /**
  * Synchronise the give master mds with the store.

Modified: httpd/httpd/trunk/modules/md/md_store_fs.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_store_fs.c?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_store_fs.c (original)
+++ httpd/httpd/trunk/modules/md/md_store_fs.c Mon Sep  4 14:07:29 2017
@@ -258,6 +258,57 @@ read:
     return rv;
 }
 
+static apr_status_t setup_fallback_cert(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
+{
+    md_store_fs_t *s_fs = baton;
+    md_pkey_t *fallback_key;
+    md_cert_t *fallback_cert;
+    md_pkey_spec_t spec;
+    apr_status_t rv;
+
+    if (APR_SUCCESS == (rv = fs_load(&s_fs->s, MD_SG_NONE, NULL, MD_FN_FALLBACK_PKEY, 
+                                     MD_SV_PKEY, (void**)&fallback_key, ptemp))
+        && APR_SUCCESS == (rv = fs_load(&s_fs->s, MD_SG_NONE, NULL, MD_FN_FALLBACK_CERT, 
+                                        MD_SV_CERT, (void**)&fallback_cert, ptemp))) {
+        apr_time_t not_after = md_cert_get_not_after(fallback_cert);
+        if (not_after > apr_time_now() + apr_time_from_sec(7 * MD_SECS_PER_DAY)) {
+            /* at least a week more valid, expect drive and restart way before that */
+            return APR_SUCCESS;
+        }
+    }
+    
+    spec.type = MD_PKEY_TYPE_RSA;
+    spec.params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
+        
+    if (APR_SUCCESS != (rv = md_pkey_gen(&fallback_key, ptemp, &spec))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "create fallback key");
+        return rv;
+    }
+    
+    if (APR_SUCCESS != (rv = md_store_save(&s_fs->s, ptemp, MD_SG_NONE, NULL, 
+                                           MD_FN_FALLBACK_PKEY, MD_SV_PKEY, 
+                                           (void*)fallback_key, 0))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "save fallback key");
+        return rv;
+    }
+
+    if (APR_SUCCESS != (rv = md_cert_self_sign(&fallback_cert, "Apache Managed Domain Fallback", 
+                                               "temporary.invalid.certificate", fallback_key, 
+                                               apr_time_from_sec(14 * MD_SECS_PER_DAY), ptemp))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "create fallback certificate");
+        return rv;
+    }
+
+    if (APR_SUCCESS != (rv = md_store_save(&s_fs->s, ptemp, MD_SG_NONE, NULL, 
+                                           MD_FN_FALLBACK_CERT, MD_SV_CERT, 
+                                           (void*)fallback_cert, 0))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, ptemp, "save fallback certificate");
+        return rv;
+    }
+
+    return rv;
+}
+
 apr_status_t md_store_fs_init(md_store_t **pstore, apr_pool_t *p, const char *path)
 {
     md_store_fs_t *s_fs;
@@ -302,6 +353,9 @@ apr_status_t md_store_fs_init(md_store_t
         }
     }
     rv = md_util_pool_vdo(setup_store_file, s_fs, p, NULL);
+    if (APR_SUCCESS == rv) {
+        rv = md_util_pool_vdo(setup_fallback_cert, s_fs, p, NULL);
+    }
     
     if (APR_SUCCESS != rv) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "init fs store at %s", path);

Modified: httpd/httpd/trunk/modules/md/md_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_version.h?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_version.h (original)
+++ httpd/httpd/trunk/modules/md/md_version.h Mon Sep  4 14:07:29 2017
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "0.8.1"
+#define MOD_MD_VERSION "0.9.0"
 
 /**
  * @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 0x000801
+#define MOD_MD_VERSION_NUM 0x000900
 
 #define MD_EXPERIMENTAL 0
 #define MD_ACME_DEF_URL    "https://acme-v01.api.letsencrypt.org/directory"

Modified: httpd/httpd/trunk/modules/md/mod_md.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.c?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c (original)
+++ httpd/httpd/trunk/modules/md/mod_md.c Mon Sep  4 14:07:29 2017
@@ -741,18 +741,11 @@ static void load_stage_sets(apr_array_he
     return;
 }
 
-static apr_status_t md_post_config(apr_pool_t *p, apr_pool_t *plog,
-                                   apr_pool_t *ptemp, server_rec *s)
+static apr_status_t md_check_config(apr_pool_t *p, apr_pool_t *plog,
+                                    apr_pool_t *ptemp, server_rec *s)
 {
     const char *mod_md_init_key = "mod_md_init_counter";
     void *data = NULL;
-    md_srv_conf_t *sc;
-    md_mod_conf_t *mc;
-    apr_array_header_t *drive_names;
-    md_reg_t *reg;
-    apr_status_t rv = APR_SUCCESS;
-    const md_t *md;
-    int i;
     
     apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool);
     if (data == NULL) {
@@ -767,20 +760,29 @@ static apr_status_t md_post_config(apr_p
     }
 
     init_setups(p, s);
-    
     md_log_set(log_is_level, log_print, NULL);
 
-    sc = md_config_get(s);
-    mc = sc->mc;
-    
-    /* 1. Check uniqueness of MDs, calculate global, configured MD list.
+    /* Check uniqueness of MDs, calculate global, configured MD list.
      * If successful, we have a list of MD definitions that do not overlap. */
     /* We also need to find out if we can be reached on 80/443 from the outside (e.g. the CA) */
-    if (APR_SUCCESS != (rv = md_calc_md_list(p, plog, ptemp, s))) {
-        goto out;
-    }
+    return md_calc_md_list(p, plog, ptemp, s);
+}
+    
+static apr_status_t md_post_config(apr_pool_t *p, apr_pool_t *plog,
+                                   apr_pool_t *ptemp, server_rec *s)
+{
+    md_srv_conf_t *sc;
+    md_mod_conf_t *mc;
+    md_reg_t *reg;
+    const md_t *md;
+    apr_array_header_t *drive_names;
+    apr_status_t rv = APR_SUCCESS;
+    int i;
+
+    sc = md_config_get(s);
+    mc = sc->mc;
     
-    /* 2. Synchronize the defintions we now have with the store via a registry (reg). */
+    /* Synchronize the defintions we now have with the store via a registry (reg). */
     if (APR_SUCCESS != (rv = setup_reg(&reg, p, s, 1))) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10072)
                      "setup md registry");
@@ -790,10 +792,9 @@ static apr_status_t md_post_config(apr_p
                                          mc->can_http, mc->can_https))) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10073)
                      "synching %d mds to registry", mc->mds->nelts);
-        goto out;
     }
     
-    /* 3. Determine the managed domains that are in auto drive_mode. For those,
+    /* Determine the managed domains that are in auto drive_mode. For those,
      * determine in which state they are:
      *  - UNKNOWN:            should not happen, report, dont drive
      *  - ERROR:              something we do not know how to fix, report, dont drive
@@ -820,7 +821,7 @@ static apr_status_t md_post_config(apr_p
         }
     }
     
-    /* 4. I there are MDs to drive, start a watchdog to check on them regularly */
+    /* If there are MDs to drive, start a watchdog to check on them regularly */
     if (drive_names->nelts > 0) {
         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(10074)
                      "%d out of %d mds are configured for auto-drive", 
@@ -834,7 +835,7 @@ static apr_status_t md_post_config(apr_p
         ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10075)
                      "no mds to auto drive, no watchdog needed");
     }
-out:     
+out:
     return rv;
 }
 
@@ -855,9 +856,8 @@ static int md_is_managed(server_rec *s)
     return 0;
 }
 
-static apr_status_t md_get_credentials(server_rec *s, apr_pool_t *p,
-                                       const char **pkeyfile, const char **pcertfile,
-                                       const char **pchainfile)
+static apr_status_t md_get_certificate(server_rec *s, apr_pool_t *p,
+                                       const char **pkeyfile, const char **pcertfile)
 {
     apr_status_t rv = APR_ENOENT;    
     md_srv_conf_t *sc;
@@ -866,26 +866,53 @@ static apr_status_t md_get_credentials(s
     
     *pkeyfile = NULL;
     *pcertfile = NULL;
-    *pchainfile = NULL;
     
     sc = md_config_get(s);
     
     if (sc && sc->assigned) {
         assert(sc->mc);
         assert(sc->mc->store);
-        if (APR_SUCCESS == (rv = md_reg_init(&reg, p, sc->mc->store))) {
-            md = md_reg_get(reg, sc->assigned->name, p);
-            if (md->state != MD_S_COMPLETE) {
-                return APR_EAGAIN;
-            }
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10077) 
-                         "%s: loading credentials for server %s", md->name, s->server_hostname);
-            return md_reg_get_cred_files(reg, md, p, pkeyfile, pcertfile, pchainfile);
+        if (APR_SUCCESS != (rv = md_reg_init(&reg, p, sc->mc->store))) {
+            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))) {
+            return rv;
+        }
+
+        if (!*pkeyfile || !*pcertfile 
+            || APR_SUCCESS != md_util_is_file(*pkeyfile, p)
+            || APR_SUCCESS != md_util_is_file(*pcertfile, p)) {
+            /* Provide temporary, self-signed certificate as fallback, so that
+             * clients do not get obscure TLS handshake errors or will see a fallback
+             * virtual host that is not intended to be served here. */
+            md_store_get_fname(pkeyfile, sc->mc->store, MD_SG_NONE, NULL, MD_FN_FALLBACK_PKEY, p);
+            md_store_get_fname(pcertfile, sc->mc->store, MD_SG_NONE, NULL, MD_FN_FALLBACK_CERT, p);
+            
+            return APR_EAGAIN;
+        }
+
+        /* We have key and cert files, but they might no longer be valid or not
+         * match all domain names. Still use these files for now, but indicate that 
+         * resources should no longer be served until we have a new certificate again. */
+        if (md->state != MD_S_COMPLETE) {
+            return APR_EAGAIN;
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10077) 
+                     "%s: providing certificate for server %s", md->name, s->server_hostname);
     }
     return rv;
 }
 
+static apr_status_t md_get_credentials(server_rec *s, apr_pool_t *p,
+                                       const char **pkeyfile, const char **pcertfile,
+                                       const char **pchainfile)
+{
+    *pchainfile = NULL;
+    return md_get_certificate(s, p, pkeyfile, pcertfile);
+}
 
 static int md_is_challenge(conn_rec *c, const char *servername,
                            X509 **pcert, EVP_PKEY **pkey)
@@ -1005,6 +1032,7 @@ static void md_hooks(apr_pool_t *pool)
     
     /* Run once after configuration is set, before mod_ssl.
      */
+    ap_hook_check_config(md_check_config, NULL, mod_ssl, APR_HOOK_MIDDLE);
     ap_hook_post_config(md_post_config, NULL, mod_ssl, APR_HOOK_MIDDLE);
     
     /* Run once after a child process has been created.
@@ -1015,6 +1043,7 @@ static void md_hooks(apr_pool_t *pool)
     ap_hook_post_read_request(md_http_challenge_pr, NULL, NULL, APR_HOOK_MIDDLE);
 
     APR_REGISTER_OPTIONAL_FN(md_is_managed);
+    APR_REGISTER_OPTIONAL_FN(md_get_certificate);
     APR_REGISTER_OPTIONAL_FN(md_get_credentials);
     APR_REGISTER_OPTIONAL_FN(md_is_challenge);
 }

Modified: httpd/httpd/trunk/modules/md/mod_md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/mod_md.h?rev=1807228&r1=1807227&r2=1807228&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.h (original)
+++ httpd/httpd/trunk/modules/md/mod_md.h Mon Sep  4 14:07:29 2017
@@ -24,6 +24,17 @@ struct server_rec;
 APR_DECLARE_OPTIONAL_FN(int, 
                         md_is_managed, (struct server_rec *));
 
+/**
+ * Get the certificate/key for the managed domain (md_is_managed != 0).
+ * 
+ * @return APR_EAGAIN if the real certicate is not available yet
+ */
+APR_DECLARE_OPTIONAL_FN(apr_status_t, 
+                        md_get_certificate, (struct server_rec *, apr_pool_t *,
+                                             const char **pkeyfile, 
+                                             const char **pcertfile));
+
+/* previous version for md_get_certificate, to be phased out soon */
 APR_DECLARE_OPTIONAL_FN(apr_status_t, 
                         md_get_credentials, (struct server_rec *, apr_pool_t *,
                                              const char **pkeyfile,