v1.7.1: don't self-poison dns cache, more consistency
This commit is contained in:
		
							parent
							
								
									b1182457cd
								
							
						
					
					
						commit
						e5e7377712
					
				
							
								
								
									
										196
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								node.js
									
									
									
									
									
								
							@ -47,10 +47,9 @@ ACME.challengeTests = {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
, 'dns-01': function (me, auth) {
 | 
					, 'dns-01': function (me, auth) {
 | 
				
			||||||
    // remove leading *. on wildcard domains
 | 
					    // remove leading *. on wildcard domains
 | 
				
			||||||
    var hostname = ACME.challengePrefixes['dns-01'] + '.' + auth.hostname.replace(/^\*\./, '');
 | 
					 | 
				
			||||||
    return me._dig({
 | 
					    return me._dig({
 | 
				
			||||||
      type: 'TXT'
 | 
					      type: 'TXT'
 | 
				
			||||||
    , name: hostname
 | 
					    , name: auth.dnsHost
 | 
				
			||||||
    }).then(function (ans) {
 | 
					    }).then(function (ans) {
 | 
				
			||||||
      var err;
 | 
					      var err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -62,7 +61,7 @@ ACME.challengeTests = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      err = new Error(
 | 
					      err = new Error(
 | 
				
			||||||
        "Error: Failed DNS-01 Pre-Flight Dry Run.\n"
 | 
					        "Error: Failed DNS-01 Pre-Flight Dry Run.\n"
 | 
				
			||||||
      + "dig TXT '" + hostname + "' does not return '" + auth.dnsAuthorization + "'\n"
 | 
					      + "dig TXT '" + auth.dnsHost + "' does not return '" + auth.dnsAuthorization + "'\n"
 | 
				
			||||||
      + "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
 | 
					      + "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      err.code = 'E_FAIL_DRY_CHALLENGE';
 | 
					      err.code = 'E_FAIL_DRY_CHALLENGE';
 | 
				
			||||||
@ -164,7 +163,7 @@ ACME._registerAccount = function (me, options) {
 | 
				
			|||||||
          options.accountKeypair
 | 
					          options.accountKeypair
 | 
				
			||||||
        , undefined
 | 
					        , undefined
 | 
				
			||||||
        , { nonce: me._nonce
 | 
					        , { nonce: me._nonce
 | 
				
			||||||
          , alg: 'RS256'
 | 
					          , alg: (me._alg || 'RS256')
 | 
				
			||||||
          , url: me._directoryUrls.newAccount
 | 
					          , url: me._directoryUrls.newAccount
 | 
				
			||||||
          , jwk: jwk
 | 
					          , jwk: jwk
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@ -296,48 +295,58 @@ ACME._testChallenges = function (me, options) {
 | 
				
			|||||||
    return Promise.resolve();
 | 
					    return Promise.resolve();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var CHECK_DELAY = 0;
 | 
				
			||||||
  return Promise.all(options.domains.map(function (identifierValue) {
 | 
					  return Promise.all(options.domains.map(function (identifierValue) {
 | 
				
			||||||
    // TODO we really only need one to pass, not all to pass
 | 
					    // TODO we really only need one to pass, not all to pass
 | 
				
			||||||
    var results = ACME._testChallengeOptions();
 | 
					    var challenges = ACME._testChallengeOptions();
 | 
				
			||||||
    if (identifierValue.inludes("*")) {
 | 
					    if (identifierValue.includes("*")) {
 | 
				
			||||||
      results = results.filter(function (ch) { return ch._wildcard; });
 | 
					      challenges = challenges.filter(function (ch) { return ch._wildcard; });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var challenge = ACME._chooseChallenge(options, results);
 | 
					
 | 
				
			||||||
 | 
					    var challenge = ACME._chooseChallenge(options, { challenges: challenges });
 | 
				
			||||||
    if (!challenge) {
 | 
					    if (!challenge) {
 | 
				
			||||||
      // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 | 
					      // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 | 
				
			||||||
      var enabled = options.challengeTypes.join(', ') || 'none';
 | 
					      var enabled = options.challengeTypes.join(', ') || 'none';
 | 
				
			||||||
      var suitable = results.map(function (r) { return r.type; }).join(', ') || 'none';
 | 
					      var suitable = challenges.map(function (r) { return r.type; }).join(', ') || 'none';
 | 
				
			||||||
      return Promise.reject(new Error(
 | 
					      return Promise.reject(new Error(
 | 
				
			||||||
        "None of the challenge types that you've enabled ( " + enabled + " )"
 | 
					        "None of the challenge types that you've enabled ( " + enabled + " )"
 | 
				
			||||||
          + " are suitable for validating the domain you've selected (" + identifierValue + ")."
 | 
					          + " are suitable for validating the domain you've selected (" + identifierValue + ")."
 | 
				
			||||||
          + " You must enable one of ( " + suitable + " )."
 | 
					          + " You must enable one of ( " + suitable + " )."
 | 
				
			||||||
      ));
 | 
					      ));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return Promise.resolve().then(function () {
 | 
					    if ('dns-01' === challenge.type) {
 | 
				
			||||||
      var thumbprint = me.RSA.thumbprint(options.accountKeypair);
 | 
					      // nameservers take a second to propagate
 | 
				
			||||||
      var keyAuthorization = challenge.token + '.' + thumbprint;
 | 
					      CHECK_DELAY = 5 * 1000;
 | 
				
			||||||
      var auth = {
 | 
					    }
 | 
				
			||||||
        identifier: { type: "dns", value: identifierValue }
 | 
					 | 
				
			||||||
      , hostname: identifierValue
 | 
					 | 
				
			||||||
      , type: challenge.type
 | 
					 | 
				
			||||||
      , token: challenge.token
 | 
					 | 
				
			||||||
      , thumbprint: thumbprint
 | 
					 | 
				
			||||||
      , keyAuthorization: keyAuthorization
 | 
					 | 
				
			||||||
      , dnsAuthorization: ACME._toWebsafeBase64(
 | 
					 | 
				
			||||||
          require('crypto').createHash('sha256').update(keyAuthorization).digest('base64')
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.resolve().then(function () {
 | 
				
			||||||
 | 
					      var results = {
 | 
				
			||||||
 | 
					        identifier: {
 | 
				
			||||||
 | 
					          type: "dns"
 | 
				
			||||||
 | 
					        , value: identifierValue.replace(/^\*\./, '')
 | 
				
			||||||
 | 
					        , wildcard: identifierValue.includes('*.') || undefined
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      , challenges: [ challenge ]
 | 
				
			||||||
 | 
					      , expires: new Date(Date.now() + (60 * 1000)).toISOString()
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      var dryrun = true;
 | 
				
			||||||
 | 
					      var auth = ACME._challengeToAuth(me, options, results, challenge, dryrun);
 | 
				
			||||||
      return ACME._setChallenge(me, options, auth).then(function () {
 | 
					      return ACME._setChallenge(me, options, auth).then(function () {
 | 
				
			||||||
        return ACME.challengeTests[challenge.type](me, auth);
 | 
					        return auth;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }));
 | 
					  })).then(function (auths) {
 | 
				
			||||||
 | 
					    return ACME._wait(CHECK_DELAY).then(function () {
 | 
				
			||||||
 | 
					      return Promise.all(auths.map(function (auth) {
 | 
				
			||||||
 | 
					        return ACME.challengeTests[auth.type](me, auth);
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
ACME._chooseChallenge = function(options, results) {
 | 
					ACME._chooseChallenge = function(options, results) {
 | 
				
			||||||
  // For each of the challenge types that we support
 | 
					  // For each of the challenge types that we support
 | 
				
			||||||
  var challenge;
 | 
					  var challenge;
 | 
				
			||||||
  options.challengesTypes.some(function (chType) {
 | 
					  options.challengeTypes.some(function (chType) {
 | 
				
			||||||
    // And for each of the challenge types that are allowed
 | 
					    // And for each of the challenge types that are allowed
 | 
				
			||||||
    return results.challenges.some(function (ch) {
 | 
					    return results.challenges.some(function (ch) {
 | 
				
			||||||
      // Check to see if there are any matches
 | 
					      // Check to see if there are any matches
 | 
				
			||||||
@ -350,30 +359,57 @@ ACME._chooseChallenge = function(options, results) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return challenge;
 | 
					  return challenge;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					ACME._challengeToAuth = function (me, options, request, challenge, dryrun) {
 | 
				
			||||||
 | 
					  // we don't poison the dns cache with our dummy request
 | 
				
			||||||
 | 
					  var dnsPrefix = ACME.challengePrefixes['dns-01'];
 | 
				
			||||||
 | 
					  if (dryrun) {
 | 
				
			||||||
 | 
					    dnsPrefix = dnsPrefix.replace('acme-challenge', 'greenlock-dryrun-' + Math.random().toString().slice(2,6));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var auth = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // straight copy from the new order response
 | 
				
			||||||
 | 
					  // { identifier, status, expires, challenges, wildcard }
 | 
				
			||||||
 | 
					  Object.keys(request).forEach(function (key) {
 | 
				
			||||||
 | 
					    auth[key] = request[key];
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // copy from the challenge we've chosen
 | 
				
			||||||
 | 
					  // { type, status, url, token }
 | 
				
			||||||
 | 
					  // (note the duplicate status overwrites the one above, but they should be the same)
 | 
				
			||||||
 | 
					  Object.keys(challenge).forEach(function (key) {
 | 
				
			||||||
 | 
					    auth[key] = challenge[key];
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // batteries-included helpers
 | 
				
			||||||
 | 
					  auth.hostname = request.identifier.value;
 | 
				
			||||||
 | 
					  auth.thumbprint = me.RSA.thumbprint(options.accountKeypair);
 | 
				
			||||||
 | 
					  //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | 
				
			||||||
 | 
					  auth.keyAuthorization = challenge.token + '.' + auth.thumbprint;
 | 
				
			||||||
 | 
					  auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', '');
 | 
				
			||||||
 | 
					  auth.dnsAuthorization = ACME._toWebsafeBase64(
 | 
				
			||||||
 | 
					    require('crypto').createHash('sha256').update(auth.keyAuthorization).digest('base64')
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
 | 
				
			||||||
 | 
					  auth.altname = ACME._untame(request.identifier.value, request.wildcard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return auth;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ACME._untame = function (name, wild) {
 | 
				
			||||||
 | 
					  if (wild) { name = '*.' + name.replace('*.', ''); }
 | 
				
			||||||
 | 
					  return name;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
					// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
				
			||||||
ACME._postChallenge = function (me, options, identifier, ch) {
 | 
					ACME._postChallenge = function (me, options, auth) {
 | 
				
			||||||
  var RETRY_INTERVAL = me.retryInterval || 1000;
 | 
					  var RETRY_INTERVAL = me.retryInterval || 1000;
 | 
				
			||||||
  var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
 | 
					  var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
 | 
				
			||||||
  var MAX_POLL = me.retryPoll || 8;
 | 
					  var MAX_POLL = me.retryPoll || 8;
 | 
				
			||||||
  var MAX_PEND = me.retryPending || 4;
 | 
					  var MAX_PEND = me.retryPending || 4;
 | 
				
			||||||
  var count = 0;
 | 
					  var count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var thumbprint = me.RSA.thumbprint(options.accountKeypair);
 | 
					  var altname = ACME._untame(auth.identifier.value, auth.wildcard);
 | 
				
			||||||
  var keyAuthorization = ch.token + '.' + thumbprint;
 | 
					 | 
				
			||||||
  //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | 
					 | 
				
			||||||
  //   /.well-known/acme-challenge/:token
 | 
					 | 
				
			||||||
  var auth = {
 | 
					 | 
				
			||||||
    identifier: identifier
 | 
					 | 
				
			||||||
  , hostname: identifier.value
 | 
					 | 
				
			||||||
  , type: ch.type
 | 
					 | 
				
			||||||
  , token: ch.token
 | 
					 | 
				
			||||||
  , thumbprint: thumbprint
 | 
					 | 
				
			||||||
  , keyAuthorization: keyAuthorization
 | 
					 | 
				
			||||||
  , dnsAuthorization: ACME._toWebsafeBase64(
 | 
					 | 
				
			||||||
      require('crypto').createHash('sha256').update(keyAuthorization).digest('base64')
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
   POST /acme/authz/1234 HTTP/1.1
 | 
					   POST /acme/authz/1234 HTTP/1.1
 | 
				
			||||||
@ -397,13 +433,13 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
    var jws = me.RSA.signJws(
 | 
					    var jws = me.RSA.signJws(
 | 
				
			||||||
      options.accountKeypair
 | 
					      options.accountKeypair
 | 
				
			||||||
    , undefined
 | 
					    , undefined
 | 
				
			||||||
    , { nonce: me._nonce, alg: (me._alg || 'RS256'), url: ch.url, kid: me._kid }
 | 
					    , { nonce: me._nonce, alg: (me._alg || 'RS256'), url: auth.url, kid: me._kid }
 | 
				
			||||||
    , Buffer.from(JSON.stringify({ "status": "deactivated" }))
 | 
					    , Buffer.from(JSON.stringify({ "status": "deactivated" }))
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    me._nonce = null;
 | 
					    me._nonce = null;
 | 
				
			||||||
    return me._request({
 | 
					    return me._request({
 | 
				
			||||||
      method: 'POST'
 | 
					      method: 'POST'
 | 
				
			||||||
    , url: ch.url
 | 
					    , url: auth.url
 | 
				
			||||||
    , headers: { 'Content-Type': 'application/jose+json' }
 | 
					    , headers: { 'Content-Type': 'application/jose+json' }
 | 
				
			||||||
    , json: jws
 | 
					    , json: jws
 | 
				
			||||||
    }).then(function (resp) {
 | 
					    }).then(function (resp) {
 | 
				
			||||||
@ -422,14 +458,14 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
  function pollStatus() {
 | 
					  function pollStatus() {
 | 
				
			||||||
    if (count >= MAX_POLL) {
 | 
					    if (count >= MAX_POLL) {
 | 
				
			||||||
      return Promise.reject(new Error(
 | 
					      return Promise.reject(new Error(
 | 
				
			||||||
        "[acme-v2] stuck in bad pending/processing state for '" + identifier.value + "'"
 | 
					        "[acme-v2] stuck in bad pending/processing state for '" + altname + "'"
 | 
				
			||||||
      ));
 | 
					      ));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    count += 1;
 | 
					    count += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
 | 
					    if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
 | 
				
			||||||
    return me._request({ method: 'GET', url: ch.url, json: true }).then(function (resp) {
 | 
					    return me._request({ method: 'GET', url: auth.url, json: true }).then(function (resp) {
 | 
				
			||||||
      if ('processing' === resp.body.status) {
 | 
					      if ('processing' === resp.body.status) {
 | 
				
			||||||
        if (me.debug) { console.debug('poll: again'); }
 | 
					        if (me.debug) { console.debug('poll: again'); }
 | 
				
			||||||
        return ACME._wait(RETRY_INTERVAL).then(pollStatus);
 | 
					        return ACME._wait(RETRY_INTERVAL).then(pollStatus);
 | 
				
			||||||
@ -453,7 +489,12 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
          } else if (2 === options.removeChallenge.length) {
 | 
					          } else if (2 === options.removeChallenge.length) {
 | 
				
			||||||
            options.removeChallenge(auth, function (err) { return err; });
 | 
					            options.removeChallenge(auth, function (err) { return err; });
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            options.removeChallenge(identifier.value, ch.token, function () {});
 | 
					            if (!ACME._removeChallengeWarn) {
 | 
				
			||||||
 | 
					              console.warn("Please update to acme-v2 removeChallenge(options) <Promise> or removeChallenge(options, cb).");
 | 
				
			||||||
 | 
					              console.warn("The API has been changed for compatibility with all ACME / Let's Encrypt challenge types.");
 | 
				
			||||||
 | 
					              ACME._removeChallengeWarn = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            options.removeChallenge(auth.request.identifier, auth.token, function () {});
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } catch(e) {}
 | 
					        } catch(e) {}
 | 
				
			||||||
        return resp.body;
 | 
					        return resp.body;
 | 
				
			||||||
@ -461,13 +502,13 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      var errmsg;
 | 
					      var errmsg;
 | 
				
			||||||
      if (!resp.body.status) {
 | 
					      if (!resp.body.status) {
 | 
				
			||||||
        errmsg = "[acme-v2] (E_STATE_EMPTY) empty challenge state for '" + identifier.value + "':";
 | 
					        errmsg = "[acme-v2] (E_STATE_EMPTY) empty challenge state for '" + altname + "':";
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else if ('invalid' === resp.body.status) {
 | 
					      else if ('invalid' === resp.body.status) {
 | 
				
			||||||
        errmsg = "[acme-v2] (E_STATE_INVALID) challenge state for '" + identifier.value + "': '" + resp.body.status + "'";
 | 
					        errmsg = "[acme-v2] (E_STATE_INVALID) challenge state for '" + altname + "': '" + resp.body.status + "'";
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
        errmsg = "[acme-v2] (E_STATE_UKN) challenge state for '" + identifier.value + "': '" + resp.body.status + "'";
 | 
					        errmsg = "[acme-v2] (E_STATE_UKN) challenge state for '" + altname + "': '" + resp.body.status + "'";
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return Promise.reject(new Error(errmsg));
 | 
					      return Promise.reject(new Error(errmsg));
 | 
				
			||||||
@ -478,13 +519,13 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
    var jws = me.RSA.signJws(
 | 
					    var jws = me.RSA.signJws(
 | 
				
			||||||
      options.accountKeypair
 | 
					      options.accountKeypair
 | 
				
			||||||
    , undefined
 | 
					    , undefined
 | 
				
			||||||
    , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid }
 | 
					    , { nonce: me._nonce, alg: (me._alg || 'RS256'), url: auth.url, kid: me._kid }
 | 
				
			||||||
    , Buffer.from(JSON.stringify({ }))
 | 
					    , Buffer.from(JSON.stringify({ }))
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    me._nonce = null;
 | 
					    me._nonce = null;
 | 
				
			||||||
    return me._request({
 | 
					    return me._request({
 | 
				
			||||||
      method: 'POST'
 | 
					      method: 'POST'
 | 
				
			||||||
    , url: ch.url
 | 
					    , url: auth.url
 | 
				
			||||||
    , headers: { 'Content-Type': 'application/jose+json' }
 | 
					    , headers: { 'Content-Type': 'application/jose+json' }
 | 
				
			||||||
    , json: jws
 | 
					    , json: jws
 | 
				
			||||||
    }).then(function (resp) {
 | 
					    }).then(function (resp) {
 | 
				
			||||||
@ -519,6 +560,11 @@ ACME._setChallenge = function (me, options, auth) {
 | 
				
			|||||||
        Object.keys(auth).forEach(function (key) {
 | 
					        Object.keys(auth).forEach(function (key) {
 | 
				
			||||||
          challengeCb[key] = auth[key];
 | 
					          challengeCb[key] = auth[key];
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        if (!ACME._setChallengeWarn) {
 | 
				
			||||||
 | 
					          console.warn("Please update to acme-v2 setChallenge(options) <Promise> or setChallenge(options, cb).");
 | 
				
			||||||
 | 
					          console.warn("The API has been changed for compatibility with all ACME / Let's Encrypt challenge types.");
 | 
				
			||||||
 | 
					          ACME._setChallengeWarn = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        options.setChallenge(auth.identifier.value, auth.token, auth.keyAuthorization, challengeCb);
 | 
					        options.setChallenge(auth.identifier.value, auth.token, auth.keyAuthorization, challengeCb);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch(e) {
 | 
					    } catch(e) {
 | 
				
			||||||
@ -541,7 +587,7 @@ ACME._finalizeOrder = function (me, options, validatedDomains) {
 | 
				
			|||||||
    var jws = me.RSA.signJws(
 | 
					    var jws = me.RSA.signJws(
 | 
				
			||||||
      options.accountKeypair
 | 
					      options.accountKeypair
 | 
				
			||||||
    , undefined
 | 
					    , undefined
 | 
				
			||||||
    , { nonce: me._nonce, alg: 'RS256', url: me._finalize, kid: me._kid }
 | 
					    , { nonce: me._nonce, alg: (me._alg || 'RS256'), url: me._finalize, kid: me._kid }
 | 
				
			||||||
    , Buffer.from(payload)
 | 
					    , Buffer.from(payload)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -642,14 +688,13 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  // TODO check that all challengeTypes are represented in challenges
 | 
					  // TODO check that all challengeTypes are represented in challenges
 | 
				
			||||||
  if (!options.challengeTypes.length) {
 | 
					  if (!options.challengeTypes.length) {
 | 
				
			||||||
    return Promise.reject(new Error("options.challengesTypes (string array) must be specified"
 | 
					    return Promise.reject(new Error("options.challengeTypes (string array) must be specified"
 | 
				
			||||||
      + " (and in order of preferential priority)."));
 | 
					      + " (and in order of preferential priority)."));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!(options.domains && options.domains.length)) {
 | 
					  if (!(options.domains && options.domains.length)) {
 | 
				
			||||||
    return Promise.reject(new Error("options.domains must be a list of string domain names,"
 | 
					    return Promise.reject(new Error("options.domains must be a list of string domain names,"
 | 
				
			||||||
    + " with the first being the subject of the domain (or options.subject must specified)."));
 | 
					    + " with the first being the subject of the domain (or options.subject must specified)."));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!options.subject) { options.subject = options.domains[0]; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // It's just fine if there's no account, we'll go get the key id we need via the public key
 | 
					  // It's just fine if there's no account, we'll go get the key id we need via the public key
 | 
				
			||||||
  if (!me._kid) {
 | 
					  if (!me._kid) {
 | 
				
			||||||
@ -670,8 +715,15 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
    if (me.debug) { console.debug('[acme-v2] certificates.create'); }
 | 
					    if (me.debug) { console.debug('[acme-v2] certificates.create'); }
 | 
				
			||||||
    return ACME._getNonce(me).then(function () {
 | 
					    return ACME._getNonce(me).then(function () {
 | 
				
			||||||
      var body = {
 | 
					      var body = {
 | 
				
			||||||
        identifiers: options.domains.map(function (hostname) {
 | 
					        // raw wildcard syntax MUST be used here
 | 
				
			||||||
          return { type: "dns" , value: hostname };
 | 
					        identifiers: options.domains.sort(function (a, b) {
 | 
				
			||||||
 | 
					          // the first in the list will be the subject of the certificate, I believe (and hope)
 | 
				
			||||||
 | 
					          if (!options.subject) { return 0; }
 | 
				
			||||||
 | 
					          if (options.subject === a) { return -1; }
 | 
				
			||||||
 | 
					          if (options.subject === b) { return 1; }
 | 
				
			||||||
 | 
					          return 0;
 | 
				
			||||||
 | 
					        }).map(function (hostname) {
 | 
				
			||||||
 | 
					          return { type: "dns", value: hostname };
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        //, "notBefore": "2016-01-01T00:00:00Z"
 | 
					        //, "notBefore": "2016-01-01T00:00:00Z"
 | 
				
			||||||
        //, "notAfter": "2016-01-08T00:00:00Z"
 | 
					        //, "notAfter": "2016-01-08T00:00:00Z"
 | 
				
			||||||
@ -698,7 +750,8 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
      }).then(function (resp) {
 | 
					      }).then(function (resp) {
 | 
				
			||||||
        me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
					        me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
				
			||||||
        var location = resp.toJSON().headers.location;
 | 
					        var location = resp.toJSON().headers.location;
 | 
				
			||||||
        var auths;
 | 
					        var setAuths;
 | 
				
			||||||
 | 
					        var auths = [];
 | 
				
			||||||
        if (me.debug) { console.debug(location); } // the account id url
 | 
					        if (me.debug) { console.debug(location); } // the account id url
 | 
				
			||||||
        if (me.debug) { console.debug(resp.toJSON()); }
 | 
					        if (me.debug) { console.debug(resp.toJSON()); }
 | 
				
			||||||
        me._authorizations = resp.body.authorizations;
 | 
					        me._authorizations = resp.body.authorizations;
 | 
				
			||||||
@ -713,12 +766,10 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
          ));
 | 
					          ));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); }
 | 
					        if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); }
 | 
				
			||||||
 | 
					        setAuths = me._authorizations.slice(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //return resp.body;
 | 
					        function setNext() {
 | 
				
			||||||
        auths = me._authorizations.slice(0);
 | 
					          var authUrl = setAuths.shift();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        function next() {
 | 
					 | 
				
			||||||
          var authUrl = auths.shift();
 | 
					 | 
				
			||||||
          if (!authUrl) { return; }
 | 
					          if (!authUrl) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          return ACME._getChallenges(me, options, authUrl).then(function (results) {
 | 
					          return ACME._getChallenges(me, options, authUrl).then(function (results) {
 | 
				
			||||||
@ -726,7 +777,7 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // If it's already valid, we're golden it regardless
 | 
					            // If it's already valid, we're golden it regardless
 | 
				
			||||||
            if (results.challenges.some(function (ch) { return 'valid' === ch.status; })) {
 | 
					            if (results.challenges.some(function (ch) { return 'valid' === ch.status; })) {
 | 
				
			||||||
              return;
 | 
					              return setNext();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var challenge = ACME._chooseChallenge(options, results);
 | 
					            var challenge = ACME._chooseChallenge(options, results);
 | 
				
			||||||
@ -737,13 +788,22 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
              ));
 | 
					              ));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ACME._postChallenge(me, options, results.identifier, challenge);
 | 
					            var auth = ACME._challengeToAuth(me, options, results, challenge);
 | 
				
			||||||
          }).then(function () {
 | 
					            auths.push(auth);
 | 
				
			||||||
            return next();
 | 
					            return ACME._setChallenge(me, options, auth).then(setNext);
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return next().then(function () {
 | 
					        function challengeNext() {
 | 
				
			||||||
 | 
					          var auth = auths.shift();
 | 
				
			||||||
 | 
					          if (!auth) { return; }
 | 
				
			||||||
 | 
					          return ACME._postChallenge(me, options, auth).then(challengeNext);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // First we set every challenge
 | 
				
			||||||
 | 
					        // Then we ask for each challenge to be checked
 | 
				
			||||||
 | 
					        // Doing otherwise would potentially cause us to poison our own DNS cache with misses
 | 
				
			||||||
 | 
					        return setNext().then(challengeNext).then(function () {
 | 
				
			||||||
          if (me.debug) { console.debug("[getCertificate] next.then"); }
 | 
					          if (me.debug) { console.debug("[getCertificate] next.then"); }
 | 
				
			||||||
          var validatedDomains = body.identifiers.map(function (ident) {
 | 
					          var validatedDomains = body.identifiers.map(function (ident) {
 | 
				
			||||||
            return ident.value;
 | 
					            return ident.value;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "acme-v2",
 | 
					  "name": "acme-v2",
 | 
				
			||||||
  "version": "1.7.0",
 | 
					  "version": "1.7.1",
 | 
				
			||||||
  "description": "Free SSL. A framework for building Let's Encrypt v2 clients, and other ACME v2 (draft 11) clients. Successor to le-acme-core.js",
 | 
					  "description": "Free SSL. A framework for building Let's Encrypt v2 clients, and other ACME v2 (draft 11) clients. Successor to le-acme-core.js",
 | 
				
			||||||
  "homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js",
 | 
					  "homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js",
 | 
				
			||||||
  "main": "node.js",
 | 
					  "main": "node.js",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user