v2.7.23: regression bugfixes: node v6 and cloudflare dns-01
This commit is contained in:
parent
93b9158b1b
commit
5316af67be
|
@ -0,0 +1,21 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function requireBluebird() {
|
||||||
|
try {
|
||||||
|
return require('bluebird');
|
||||||
|
} catch(e) {
|
||||||
|
console.error("");
|
||||||
|
console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support.");
|
||||||
|
console.error("EASY FIX: `npm install --save bluebird`");
|
||||||
|
console.error("");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('undefined' === typeof Promise) {
|
||||||
|
global.Promise = requireBluebird();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('function' !== typeof require('util').promisify) {
|
||||||
|
require('util').promisify = requireBluebird().promisify;
|
||||||
|
}
|
35
lib/core.js
35
lib/core.js
|
@ -1,11 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
/*global Promise*/
|
||||||
|
require('./compat.js');
|
||||||
|
|
||||||
var PromiseA;
|
|
||||||
try {
|
|
||||||
PromiseA = require('bluebird');
|
|
||||||
} catch(e) {
|
|
||||||
PromiseA = global.Promise;
|
|
||||||
}
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
function promisifyAll(obj) {
|
function promisifyAll(obj) {
|
||||||
var aobj = {};
|
var aobj = {};
|
||||||
|
@ -42,7 +38,7 @@ module.exports.create = function (gl) {
|
||||||
|
|
||||||
// TODO check response header on request for cache time
|
// TODO check response header on request for cache time
|
||||||
if ((now - gl._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) {
|
if ((now - gl._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) {
|
||||||
return PromiseA.resolve(gl._ipc.acmeUrls);
|
return Promise.resolve(gl._ipc.acmeUrls);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO acme-v2/nocompat
|
// TODO acme-v2/nocompat
|
||||||
|
@ -79,7 +75,7 @@ module.exports.create = function (gl) {
|
||||||
+ " and 'rsaKeySize' must be 2048 or greater."
|
+ " and 'rsaKeySize' must be 2048 or greater."
|
||||||
);
|
);
|
||||||
err.code = 'E_ARGS';
|
err.code = 'E_ARGS';
|
||||||
return PromiseA.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.testEmail(args.email).then(function () {
|
return utils.testEmail(args.email).then(function () {
|
||||||
|
@ -156,9 +152,9 @@ module.exports.create = function (gl) {
|
||||||
if (newAccountKeypair) {
|
if (newAccountKeypair) {
|
||||||
accountKeypairPromise = gl.store.accounts.setKeypairAsync(args, keypair);
|
accountKeypairPromise = gl.store.accounts.setKeypairAsync(args, keypair);
|
||||||
}
|
}
|
||||||
return PromiseA.resolve(accountKeypairPromise).then(function () {
|
return Promise.resolve(accountKeypairPromise).then(function () {
|
||||||
// TODO move templating of arguments to right here?
|
// TODO move templating of arguments to right here?
|
||||||
if (!gl.store.accounts.setAsync) { return PromiseA.resolve({ keypair: keypair }); }
|
if (!gl.store.accounts.setAsync) { return Promise.resolve({ keypair: keypair }); }
|
||||||
return gl.store.accounts.setAsync(args, reg).then(function (account) {
|
return gl.store.accounts.setAsync(args, reg).then(function (account) {
|
||||||
if (account && 'object' !== typeof account) {
|
if (account && 'object' !== typeof account) {
|
||||||
throw new Error("store.accounts.setAsync should either return 'null' or an object with at least a string 'id'");
|
throw new Error("store.accounts.setAsync should either return 'null' or an object with at least a string 'id'");
|
||||||
|
@ -181,7 +177,7 @@ module.exports.create = function (gl) {
|
||||||
if (gl.store.accounts.checkAsync) {
|
if (gl.store.accounts.checkAsync) {
|
||||||
accountPromise = core.accounts.checkAsync(args);
|
accountPromise = core.accounts.checkAsync(args);
|
||||||
}
|
}
|
||||||
return PromiseA.resolve(accountPromise).then(function (account) {
|
return Promise.resolve(accountPromise).then(function (account) {
|
||||||
if (!account) { return core.accounts.registerAsync(args); }
|
if (!account) { return core.accounts.registerAsync(args); }
|
||||||
if (account.keypair) { return account; }
|
if (account.keypair) { return account; }
|
||||||
|
|
||||||
|
@ -201,7 +197,7 @@ module.exports.create = function (gl) {
|
||||||
var requiredArgs = ['accountId', 'email', 'domains', 'domain'];
|
var requiredArgs = ['accountId', 'email', 'domains', 'domain'];
|
||||||
if (!(args.account && (args.account.id || args.account.kid))
|
if (!(args.account && (args.account.id || args.account.kid))
|
||||||
&& !requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) {
|
&& !requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) {
|
||||||
return PromiseA.reject(new Error(
|
return Promise.reject(new Error(
|
||||||
"In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present"
|
"In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -213,7 +209,7 @@ module.exports.create = function (gl) {
|
||||||
|
|
||||||
// we can re-register the same account until we're blue in the face and it's all the same
|
// we can re-register the same account until we're blue in the face and it's all the same
|
||||||
// of course, we can also skip the lookup if we do store the account, but whatever
|
// of course, we can also skip the lookup if we do store the account, but whatever
|
||||||
if (!gl.store.accounts.checkAsync) { return PromiseA.resolve(null); }
|
if (!gl.store.accounts.checkAsync) { return Promise.resolve(null); }
|
||||||
return gl.store.accounts.checkAsync(args).then(function (account) {
|
return gl.store.accounts.checkAsync(args).then(function (account) {
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
|
@ -240,7 +236,7 @@ module.exports.create = function (gl) {
|
||||||
args = utils.tplCopy(copy);
|
args = utils.tplCopy(copy);
|
||||||
|
|
||||||
if (!Array.isArray(args.domains)) {
|
if (!Array.isArray(args.domains)) {
|
||||||
return PromiseA.reject(new Error('args.domains should be an array of domains'));
|
return Promise.reject(new Error('args.domains should be an array of domains'));
|
||||||
}
|
}
|
||||||
//if (-1 === args.domains.indexOf(args.subject)) // TODO relax the constraint once acme-v2 handles subject?
|
//if (-1 === args.domains.indexOf(args.subject)) // TODO relax the constraint once acme-v2 handles subject?
|
||||||
if (args.subject !== args.domains[0]) {
|
if (args.subject !== args.domains[0]) {
|
||||||
|
@ -250,7 +246,7 @@ module.exports.create = function (gl) {
|
||||||
console.warn('\topts.domains: (set by you in approveDomains()) ' + args.domains.join(','));
|
console.warn('\topts.domains: (set by you in approveDomains()) ' + args.domains.join(','));
|
||||||
console.warn("Updating your code will prevent weird, random, hard-to-repro bugs during renewals");
|
console.warn("Updating your code will prevent weird, random, hard-to-repro bugs during renewals");
|
||||||
console.warn("(also this will be required in the next major version of greenlock)");
|
console.warn("(also this will be required in the next major version of greenlock)");
|
||||||
//return PromiseA.reject(new Error('certificate subject (primary domain) must be the first in opts.domains'));
|
//return Promise.reject(new Error('certificate subject (primary domain) must be the first in opts.domains'));
|
||||||
}
|
}
|
||||||
if (!(args.domains.length && args.domains.every(utils.isValidDomain))) {
|
if (!(args.domains.length && args.domains.every(utils.isValidDomain))) {
|
||||||
// NOTE: this library can't assume to handle the http loopback
|
// NOTE: this library can't assume to handle the http loopback
|
||||||
|
@ -258,7 +254,7 @@ module.exports.create = function (gl) {
|
||||||
// so we do not check dns records or attempt a loopback here
|
// so we do not check dns records or attempt a loopback here
|
||||||
err = new Error("invalid domain name(s): '(" + args.subject + ') ' + args.domains.join(',') + "'");
|
err = new Error("invalid domain name(s): '(" + args.subject + ') ' + args.domains.join(',') + "'");
|
||||||
err.code = "INVALID_DOMAIN";
|
err.code = "INVALID_DOMAIN";
|
||||||
return PromiseA.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a previous request to (re)register a certificate is already underway we need
|
// If a previous request to (re)register a certificate is already underway we need
|
||||||
|
@ -384,6 +380,9 @@ module.exports.create = function (gl) {
|
||||||
Object.keys(challenge).forEach(function (key) {
|
Object.keys(challenge).forEach(function (key) {
|
||||||
done[key] = challenge[key];
|
done[key] = challenge[key];
|
||||||
});
|
});
|
||||||
|
// regression bugfix for le-challenge-cloudflare
|
||||||
|
// (_acme-challege => _greenlock-dryrun-XXXX)
|
||||||
|
copy.acmePrefix = (challenge.dnsHost||'').replace(/\.*/, '') || copy.acmePrefix;
|
||||||
gl.challenges[challenge.type].set(copy, challenge.altname, challenge.token, challenge.keyAuthorization, done);
|
gl.challenges[challenge.type].set(copy, challenge.altname, challenge.token, challenge.keyAuthorization, done);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -429,7 +428,7 @@ module.exports.create = function (gl) {
|
||||||
args.keypair = domainKeypair;
|
args.keypair = domainKeypair;
|
||||||
promise = gl.store.certificates.setKeypairAsync(args, domainKeypair);
|
promise = gl.store.certificates.setKeypairAsync(args, domainKeypair);
|
||||||
}
|
}
|
||||||
return PromiseA.resolve(promise).then(function () {
|
return Promise.resolve(promise).then(function () {
|
||||||
return gl.store.certificates.setAsync(args).then(function () {
|
return gl.store.certificates.setAsync(args).then(function () {
|
||||||
return results;
|
return results;
|
||||||
});
|
});
|
||||||
|
@ -455,7 +454,7 @@ module.exports.create = function (gl) {
|
||||||
+ new Date(renewableAt).toISOString() + "'. Set { duplicate: true } to force."
|
+ new Date(renewableAt).toISOString() + "'. Set { duplicate: true } to force."
|
||||||
);
|
);
|
||||||
err.code = 'E_NOT_RENEWABLE';
|
err.code = 'E_NOT_RENEWABLE';
|
||||||
return PromiseA.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either the cert has entered its renewal period
|
// Either the cert has entered its renewal period
|
||||||
|
|
20
lib/utils.js
20
lib/utils.js
|
@ -1,12 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
require('./compat.js');
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var homeRe = new RegExp("^~(\\/|\\\\|\\" + path.sep + ")");
|
var homeRe = new RegExp("^~(\\/|\\\\|\\" + path.sep + ")");
|
||||||
// very basic check. Allows *.example.com.
|
// very basic check. Allows *.example.com.
|
||||||
var re = /^(\*\.)?[a-zA-Z0-9\.\-]+$/;
|
var re = /^(\*\.)?[a-zA-Z0-9\.\-]+$/;
|
||||||
var punycode = require('punycode');
|
var punycode = require('punycode');
|
||||||
var promisify = (require('util').promisify || require('bluebird').promisify);
|
var dnsResolveMxAsync = require('util').promisify(require('dns').resolveMx);
|
||||||
var dnsResolveMxAsync = promisify(require('dns').resolveMx);
|
|
||||||
|
|
||||||
module.exports.attachCertInfo = function (results) {
|
module.exports.attachCertInfo = function (results) {
|
||||||
var certInfo = require('cert-info').info(results.cert);
|
var certInfo = require('cert-info').info(results.cert);
|
||||||
|
@ -54,12 +54,24 @@ module.exports.merge = function (/*defaults, args*/) {
|
||||||
|
|
||||||
allDefaults.forEach(function (defaults) {
|
allDefaults.forEach(function (defaults) {
|
||||||
Object.keys(defaults).forEach(function (key) {
|
Object.keys(defaults).forEach(function (key) {
|
||||||
copy[key] = defaults[key];
|
if ('challenges' === key && copy[key] && defaults[key]) {
|
||||||
|
Object.keys(defaults[key]).forEach(function (k) {
|
||||||
|
copy[key][k] = defaults[key][k];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
copy[key] = defaults[key];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(args).forEach(function (key) {
|
Object.keys(args).forEach(function (key) {
|
||||||
copy[key] = args[key];
|
if ('challenges' === key && copy[key] && args[key]) {
|
||||||
|
Object.keys(args[key]).forEach(function (k) {
|
||||||
|
copy[key][k] = args[key][k];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
copy[key] = args[key];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "greenlock",
|
"name": "greenlock",
|
||||||
"version": "2.7.22",
|
"version": "2.7.23",
|
||||||
"description": "Greenlock is Let's Encrypt (ACME) client for node.js",
|
"description": "Greenlock is Let's Encrypt (ACME) client for node.js",
|
||||||
"homepage": "https://greenlock.domains/",
|
"homepage": "https://greenlock.domains/",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var PromiseA = require('bluebird');
|
|
||||||
var path = require('path');
|
|
||||||
var requestAsync = PromiseA.promisify(require('@coolaj86/request'));
|
|
||||||
var LE = require('../').LE;
|
|
||||||
var le = LE.create({
|
|
||||||
server: 'staging'
|
|
||||||
, acme: require('le-acme-core').ACME.create()
|
|
||||||
, store: require('le-store-certbot').create({
|
|
||||||
configDir: '~/letsencrypt.test/etc'.split('/').join(path.sep)
|
|
||||||
, webrootPath: '~/letsencrypt.test/var/:hostname'.split('/').join(path.sep)
|
|
||||||
})
|
|
||||||
, challenge: require('le-challenge-fs').create({
|
|
||||||
webrootPath: '~/letsencrypt.test/var/:hostname'.split('/').join(path.sep)
|
|
||||||
})
|
|
||||||
, debug: true
|
|
||||||
});
|
|
||||||
var utils = require('../lib/utils');
|
|
||||||
|
|
||||||
if ('/.well-known/acme-challenge/' !== LE.acmeChallengePrefix) {
|
|
||||||
throw new Error("Bad constant 'acmeChallengePrefix'");
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseUrl;
|
|
||||||
// could use localhost as well, but for the sake of an FQDN for testing, we use this
|
|
||||||
// also, example.com is just a junk domain to make sure that it is ignored
|
|
||||||
// (even though it should always be an array of only one element in lib/core.js)
|
|
||||||
var domains = [ 'localhost.daplie.com', 'example.com' ]; // or just localhost
|
|
||||||
var token = 'token-id';
|
|
||||||
var secret = 'key-secret';
|
|
||||||
|
|
||||||
var tests = [
|
|
||||||
function () {
|
|
||||||
console.log('Test Url:', baseUrl + token);
|
|
||||||
return requestAsync({ url: baseUrl + token }).then(function (req) {
|
|
||||||
if (404 !== req.statusCode) {
|
|
||||||
console.log(req.statusCode);
|
|
||||||
throw new Error("Should be status 404");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
, function () {
|
|
||||||
var copy = utils.merge({ domains: domains }, le);
|
|
||||||
copy = utils.tplCopy(copy);
|
|
||||||
return PromiseA.promisify(le.challenge.set)(copy, domains[0], token, secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
, function () {
|
|
||||||
return requestAsync(baseUrl + token).then(function (req) {
|
|
||||||
if (200 !== req.statusCode) {
|
|
||||||
console.log(req.statusCode, req.body);
|
|
||||||
throw new Error("Should be status 200");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.body !== secret) {
|
|
||||||
console.error(token, secret, req.body);
|
|
||||||
throw new Error("req.body should be secret");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
, function () {
|
|
||||||
var copy = utils.merge({ domains: domains }, le);
|
|
||||||
copy = utils.tplCopy(copy);
|
|
||||||
return PromiseA.promisify(le.challenge.remove)(copy, domains[0], token);
|
|
||||||
}
|
|
||||||
|
|
||||||
, function () {
|
|
||||||
return requestAsync(baseUrl + token).then(function (req) {
|
|
||||||
if (404 !== req.statusCode) {
|
|
||||||
console.log(req.statusCode);
|
|
||||||
throw new Error("Should be status 404");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
//var express = require(express);
|
|
||||||
var server = require('http').createServer(le.middleware());
|
|
||||||
server.listen(0, function () {
|
|
||||||
console.log('Server running, proceeding to test.');
|
|
||||||
baseUrl = 'http://' + domains[0] + ':' + server.address().port + LE.acmeChallengePrefix;
|
|
||||||
|
|
||||||
function next() {
|
|
||||||
var test = tests.shift();
|
|
||||||
if (!test) {
|
|
||||||
console.info('All tests passed');
|
|
||||||
server.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
test().then(next, function (err) {
|
|
||||||
console.error('ERROR');
|
|
||||||
console.error(err.stack);
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
Loading…
Reference in New Issue