make Prettier

This commit is contained in:
AJ ONeal 2019-10-08 04:51:15 -06:00
parent 929429f1ef
commit 33e6f800b8
13 changed files with 1844 additions and 1321 deletions

View File

@ -1,5 +1,4 @@
STOP # STOP
====
**These aren't the droids you're looking for.** **These aren't the droids you're looking for.**
@ -7,8 +6,7 @@ You probably don't want to use `greenlock` directly.
Instead, look here: Instead, look here:
Webservers ## Webservers
----------
For any type of webserver (express, hapi, koa, connect, https, spdy, etc), For any type of webserver (express, hapi, koa, connect, https, spdy, etc),
you're going to want to take a look at you're going to want to take a look at
@ -16,8 +14,7 @@ you're going to want to take a look at
<https://git.coolaj86.com/coolaj86/greenlock-express.js> <https://git.coolaj86.com/coolaj86/greenlock-express.js>
CLIs ## CLIs
----
For any type of CLI (like what you want to use with bash, fish, zsh, cmd.exe, PowerShell, etc), For any type of CLI (like what you want to use with bash, fish, zsh, cmd.exe, PowerShell, etc),
you're going to want to take a look at you're going to want to take a look at
@ -25,8 +22,7 @@ you're going to want to take a look at
<https://git.coolaj86.com/coolaj86/greenlock-cli.js> <https://git.coolaj86.com/coolaj86/greenlock-cli.js>
No, I wanted greenlock # No, I wanted greenlock
======================
Well, take a look at the API in the main README Well, take a look at the API in the main README
and you can also check out the code in the repos above. and you can also check out the code in the repos above.

View File

@ -5,63 +5,73 @@ var Greenlock = require('../');
var db = {}; var db = {};
var config = { var config = {
server: 'https://acme-v02.api.letsencrypt.org/directory' server: 'https://acme-v02.api.letsencrypt.org/directory',
, version: 'draft-11' version: 'draft-11',
, configDir: require('os').homedir() + '/acme/etc' // or /etc/acme or wherever configDir: require('os').homedir() + '/acme/etc', // or /etc/acme or wherever
, privkeyPath: ':config/live/:hostname/privkey.pem' // privkeyPath: ':config/live/:hostname/privkey.pem', //
, fullchainPath: ':config/live/:hostname/fullchain.pem' // Note: both that :config and :hostname fullchainPath: ':config/live/:hostname/fullchain.pem', // Note: both that :config and :hostname
, certPath: ':config/live/:hostname/cert.pem' // will be templated as expected certPath: ':config/live/:hostname/cert.pem', // will be templated as expected
, chainPath: ':config/live/:hostname/chain.pem' // chainPath: ':config/live/:hostname/chain.pem', //
, rsaKeySize: 2048 rsaKeySize: 2048,
, debug: true debug: true
}; };
var handlers = { var handlers = {
setChallenge: function (opts, hostname, key, val, cb) { // called during the ACME server handshake, before validation setChallenge: function(opts, hostname, key, val, cb) {
db[key] = { // called during the ACME server handshake, before validation
hostname: hostname db[key] = {
, key: key hostname: hostname,
, val: val key: key,
}; val: val
};
cb(null); cb(null);
} },
, removeChallenge: function (opts, hostname, key, cb) { // called after validation on both success and failure removeChallenge: function(opts, hostname, key, cb) {
db[key] = null; // called after validation on both success and failure
cb(null); db[key] = null;
} cb(null);
, getChallenge: function (opts, hostname, key, cb) { // this is special because it is called by the webserver },
cb(null, db[key].val); // (see greenlock-cli/bin & greenlock-express/standalone), getChallenge: function(opts, hostname, key, cb) {
// not by the library itself // this is special because it is called by the webserver
} cb(null, db[key].val); // (see greenlock-cli/bin & greenlock-express/standalone),
, agreeToTerms: function (tosUrl, cb) { // gives you an async way to expose the legal agreement // not by the library itself
cb(null, tosUrl); // (terms of use) to your users before accepting },
} agreeToTerms: function(tosUrl, cb) {
// gives you an async way to expose the legal agreement
cb(null, tosUrl); // (terms of use) to your users before accepting
}
}; };
var greenlock = Greenlock.create(config, handlers); var greenlock = Greenlock.create(config, handlers);
console.error("CHANGE THE EMAIL, DOMAINS, AND AGREE TOS IN THE EXAMPLE BEFORE RUNNING IT"); console.error(
'CHANGE THE EMAIL, DOMAINS, AND AGREE TOS IN THE EXAMPLE BEFORE RUNNING IT'
);
process.exit(1); process.exit(1);
// checks :conf/renewal/:hostname.conf // checks :conf/renewal/:hostname.conf
greenlock.register({ // and either renews or registers greenlock.register(
domains: ['example.com'] // CHANGE TO YOUR DOMAIN {
, email: 'user@email.com' // CHANGE TO YOUR EMAIL // and either renews or registers
, agreeTos: false // set to true to automatically accept an agreement domains: ['example.com'], // CHANGE TO YOUR DOMAIN
// which you have pre-approved (not recommended) email: 'user@email.com', // CHANGE TO YOUR EMAIL
, rsaKeySize: 2048 agreeTos: false, // set to true to automatically accept an agreement
}, function (err) { // which you have pre-approved (not recommended)
if (err) { rsaKeySize: 2048
// Note: you must have a webserver running },
// and expose handlers.getChallenge to it function(err) {
// in order to pass validation if (err) {
// See greenlock-cli and or greenlock-express // Note: you must have a webserver running
console.error('[Error]: greenlock/examples/standalone'); // and expose handlers.getChallenge to it
console.error(err.stack); // in order to pass validation
} else { // See greenlock-cli and or greenlock-express
console.log('success'); console.error('[Error]: greenlock/examples/standalone');
} console.error(err.stack);
}); } else {
console.log('success');
}
}
);

398
index.js
View File

@ -1,6 +1,6 @@
"use strict"; 'use strict';
/*global Promise*/ /*global Promise*/
require("./lib/compat.js"); require('./lib/compat.js');
// I hate this code so much. // I hate this code so much.
// Soooo many shims for backwards compatibility (some stuff dating back to v1) // Soooo many shims for backwards compatibility (some stuff dating back to v1)
@ -8,17 +8,17 @@ require("./lib/compat.js");
var DAY = 24 * 60 * 60 * 1000; var DAY = 24 * 60 * 60 * 1000;
//var MIN = 60 * 1000; //var MIN = 60 * 1000;
var ACME = require("acme-v2/compat").ACME; var ACME = require('acme-v2/compat').ACME;
var pkg = require("./package.json"); var pkg = require('./package.json');
var util = require("util"); var util = require('util');
function promisifyAllSelf(obj) { function promisifyAllSelf(obj) {
if (obj.__promisified) { if (obj.__promisified) {
return obj; return obj;
} }
Object.keys(obj).forEach(function(key) { Object.keys(obj).forEach(function(key) {
if ("function" === typeof obj[key] && !/Async$/.test(key)) { if ('function' === typeof obj[key] && !/Async$/.test(key)) {
obj[key + "Async"] = util.promisify(obj[key]); obj[key + 'Async'] = util.promisify(obj[key]);
} }
}); });
obj.__promisified = true; obj.__promisified = true;
@ -26,7 +26,7 @@ function promisifyAllSelf(obj) {
} }
function promisifyAllStore(obj) { function promisifyAllStore(obj) {
Object.keys(obj).forEach(function(key) { Object.keys(obj).forEach(function(key) {
if ("function" !== typeof obj[key] || /Async$/.test(key)) { if ('function' !== typeof obj[key] || /Async$/.test(key)) {
return; return;
} }
@ -42,7 +42,7 @@ function promisifyAllStore(obj) {
p = util.promisify(obj[key]); p = util.promisify(obj[key]);
} }
// internal backwards compat // internal backwards compat
obj[key + "Async"] = p; obj[key + 'Async'] = p;
}); });
obj.__promisified = true; obj.__promisified = true;
return obj; return obj;
@ -58,18 +58,18 @@ function _log(debug) {
if (debug) { if (debug) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
args.shift(); args.shift();
args.unshift("[gl/index.js]"); args.unshift('[gl/index.js]');
console.log.apply(console, args); console.log.apply(console, args);
} }
} }
Greenlock.defaults = { Greenlock.defaults = {
productionServerUrl: "https://acme-v01.api.letsencrypt.org/directory", productionServerUrl: 'https://acme-v01.api.letsencrypt.org/directory',
stagingServerUrl: "https://acme-staging.api.letsencrypt.org/directory", stagingServerUrl: 'https://acme-staging.api.letsencrypt.org/directory',
rsaKeySize: ACME.rsaKeySize || 2048, rsaKeySize: ACME.rsaKeySize || 2048,
challengeType: ACME.challengeType || "http-01", challengeType: ACME.challengeType || 'http-01',
challengeTypes: ACME.challengeTypes || ["http-01", "dns-01"], challengeTypes: ACME.challengeTypes || ['http-01', 'dns-01'],
acmeChallengePrefix: ACME.acmeChallengePrefix acmeChallengePrefix: ACME.acmeChallengePrefix
}; };
@ -120,33 +120,33 @@ Greenlock.create = function(gl) {
" The old default is 'le-store-certbot', but the new default will be 'greenlock-store-fs'." + " The old default is 'le-store-certbot', but the new default will be 'greenlock-store-fs'." +
" Please `npm install greenlock-store-fs@3` and explicitly set `{ store: require('greenlock-store-fs') }`." " Please `npm install greenlock-store-fs@3` and explicitly set `{ store: require('greenlock-store-fs') }`."
); );
gl.store = require("le-store-certbot").create({ gl.store = require('le-store-certbot').create({
debug: gl.debug, debug: gl.debug,
configDir: gl.configDir, configDir: gl.configDir,
logsDir: gl.logsDir, logsDir: gl.logsDir,
webrootPath: gl.webrootPath webrootPath: gl.webrootPath
}); });
} }
gl.core = require("./lib/core"); gl.core = require('./lib/core');
var log = gl.log || _log; var log = gl.log || _log;
if (!gl.challenges) { if (!gl.challenges) {
gl.challenges = {}; gl.challenges = {};
} }
if (!gl.challenges["http-01"]) { if (!gl.challenges['http-01']) {
gl.challenges["http-01"] = require("le-challenge-fs").create({ gl.challenges['http-01'] = require('le-challenge-fs').create({
debug: gl.debug, debug: gl.debug,
webrootPath: gl.webrootPath webrootPath: gl.webrootPath
}); });
} }
if (!gl.challenges["dns-01"]) { if (!gl.challenges['dns-01']) {
try { try {
gl.challenges["dns-01"] = require("le-challenge-ddns").create({ gl.challenges['dns-01'] = require('le-challenge-ddns').create({
debug: gl.debug debug: gl.debug
}); });
} catch (e) { } catch (e) {
try { try {
gl.challenges["dns-01"] = require("le-challenge-dns").create({ gl.challenges['dns-01'] = require('le-challenge-dns').create({
debug: gl.debug debug: gl.debug
}); });
} catch (e) { } catch (e) {
@ -160,12 +160,12 @@ Greenlock.create = function(gl) {
gl.rsaKeySize = gl.rsaKeySize || Greenlock.rsaKeySize; gl.rsaKeySize = gl.rsaKeySize || Greenlock.rsaKeySize;
gl.challengeType = gl.challengeType || Greenlock.challengeType; gl.challengeType = gl.challengeType || Greenlock.challengeType;
gl._ipc = ipc; gl._ipc = ipc;
gl._communityPackage = gl._communityPackage || "greenlock.js"; gl._communityPackage = gl._communityPackage || 'greenlock.js';
if ("greenlock.js" === gl._communityPackage) { if ('greenlock.js' === gl._communityPackage) {
gl._communityPackageVersion = pkg.version; gl._communityPackageVersion = pkg.version;
} else { } else {
gl._communityPackageVersion = gl._communityPackageVersion =
gl._communityPackageVersion || "greenlock.js-" + pkg.version; gl._communityPackageVersion || 'greenlock.js-' + pkg.version;
} }
gl.agreeToTerms = gl.agreeToTerms =
gl.agreeToTerms || gl.agreeToTerms ||
@ -186,39 +186,47 @@ Greenlock.create = function(gl) {
// BEGIN VERSION MADNESS // // BEGIN VERSION MADNESS //
/////////////////////////// ///////////////////////////
gl.version = gl.version || "draft-11"; gl.version = gl.version || 'draft-11';
gl.server = gl.server || "https://acme-v02.api.letsencrypt.org/directory"; gl.server = gl.server || 'https://acme-v02.api.letsencrypt.org/directory';
if (!gl.version) { if (!gl.version) {
//console.warn("Please specify version: 'v01' (Let's Encrypt v1) or 'draft-12' (Let's Encrypt v2 / ACME draft 12)"); //console.warn("Please specify version: 'v01' (Let's Encrypt v1) or 'draft-12' (Let's Encrypt v2 / ACME draft 12)");
console.warn(""); console.warn('');
console.warn(""); console.warn('');
console.warn(""); console.warn('');
console.warn("=========================================================="); console.warn(
console.warn("== greenlock.js (v2.2.0+) =="); '=========================================================='
console.warn("=========================================================="); );
console.warn(""); console.warn(
'== greenlock.js (v2.2.0+) =='
);
console.warn(
'=========================================================='
);
console.warn('');
console.warn("Please specify 'version' option:"); console.warn("Please specify 'version' option:");
console.warn(""); console.warn('');
console.warn(" 'draft-12' for Let's Encrypt v2 and ACME draft 12"); console.warn(
" 'draft-12' for Let's Encrypt v2 and ACME draft 12"
);
console.warn(" ('v02' is an alias of 'draft-12'"); console.warn(" ('v02' is an alias of 'draft-12'");
console.warn(""); console.warn('');
console.warn("or"); console.warn('or');
console.warn(""); console.warn('');
console.warn(" 'v01' for Let's Encrypt v1 (deprecated)"); console.warn(" 'v01' for Let's Encrypt v1 (deprecated)");
console.warn( console.warn(
" (also 'npm install --save le-acme-core' as this legacy dependency will soon be removed)" " (also 'npm install --save le-acme-core' as this legacy dependency will soon be removed)"
); );
console.warn(""); console.warn('');
console.warn("This will be required in versions v2.3+"); console.warn('This will be required in versions v2.3+');
console.warn(""); console.warn('');
console.warn(""); console.warn('');
} else if ("v02" === gl.version) { } else if ('v02' === gl.version) {
gl.version = "draft-11"; gl.version = 'draft-11';
} else if ("draft-12" === gl.version) { } else if ('draft-12' === gl.version) {
gl.version = "draft-11"; gl.version = 'draft-11';
} else if ("draft-11" === gl.version) { } else if ('draft-11' === gl.version) {
// no-op // no-op
} else if ("v01" !== gl.version) { } else if ('v01' !== gl.version) {
throw new Error("Unrecognized version '" + gl.version + "'"); throw new Error("Unrecognized version '" + gl.version + "'");
} }
@ -227,62 +235,62 @@ Greenlock.create = function(gl) {
"opts.server must specify an ACME directory URL, such as 'https://acme-staging-v02.api.letsencrypt.org/directory'" "opts.server must specify an ACME directory URL, such as 'https://acme-staging-v02.api.letsencrypt.org/directory'"
); );
} }
if ("staging" === gl.server || "production" === gl.server) { if ('staging' === gl.server || 'production' === gl.server) {
if ("staging" === gl.server) { if ('staging' === gl.server) {
gl.server = "https://acme-staging.api.letsencrypt.org/directory"; gl.server = 'https://acme-staging.api.letsencrypt.org/directory';
gl.version = "v01"; gl.version = 'v01';
gl._deprecatedServerName = "staging"; gl._deprecatedServerName = 'staging';
} else if ("production" === gl.server) { } else if ('production' === gl.server) {
gl.server = "https://acme-v01.api.letsencrypt.org/directory"; gl.server = 'https://acme-v01.api.letsencrypt.org/directory';
gl.version = "v01"; gl.version = 'v01';
gl._deprecatedServerName = "production"; gl._deprecatedServerName = 'production';
} }
console.warn(""); console.warn('');
console.warn(""); console.warn('');
console.warn("=== WARNING ==="); console.warn('=== WARNING ===');
console.warn(""); console.warn('');
console.warn( console.warn(
"Due to versioning issues the '" + "Due to versioning issues the '" +
gl._deprecatedServerName + gl._deprecatedServerName +
"' option is deprecated." "' option is deprecated."
); );
console.warn("Please specify the full url and version."); console.warn('Please specify the full url and version.');
console.warn(""); console.warn('');
console.warn("For APIs add:"); console.warn('For APIs add:');
console.warn('\t, "version": "' + gl.version + '"'); console.warn('\t, "version": "' + gl.version + '"');
console.warn('\t, "server": "' + gl.server + '"'); console.warn('\t, "server": "' + gl.server + '"');
console.warn(""); console.warn('');
console.warn("For the CLI add:"); console.warn('For the CLI add:');
console.warn("\t--acme-url '" + gl.server + "' \\"); console.warn("\t--acme-url '" + gl.server + "' \\");
console.warn("\t--acme-version '" + gl.version + "' \\"); console.warn("\t--acme-version '" + gl.version + "' \\");
console.warn(""); console.warn('');
console.warn(""); console.warn('');
} }
function loadLeV01() { function loadLeV01() {
console.warn(""); console.warn('');
console.warn("=== WARNING ==="); console.warn('=== WARNING ===');
console.warn(""); console.warn('');
console.warn("Let's Encrypt v1 is deprecated."); console.warn("Let's Encrypt v1 is deprecated.");
console.warn("Please update to Let's Encrypt v2 (ACME draft 12)"); console.warn("Please update to Let's Encrypt v2 (ACME draft 12)");
console.warn(""); console.warn('');
try { try {
return require("le-acme-core").ACME; return require('le-acme-core').ACME;
} catch (e) { } catch (e) {
console.error(""); console.error('');
console.error("=== Error (easy-to-fix) ==="); console.error('=== Error (easy-to-fix) ===');
console.error(""); console.error('');
console.error( console.error(
"Hey, this isn't a big deal, but you need to manually add v1 support:" "Hey, this isn't a big deal, but you need to manually add v1 support:"
); );
console.error(""); console.error('');
console.error(" npm install --save le-acme-core"); console.error(' npm install --save le-acme-core');
console.error(""); console.error('');
console.error( console.error(
"Just run that real quick, restart, and everything will work great." 'Just run that real quick, restart, and everything will work great.'
); );
console.error(""); console.error('');
console.error(""); console.error('');
process.exit(e.code || 13); process.exit(e.code || 13);
} }
} }
@ -290,32 +298,32 @@ Greenlock.create = function(gl) {
if ( if (
-1 !== -1 !==
[ [
"https://acme-v02.api.letsencrypt.org/directory", 'https://acme-v02.api.letsencrypt.org/directory',
"https://acme-staging-v02.api.letsencrypt.org/directory" 'https://acme-staging-v02.api.letsencrypt.org/directory'
].indexOf(gl.server) ].indexOf(gl.server)
) { ) {
if ("draft-11" !== gl.version) { if ('draft-11' !== gl.version) {
console.warn( console.warn(
"Detected Let's Encrypt v02 URL. Changing version to draft-12." "Detected Let's Encrypt v02 URL. Changing version to draft-12."
); );
gl.version = "draft-11"; gl.version = 'draft-11';
} }
} else if ( } else if (
-1 !== -1 !==
[ [
"https://acme-v01.api.letsencrypt.org/directory", 'https://acme-v01.api.letsencrypt.org/directory',
"https://acme-staging.api.letsencrypt.org/directory" 'https://acme-staging.api.letsencrypt.org/directory'
].indexOf(gl.server) || ].indexOf(gl.server) ||
"v01" === gl.version 'v01' === gl.version
) { ) {
if ("v01" !== gl.version) { if ('v01' !== gl.version) {
console.warn( console.warn(
"Detected Let's Encrypt v01 URL (deprecated). Changing version to v01." "Detected Let's Encrypt v01 URL (deprecated). Changing version to v01."
); );
gl.version = "v01"; gl.version = 'v01';
} }
} }
if ("v01" === gl.version) { if ('v01' === gl.version) {
ACME = loadLeV01(); ACME = loadLeV01();
} }
///////////////////////// /////////////////////////
@ -349,12 +357,14 @@ Greenlock.create = function(gl) {
gl.store.accounts = promisifyAllStore(gl.store.accounts); gl.store.accounts = promisifyAllStore(gl.store.accounts);
gl.store.certificates = promisifyAllStore(gl.store.certificates); gl.store.certificates = promisifyAllStore(gl.store.certificates);
gl._storeOpts = gl._storeOpts =
(gl.store.getOptions && gl.store.getOptions()) || gl.store.options || {}; (gl.store.getOptions && gl.store.getOptions()) ||
gl.store.options ||
{};
} catch (e) { } catch (e) {
console.error(e); console.error(e);
console.error( console.error(
"\nPROBABLE CAUSE:\n" + '\nPROBABLE CAUSE:\n' +
"\tYour greenlock-store module should have a create function and return { options, accounts, certificates }\n" '\tYour greenlock-store module should have a create function and return { options, accounts, certificates }\n'
); );
process.exit(18); process.exit(18);
return; return;
@ -384,40 +394,47 @@ Greenlock.create = function(gl) {
if (challenger.create) { if (challenger.create) {
challenger = gl.challenges[challengeType] = challenger.create(gl); challenger = gl.challenges[challengeType] = challenger.create(gl);
} }
challenger = gl.challenges[challengeType] = promisifyAllSelf(challenger); challenger = gl.challenges[challengeType] = promisifyAllSelf(
gl["_challengeOpts_" + challengeType] = challenger
);
gl['_challengeOpts_' + challengeType] =
(challenger.getOptions && challenger.getOptions()) || (challenger.getOptions && challenger.getOptions()) ||
challenger.options || challenger.options ||
{}; {};
Object.keys(gl["_challengeOpts_" + challengeType]).forEach(function(key) { Object.keys(gl['_challengeOpts_' + challengeType]).forEach(function(
key
) {
if (!(key in gl)) { if (!(key in gl)) {
gl[key] = gl["_challengeOpts_" + challengeType][key]; gl[key] = gl['_challengeOpts_' + challengeType][key];
} }
}); });
// TODO wrap these here and now with tplCopy? // TODO wrap these here and now with tplCopy?
if (!challenger.set || ![5, 2, 1].includes(challenger.set.length)) { if (!challenger.set || ![5, 2, 1].includes(challenger.set.length)) {
throw new Error( throw new Error(
"gl.challenges[" + 'gl.challenges[' +
challengeType + challengeType +
"].set receives the wrong number of arguments." + '].set receives the wrong number of arguments.' +
" You must define setChallenge as function (opts) { return Promise.resolve(); }" ' You must define setChallenge as function (opts) { return Promise.resolve(); }'
); );
} }
if (challenger.get && ![4, 2, 1].includes(challenger.get.length)) { if (challenger.get && ![4, 2, 1].includes(challenger.get.length)) {
throw new Error( throw new Error(
"gl.challenges[" + 'gl.challenges[' +
challengeType + challengeType +
"].get receives the wrong number of arguments." + '].get receives the wrong number of arguments.' +
" You must define getChallenge as function (opts) { return Promise.resolve(); }" ' You must define getChallenge as function (opts) { return Promise.resolve(); }'
); );
} }
if (!challenger.remove || ![4, 2, 1].includes(challenger.remove.length)) { if (
!challenger.remove ||
![4, 2, 1].includes(challenger.remove.length)
) {
throw new Error( throw new Error(
"gl.challenges[" + 'gl.challenges[' +
challengeType + challengeType +
"].remove receives the wrong number of arguments." + '].remove receives the wrong number of arguments.' +
" You must define removeChallenge as function (opts) { return Promise.resolve(); }" ' You must define removeChallenge as function (opts) { return Promise.resolve(); }'
); );
} }
@ -486,16 +503,20 @@ Greenlock.create = function(gl) {
if (!gl.email) { if (!gl.email) {
throw new Error( throw new Error(
"le-sni-auto is not properly configured. Missing email" 'le-sni-auto is not properly configured. Missing email'
); );
} }
if (!gl.agreeTos) { if (!gl.agreeTos) {
throw new Error( throw new Error(
"le-sni-auto is not properly configured. Missing agreeTos" 'le-sni-auto is not properly configured. Missing agreeTos'
); );
} }
if (!/[a-z]/i.test(lexOpts.domain)) { if (!/[a-z]/i.test(lexOpts.domain)) {
cb(new Error("le-sni-auto does not allow IP addresses in SNI")); cb(
new Error(
'le-sni-auto does not allow IP addresses in SNI'
)
);
return; return;
} }
@ -522,13 +543,13 @@ Greenlock.create = function(gl) {
emsg = emsg =
"tls SNI for '" + "tls SNI for '" +
lexOpts.domains.join(",") + lexOpts.domains.join(',') +
"' rejected: not in list '" + "' rejected: not in list '" +
gl.approvedDomains + gl.approvedDomains +
"'"; "'";
log(gl.debug, emsg, lexOpts.domains, gl.approvedDomains); log(gl.debug, emsg, lexOpts.domains, gl.approvedDomains);
err = new Error(emsg); err = new Error(emsg);
err.code = "E_REJECT_SNI"; err.code = 'E_REJECT_SNI';
cb(err); cb(err);
}; };
} }
@ -537,10 +558,10 @@ Greenlock.create = function(gl) {
// certs come from current in-memory cache, not lookup // certs come from current in-memory cache, not lookup
log( log(
gl.debug, gl.debug,
"gl.getCertificates called for", 'gl.getCertificates called for',
domain, domain,
"with certs for", 'with certs for',
(certs && certs.altnames) || "NONE" (certs && certs.altnames) || 'NONE'
); );
var opts = { var opts = {
domain: domain, domain: domain,
@ -550,24 +571,24 @@ Greenlock.create = function(gl) {
account: {} account: {}
}; };
opts.wildname = opts.wildname =
"*." + '*.' +
(domain || "") (domain || '')
.split(".") .split('.')
.slice(1) .slice(1)
.join("."); .join('.');
function cb2(results) { function cb2(results) {
log( log(
gl.debug, gl.debug,
"gl.approveDomains called with certs for", 'gl.approveDomains called with certs for',
(results.certs && results.certs.altnames) || "NONE", (results.certs && results.certs.altnames) || 'NONE',
"and options:" 'and options:'
); );
log(gl.debug, results.options || results); log(gl.debug, results.options || results);
var err; var err;
if (!results) { if (!results) {
err = new Error("E_REJECT_SNI"); err = new Error('E_REJECT_SNI');
err.code = "E_REJECT_SNI"; err.code = 'E_REJECT_SNI';
eb2(err); eb2(err);
return; return;
} }
@ -575,13 +596,19 @@ Greenlock.create = function(gl) {
var options = results.options || results; var options = results.options || results;
if (opts !== options) { if (opts !== options) {
Object.keys(options).forEach(function(key) { Object.keys(options).forEach(function(key) {
if ("undefined" !== typeof options[key] && "domain" !== key) { if (
'undefined' !== typeof options[key] &&
'domain' !== key
) {
opts[key] = options[key]; opts[key] = options[key];
} }
}); });
options = opts; options = opts;
} }
if (Array.isArray(options.altnames) && options.altnames.length) { if (
Array.isArray(options.altnames) &&
options.altnames.length
) {
options.domains = options.altnames; options.domains = options.altnames;
} }
options.altnames = options.domains; options.altnames = options.domains;
@ -593,24 +620,31 @@ Greenlock.create = function(gl) {
options.certificate = {}; options.certificate = {};
} }
if (results.certs) { if (results.certs) {
log(gl.debug, "gl renewing"); log(gl.debug, 'gl renewing');
return gl.core.certificates.renewAsync(options, results.certs).then( return gl.core.certificates
function(certs) { .renewAsync(options, results.certs)
// Workaround for https://github.com/nodejs/node/issues/22389 .then(
gl._updateServernames(certs); function(certs) {
cb(null, certs); // Workaround for https://github.com/nodejs/node/issues/22389
}, gl._updateServernames(certs);
function(e) { cb(null, certs);
console.debug( },
"Error renewing certificate for '" + domain + "':" function(e) {
); console.debug(
console.debug(e); "Error renewing certificate for '" +
console.error(""); domain +
cb(e); "':"
} );
); console.debug(e);
console.error('');
cb(e);
}
);
} else { } else {
log(gl.debug, "gl getting from disk or registering new"); log(
gl.debug,
'gl getting from disk or registering new'
);
return gl.core.certificates.getAsync(options).then( return gl.core.certificates.getAsync(options).then(
function(certs) { function(certs) {
// Workaround for https://github.com/nodejs/node/issues/22389 // Workaround for https://github.com/nodejs/node/issues/22389
@ -619,10 +653,12 @@ Greenlock.create = function(gl) {
}, },
function(e) { function(e) {
console.debug( console.debug(
"Error loading/registering certificate for '" + domain + "':" "Error loading/registering certificate for '" +
domain +
"':"
); );
console.debug(e); console.debug(e);
console.error(""); console.error('');
cb(e); cb(e);
} }
); );
@ -631,16 +667,20 @@ Greenlock.create = function(gl) {
function eb2(_err) { function eb2(_err) {
if (false !== gl.logRejectedDomains) { if (false !== gl.logRejectedDomains) {
console.error( console.error(
"[Error] approveDomains rejected tls sni '" + domain + "'" "[Error] approveDomains rejected tls sni '" +
domain +
"'"
); );
console.error( console.error(
"[Error] (see https://git.coolaj86.com/coolaj86/greenlock.js/issues/11)" '[Error] (see https://git.coolaj86.com/coolaj86/greenlock.js/issues/11)'
); );
if ("E_REJECT_SNI" !== _err.code) { if ('E_REJECT_SNI' !== _err.code) {
console.error("[Error] This is the rejection message:"); console.error(
'[Error] This is the rejection message:'
);
console.error(_err.message); console.error(_err.message);
} }
console.error(""); console.error('');
} }
cb(_err); cb(_err);
return; return;
@ -664,7 +704,9 @@ Greenlock.create = function(gl) {
gl.approveDomains(opts, certs, mb2); gl.approveDomains(opts, certs, mb2);
} }
} catch (e) { } catch (e) {
console.error("[ERROR] Something went wrong in approveDomains:"); console.error(
'[ERROR] Something went wrong in approveDomains:'
);
console.error(e); console.error(e);
console.error( console.error(
"BUT WAIT! Good news: It's probably your fault, so you can probably fix it." "BUT WAIT! Good news: It's probably your fault, so you can probably fix it."
@ -672,30 +714,36 @@ Greenlock.create = function(gl) {
} }
}; };
} }
gl.sni = gl.sni || require("le-sni-auto"); gl.sni = gl.sni || require('le-sni-auto');
if (gl.sni.create) { if (gl.sni.create) {
gl.sni = gl.sni.create(gl); gl.sni = gl.sni.create(gl);
} }
gl.tlsOptions.SNICallback = function(_domain, cb) { gl.tlsOptions.SNICallback = function(_domain, cb) {
// format and (lightly) sanitize sni so that users can be naive // format and (lightly) sanitize sni so that users can be naive
// and not have to worry about SQL injection or fs discovery // and not have to worry about SQL injection or fs discovery
var domain = (_domain || "").toLowerCase(); var domain = (_domain || '').toLowerCase();
// hostname labels allow a-z, 0-9, -, and are separated by dots // hostname labels allow a-z, 0-9, -, and are separated by dots
// _ is sometimes allowed // _ is sometimes allowed
// REGEX // https://www.codeproject.com/Questions/1063023/alphanumeric-validation-javascript-without-regex // REGEX // https://www.codeproject.com/Questions/1063023/alphanumeric-validation-javascript-without-regex
if ( if (
!gl.__sni_allow_dangerous_names && !gl.__sni_allow_dangerous_names &&
(!/^[a-z0-9_\.\-]+$/i.test(domain) || -1 !== domain.indexOf("..")) (!/^[a-z0-9_\.\-]+$/i.test(domain) ||
-1 !== domain.indexOf('..'))
) { ) {
log(gl.debug, "invalid sni '" + domain + "'"); log(gl.debug, "invalid sni '" + domain + "'");
cb(new Error("invalid SNI")); cb(new Error('invalid SNI'));
return; return;
} }
try { try {
gl.sni.sniCallback((gl.__sni_preserve_case && _domain) || domain, cb); gl.sni.sniCallback(
(gl.__sni_preserve_case && _domain) || domain,
cb
);
} catch (e) { } catch (e) {
console.error("[ERROR] Something went wrong in the SNICallback:"); console.error(
'[ERROR] Something went wrong in the SNICallback:'
);
console.error(e); console.error(e);
cb(e); cb(e);
} }
@ -723,7 +771,7 @@ Greenlock.create = function(gl) {
return gl.core.certificates.checkAsync(args); return gl.core.certificates.checkAsync(args);
}; };
gl.middleware = gl.middleware || require("./lib/middleware"); gl.middleware = gl.middleware || require('./lib/middleware');
if (gl.middleware.create) { if (gl.middleware.create) {
gl.middleware = gl.middleware.create(gl); gl.middleware = gl.middleware.create(gl);
} }
@ -733,17 +781,17 @@ Greenlock.create = function(gl) {
gl.middleware.sanitizeHost = function(app) { gl.middleware.sanitizeHost = function(app) {
return function(req, res, next) { return function(req, res, next) {
function realNext() { function realNext() {
if ("function" === typeof app) { if ('function' === typeof app) {
app(req, res); app(req, res);
} else if ("function" === typeof next) { } else if ('function' === typeof next) {
next(); next();
} else { } else {
res.statusCode = 500; res.statusCode = 500;
res.end("Error: no middleware assigned"); res.end('Error: no middleware assigned');
} }
} }
// Get the host:port combo, if it exists // Get the host:port combo, if it exists
var host = (req.headers.host || "").split(":"); var host = (req.headers.host || '').split(':');
// if not, move along // if not, move along
if (!host[0]) { if (!host[0]) {
@ -752,7 +800,7 @@ Greenlock.create = function(gl) {
} }
// if so, remove non-allowed characters // if so, remove non-allowed characters
var safehost = host[0].toLowerCase().replace(SERVERNAME_G, ""); var safehost = host[0].toLowerCase().replace(SERVERNAME_G, '');
// if there were unallowed characters, complain // if there were unallowed characters, complain
if ( if (
@ -767,27 +815,33 @@ Greenlock.create = function(gl) {
// make lowercase // make lowercase
if (!gl.__sni_preserve_case) { if (!gl.__sni_preserve_case) {
host[0] = safehost; host[0] = safehost;
req.headers.host = host.join(":"); req.headers.host = host.join(':');
} }
// Note: This sanitize function is also called on plain sockets, which don't need Domain Fronting checks // Note: This sanitize function is also called on plain sockets, which don't need Domain Fronting checks
if (req.socket.encrypted && !gl.__sni_allow_domain_fronting) { if (req.socket.encrypted && !gl.__sni_allow_domain_fronting) {
if (req.socket && "string" === typeof req.socket.servername) { if (req.socket && 'string' === typeof req.socket.servername) {
// Workaround for https://github.com/nodejs/node/issues/22389 // Workaround for https://github.com/nodejs/node/issues/22389
if ( if (
!gl._checkServername(safehost, req.socket.servername.toLowerCase()) !gl._checkServername(
safehost,
req.socket.servername.toLowerCase()
)
) { ) {
res.statusCode = 400; res.statusCode = 400;
res.setHeader("Content-Type", "text/html; charset=utf-8"); res.setHeader(
'Content-Type',
'text/html; charset=utf-8'
);
res.end( res.end(
"<h1>Domain Fronting Error</h1>" + '<h1>Domain Fronting Error</h1>' +
"<p>This connection was secured using TLS/SSL for '" + "<p>This connection was secured using TLS/SSL for '" +
req.socket.servername.toLowerCase() + req.socket.servername.toLowerCase() +
"'</p>" + "'</p>" +
"<p>The HTTP request specified 'Host: " + "<p>The HTTP request specified 'Host: " +
safehost + safehost +
"', which is (obviously) different.</p>" + "', which is (obviously) different.</p>" +
"<p>Because this looks like a domain fronting attack, the connection has been terminated.</p>" '<p>Because this looks like a domain fronting attack, the connection has been terminated.</p>'
); );
return; return;
} }
@ -797,7 +851,7 @@ Greenlock.create = function(gl) {
) { ) {
// TODO how to handle wrapped sockets, as with telebit? // TODO how to handle wrapped sockets, as with telebit?
console.warn( console.warn(
"\n\n\n[greenlock] WARN: no string for req.socket.servername," + '\n\n\n[greenlock] WARN: no string for req.socket.servername,' +
" skipping fronting check for '" + " skipping fronting check for '" +
safehost + safehost +
"'\n\n\n" "'\n\n\n"

View File

@ -1,65 +1,80 @@
'use strict'; 'use strict';
function addCommunityMember(opts) { function addCommunityMember(opts) {
// { name, version, email, domains, action, communityMember, telemetry } // { name, version, email, domains, action, communityMember, telemetry }
var https = require('https'); var https = require('https');
var req = https.request({ var req = https.request(
hostname: 'api.ppl.family' {
, port: 443 hostname: 'api.ppl.family',
, path: '/api/ppl.family/public/list' port: 443,
, method: 'POST' path: '/api/ppl.family/public/list',
, headers: { method: 'POST',
'Content-Type': 'application/json' headers: {
} 'Content-Type': 'application/json'
}, function (err, resp) { }
if (err) { return; } },
resp.on('data', function () {}); function(err, resp) {
}); if (err) {
req.on('error', function(error) { return;
/* ignore */ }
}); resp.on('data', function() {});
var os = require('os'); }
var data = { );
address: opts.email req.on('error', function(error) {
// greenlock-security is transactional and security only /* ignore */
, list: opts.communityMember ? (opts.name + '@ppl.family') : 'greenlock-security@ppl.family' });
, action: opts.action // reg | renew var os = require('os');
, package: opts.name var data = {
// hashed for privacy, but so we can still get some telemetry and inform users address: opts.email,
// if abnormal things are happening (like several registrations for the same domain each day) // greenlock-security is transactional and security only
, domain: (opts.domains||[]).map(function (d) { list: opts.communityMember
return require('crypto').createHash('sha1').update(d).digest('base64') ? opts.name + '@ppl.family'
.replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, ''); : 'greenlock-security@ppl.family',
}).join(',') action: opts.action, // reg | renew
}; package: opts.name,
if (false !== opts.telemetry) { // hashed for privacy, but so we can still get some telemetry and inform users
data.arch = process.arch || os.arch(); // if abnormal things are happening (like several registrations for the same domain each day)
data.platform = process.platform || os.platform(); domain: (opts.domains || [])
data.release = os.release(); .map(function(d) {
data.version = opts.version; return require('crypto')
data.node = process.version; .createHash('sha1')
} .update(d)
req.write(JSON.stringify(data, 2, null)); .digest('base64')
req.end(); .replace(/\//g, '_')
.replace(/\+/g, '-')
.replace(/=/g, '');
})
.join(',')
};
if (false !== opts.telemetry) {
data.arch = process.arch || os.arch();
data.platform = process.platform || os.platform();
data.release = os.release();
data.version = opts.version;
data.node = process.version;
}
req.write(JSON.stringify(data, 2, null));
req.end();
} }
function delay(ms) { function delay(ms) {
return new Promise(function (resolve) { return new Promise(function(resolve) {
return setTimeout(resolve, ms); return setTimeout(resolve, ms);
}); });
} }
module.exports.add = function (opts) { module.exports.add = function(opts) {
return delay(50).then(() => { return delay(50)
return addCommunityMember(opts); .then(() => {
}) return addCommunityMember(opts);
.catch(function (ex) { })
/* ignore */ .catch(function(ex) {
}) /* ignore */
} });
};
if (require.main === module) { if (require.main === module) {
//addCommunityMember('greenlock-express.js', 'reg', 'coolaj86+test42@gmail.com', ['coolaj86.com'], true); //addCommunityMember('greenlock-express.js', 'reg', 'coolaj86+test42@gmail.com', ['coolaj86.com'], true);
//addCommunityMember('greenlock.js', 'reg', 'coolaj86+test37@gmail.com', ['oneal.im'], false); //addCommunityMember('greenlock.js', 'reg', 'coolaj86+test37@gmail.com', ['oneal.im'], false);
//addCommunityMember('greenlock.js', 'reg', 'coolaj86+test11@gmail.com', ['ppl.family'], true); //addCommunityMember('greenlock.js', 'reg', 'coolaj86+test11@gmail.com', ['ppl.family'], true);
} }

View File

@ -1,21 +1,23 @@
'use strict'; 'use strict';
function requireBluebird() { function requireBluebird() {
try { try {
return require('bluebird'); return require('bluebird');
} catch(e) { } catch (e) {
console.error(""); console.error('');
console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support."); console.error(
console.error("EASY FIX: `npm install --save bluebird`"); "DON'T PANIC. You're running an old version of node with incomplete Promise support."
console.error(""); );
throw e; console.error('EASY FIX: `npm install --save bluebird`');
} console.error('');
throw e;
}
} }
if ('undefined' === typeof Promise) { if ('undefined' === typeof Promise) {
global.Promise = requireBluebird(); global.Promise = requireBluebird();
} }
if ('function' !== typeof require('util').promisify) { if ('function' !== typeof require('util').promisify) {
require('util').promisify = requireBluebird().promisify; require('util').promisify = requireBluebird().promisify;
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,92 +3,106 @@
var utils = require('./utils'); var utils = require('./utils');
function _log(debug) { function _log(debug) {
if (debug) { if (debug) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
args.shift(); args.shift();
args.unshift("[greenlock/lib/middleware.js]"); args.unshift('[greenlock/lib/middleware.js]');
console.log.apply(console, args); console.log.apply(console, args);
} }
} }
module.exports.create = function (gl) { module.exports.create = function(gl) {
if (!gl.challenges['http-01'] || !gl.challenges['http-01'].get) { if (!gl.challenges['http-01'] || !gl.challenges['http-01'].get) {
throw new Error("middleware requires challenge plugin with get method"); throw new Error('middleware requires challenge plugin with get method');
} }
var log = gl.log || _log; var log = gl.log || _log;
log(gl.debug, "created middleware"); log(gl.debug, 'created middleware');
return function (_app) { return function(_app) {
if (_app && 'function' !== typeof _app) { if (_app && 'function' !== typeof _app) {
throw new Error("use greenlock.middleware() or greenlock.middleware(function (req, res) {})"); throw new Error(
} 'use greenlock.middleware() or greenlock.middleware(function (req, res) {})'
var prefix = gl.acmeChallengePrefix || '/.well-known/acme-challenge/'; );
}
var prefix = gl.acmeChallengePrefix || '/.well-known/acme-challenge/';
return function (req, res, next) { return function(req, res, next) {
if (0 !== req.url.indexOf(prefix)) { if (0 !== req.url.indexOf(prefix)) {
log(gl.debug, "no match, skipping middleware"); log(gl.debug, 'no match, skipping middleware');
if ('function' === typeof _app) { if ('function' === typeof _app) {
_app(req, res, next); _app(req, res, next);
} } else if ('function' === typeof next) {
else if ('function' === typeof next) { next();
next(); } else {
} res.statusCode = 500;
else { res.end(
res.statusCode = 500; "[500] Developer Error: app.use('/', greenlock.middleware()) or greenlock.middleware(app)"
res.end("[500] Developer Error: app.use('/', greenlock.middleware()) or greenlock.middleware(app)"); );
} }
return; return;
} }
log(gl.debug, "this must be tinder, 'cuz it's a match!"); log(gl.debug, "this must be tinder, 'cuz it's a match!");
var token = req.url.slice(prefix.length); var token = req.url.slice(prefix.length);
var hostname = req.hostname || (req.headers.host || '').toLowerCase().replace(/:.*/, ''); var hostname =
req.hostname ||
(req.headers.host || '').toLowerCase().replace(/:.*/, '');
log(gl.debug, "hostname", hostname, "token", token); log(gl.debug, 'hostname', hostname, 'token', token);
var copy = utils.merge({ domains: [ hostname ] }, gl); var copy = utils.merge({ domains: [hostname] }, gl);
copy = utils.tplCopy(copy); copy = utils.tplCopy(copy);
copy.challenge = {}; copy.challenge = {};
copy.challenge.type = 'http-01'; // obviously... copy.challenge.type = 'http-01'; // obviously...
copy.challenge.identifier = { type: 'dns', value: hostname }; copy.challenge.identifier = { type: 'dns', value: hostname };
copy.challenge.wildcard = false; copy.challenge.wildcard = false;
copy.challenge.token = token; copy.challenge.token = token;
copy.challenge.altname = hostname; copy.challenge.altname = hostname;
function cb(opts) { function cb(opts) {
var secret = opts.keyAuthorization || opts; var secret = opts.keyAuthorization || opts;
if (secret && 'string' === typeof secret) { if (secret && 'string' === typeof secret) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end(secret); res.end(secret);
return; return;
} }
eb(new Error("couldn't retrieve keyAuthorization")); eb(new Error("couldn't retrieve keyAuthorization"));
return; return;
} }
function eb(/*err*/) { function eb(/*err*/) {
res.statusCode = 404; res.statusCode = 404;
res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.setHeader(
res.end('{ "error": { "message": "Error: These aren\'t the tokens you\'re looking for. Move along." } }'); 'Content-Type',
return; 'application/json; charset=utf-8'
} );
function mb(err, result) { res.end(
if (err) { eb(err); return; } '{ "error": { "message": "Error: These aren\'t the tokens you\'re looking for. Move along." } }'
cb(result); );
} return;
}
function mb(err, result) {
if (err) {
eb(err);
return;
}
cb(result);
}
var challenger = gl.challenges['http-01'].get; var challenger = gl.challenges['http-01'].get;
if (1 === challenger.length) { if (1 === challenger.length) {
/*global Promise*/ /*global Promise*/
return Promise.resolve().then(function () { return Promise.resolve()
return gl.challenges['http-01'].get(copy); .then(function() {
}).then(cb).catch(eb); return gl.challenges['http-01'].get(copy);
} else if (2 === challenger.length) { })
gl.challenges['http-01'].get(copy, mb); .then(cb)
} else { .catch(eb);
gl.challenges['http-01'].get(copy, hostname, token, mb); } else if (2 === challenger.length) {
} gl.challenges['http-01'].get(copy, mb);
} else {
}; gl.challenges['http-01'].get(copy, hostname, token, mb);
}; }
};
};
}; };

View File

@ -1,24 +1,24 @@
'use strict'; 'use strict';
var utils = require('./utils.js') var utils = require('./utils.js');
var cert = { subject: 'example.com', altnames: ['*.bar.com','foo.net'] }; var cert = { subject: 'example.com', altnames: ['*.bar.com', 'foo.net'] };
if (utils.certHasDomain(cert, 'bad.com')) { if (utils.certHasDomain(cert, 'bad.com')) {
throw new Error("allowed bad domain"); throw new Error('allowed bad domain');
} }
if (!utils.certHasDomain(cert, 'example.com')) { if (!utils.certHasDomain(cert, 'example.com')) {
throw new Error("missed subject"); throw new Error('missed subject');
} }
if (utils.certHasDomain(cert, 'bar.com')) { if (utils.certHasDomain(cert, 'bar.com')) {
throw new Error("allowed bad (missing) sub"); throw new Error('allowed bad (missing) sub');
} }
if (!utils.certHasDomain(cert, 'foo.bar.com')) { if (!utils.certHasDomain(cert, 'foo.bar.com')) {
throw new Error("didn't allow valid wildcarded-domain"); throw new Error("didn't allow valid wildcarded-domain");
} }
if (utils.certHasDomain(cert, 'dub.foo.bar.com')) { if (utils.certHasDomain(cert, 'dub.foo.bar.com')) {
throw new Error("allowed sub-sub domain"); throw new Error('allowed sub-sub domain');
} }
if (!utils.certHasDomain(cert, 'foo.net')) { if (!utils.certHasDomain(cert, 'foo.net')) {
throw new Error("missed altname"); throw new Error('missed altname');
} }
console.info("PASSED"); console.info('PASSED');

View File

@ -2,59 +2,62 @@
require('./compat.js'); 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 dnsResolveMxAsync = require('util').promisify(require('dns').resolveMx); var dnsResolveMxAsync = require('util').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);
// subject, altnames, issuedAt, expiresAt // subject, altnames, issuedAt, expiresAt
Object.keys(certInfo).forEach(function (key) { Object.keys(certInfo).forEach(function(key) {
results[key] = certInfo[key]; results[key] = certInfo[key];
}); });
return results; return results;
}; };
module.exports.certHasDomain = function (certInfo, _domain) { module.exports.certHasDomain = function(certInfo, _domain) {
var names = (certInfo.altnames || []).slice(0); var names = (certInfo.altnames || []).slice(0);
names.push(certInfo.subject); names.push(certInfo.subject);
return names.some(function (name) { return names.some(function(name) {
var domain = _domain.toLowerCase(); var domain = _domain.toLowerCase();
name = name.toLowerCase(); name = name.toLowerCase();
if ('*.' === name.substr(0, 2)) { if ('*.' === name.substr(0, 2)) {
name = name.substr(2); name = name.substr(2);
domain = domain.split('.').slice(1).join('.'); domain = domain
} .split('.')
return name === domain; .slice(1)
}); .join('.');
}
return name === domain;
});
}; };
module.exports.isValidDomain = function (domain) { module.exports.isValidDomain = function(domain) {
if (re.test(domain)) { if (re.test(domain)) {
return domain; return domain;
} }
domain = punycode.toASCII(domain); domain = punycode.toASCII(domain);
if (re.test(domain)) { if (re.test(domain)) {
return domain; return domain;
} }
return ''; return '';
}; };
module.exports.merge = function (/*defaults, args*/) { module.exports.merge = function(/*defaults, args*/) {
var allDefaults = Array.prototype.slice.apply(arguments); var allDefaults = Array.prototype.slice.apply(arguments);
var args = allDefaults.shift(); var args = allDefaults.shift();
var copy = {}; var copy = {};
allDefaults.forEach(function (defaults) { allDefaults.forEach(function(defaults) {
Object.keys(defaults).forEach(function (key) { Object.keys(defaults).forEach(function(key) {
/* /*
if ('challenges' === key && copy[key] && defaults[key]) { if ('challenges' === key && copy[key] && defaults[key]) {
Object.keys(defaults[key]).forEach(function (k) { Object.keys(defaults[key]).forEach(function (k) {
copy[key][k] = defaults[key][k]; copy[key][k] = defaults[key][k];
@ -63,12 +66,12 @@ module.exports.merge = function (/*defaults, args*/) {
copy[key] = defaults[key]; copy[key] = defaults[key];
} }
*/ */
copy[key] = defaults[key]; copy[key] = defaults[key];
}); });
}); });
Object.keys(args).forEach(function (key) { Object.keys(args).forEach(function(key) {
/* /*
if ('challenges' === key && copy[key] && args[key]) { if ('challenges' === key && copy[key] && args[key]) {
Object.keys(args[key]).forEach(function (k) { Object.keys(args[key]).forEach(function (k) {
copy[key][k] = args[key][k]; copy[key][k] = args[key][k];
@ -77,81 +80,86 @@ module.exports.merge = function (/*defaults, args*/) {
copy[key] = args[key]; copy[key] = args[key];
} }
*/ */
copy[key] = args[key]; copy[key] = args[key];
}); });
return copy; return copy;
}; };
module.exports.tplCopy = function (copy) { module.exports.tplCopy = function(copy) {
var homedir = require('os').homedir(); var homedir = require('os').homedir();
var tplKeys; var tplKeys;
copy.hostnameGet = function (copy) { copy.hostnameGet = function(copy) {
return copy.subject || (copy.domains || [])[0] || copy.domain; return copy.subject || (copy.domains || [])[0] || copy.domain;
}; };
Object.keys(copy).forEach(function (key) { Object.keys(copy).forEach(function(key) {
var newName; var newName;
if (!/Get$/.test(key)) { if (!/Get$/.test(key)) {
return; return;
} }
newName = key.replace(/Get$/, ''); newName = key.replace(/Get$/, '');
copy[newName] = copy[newName] || copy[key](copy); copy[newName] = copy[newName] || copy[key](copy);
}); });
tplKeys = Object.keys(copy); tplKeys = Object.keys(copy);
tplKeys.sort(function (a, b) { tplKeys.sort(function(a, b) {
return b.length - a.length; return b.length - a.length;
}); });
tplKeys.forEach(function (key) { tplKeys.forEach(function(key) {
if ('string' !== typeof copy[key]) { if ('string' !== typeof copy[key]) {
return; return;
} }
copy[key] = copy[key].replace(homeRe, homedir + path.sep); copy[key] = copy[key].replace(homeRe, homedir + path.sep);
}); });
tplKeys.forEach(function (key) { tplKeys.forEach(function(key) {
if ('string' !== typeof copy[key]) { if ('string' !== typeof copy[key]) {
return; return;
} }
tplKeys.forEach(function (tplname) { tplKeys.forEach(function(tplname) {
if (!copy[tplname]) { if (!copy[tplname]) {
// what can't be templated now may be templatable later // what can't be templated now may be templatable later
return; return;
} }
copy[key] = copy[key].replace(':' + tplname, copy[tplname]); copy[key] = copy[key].replace(':' + tplname, copy[tplname]);
}); });
}); });
return copy; return copy;
}; };
module.exports.testEmail = function (email) { module.exports.testEmail = function(email) {
var parts = (email||'').split('@'); var parts = (email || '').split('@');
var err; var err;
if (2 !== parts.length || !parts[0] || !parts[1]) { if (2 !== parts.length || !parts[0] || !parts[1]) {
err = new Error("malformed email address '" + email + "'"); err = new Error("malformed email address '" + email + "'");
err.code = 'E_EMAIL'; err.code = 'E_EMAIL';
return Promise.reject(err); return Promise.reject(err);
} }
return dnsResolveMxAsync(parts[1]).then(function (records) { return dnsResolveMxAsync(parts[1]).then(
// records only returns when there is data function(records) {
if (!records.length) { // records only returns when there is data
throw new Error("sanity check fail: success, but no MX records returned"); if (!records.length) {
} throw new Error(
return email; 'sanity check fail: success, but no MX records returned'
}, function (err) { );
if ('ENODATA' === err.code) { }
err = new Error("no MX records found for '" + parts[1] + "'"); return email;
err.code = 'E_EMAIL'; },
return Promise.reject(err); function(err) {
} if ('ENODATA' === err.code) {
}); err = new Error("no MX records found for '" + parts[1] + "'");
err.code = 'E_EMAIL';
return Promise.reject(err);
}
}
);
}; };

View File

@ -2,13 +2,13 @@
var LE = require('../').LE; var LE = require('../').LE;
var le = LE.create({ var le = LE.create({
server: 'staging' server: 'staging',
, acme: require('le-acme-core').ACME.create() acme: require('le-acme-core').ACME.create(),
, store: require('le-store-certbot').create({ store: require('le-store-certbot').create({
configDir: '~/letsencrypt.test/etc/' configDir: '~/letsencrypt.test/etc/',
, webrootPath: '~/letsencrypt.test/tmp/:hostname' webrootPath: '~/letsencrypt.test/tmp/:hostname'
}) }),
, debug: true debug: true
}); });
// TODO test generateRsaKey code path separately // TODO test generateRsaKey code path separately
@ -20,37 +20,45 @@ var testEmail = 'coolaj86+le.' + testId + '@gmail.com';
var testAccountId = '939573edbf2506c92c9ab32131209d7b'; var testAccountId = '939573edbf2506c92c9ab32131209d7b';
var tests = [ var tests = [
function () { function() {
return le.core.accounts.checkAsync({ return le.core.accounts
accountId: testAccountId .checkAsync({
}).then(function (account) { accountId: testAccountId
if (!account) { })
throw new Error("Test account should exist when searched by account id."); .then(function(account) {
} if (!account) {
}); throw new Error(
} 'Test account should exist when searched by account id.'
);
}
});
},
, function () { function() {
return le.core.accounts.checkAsync({ return le.core.accounts
email: testEmail .checkAsync({
}).then(function (account) { email: testEmail
console.log('account.regr'); })
console.log(account.regr); .then(function(account) {
if (!account) { console.log('account.regr');
throw new Error("Test account should exist when searched by email."); console.log(account.regr);
} if (!account) {
}); throw new Error(
} 'Test account should exist when searched by email.'
);
}
});
}
]; ];
function run() { function run() {
var test = tests.shift(); var test = tests.shift();
if (!test) { if (!test) {
console.info('All tests passed'); console.info('All tests passed');
return; return;
} }
test().then(run); test().then(run);
} }
run(); run();

View File

@ -2,13 +2,13 @@
var LE = require('../').LE; var LE = require('../').LE;
var le = LE.create({ var le = LE.create({
server: 'staging' server: 'staging',
, acme: require('le-acme-core').ACME.create() acme: require('le-acme-core').ACME.create(),
, store: require('le-store-certbot').create({ store: require('le-store-certbot').create({
configDir: '~/letsencrypt.test/etc/' configDir: '~/letsencrypt.test/etc/',
, webrootPath: '~/letsencrypt.test/tmp/:hostname' webrootPath: '~/letsencrypt.test/tmp/:hostname'
}) }),
, debug: true debug: true
}); });
//var testId = Math.round(Date.now() / 1000).toString(); //var testId = Math.round(Date.now() / 1000).toString();
@ -18,88 +18,117 @@ var testEmail = 'coolaj86+le.' + testId + '@gmail.com';
var testAccount; var testAccount;
var tests = [ var tests = [
function () { function() {
return le.core.accounts.checkAsync({ return le.core.accounts
email: testEmail .checkAsync({
}).then(function (account) { email: testEmail
if (account) { })
console.error(account); .then(function(account) {
throw new Error("Test account should not exist."); if (account) {
} console.error(account);
}); throw new Error('Test account should not exist.');
} }
, function () { });
return le.core.accounts.registerAsync({ },
email: testEmail function() {
, agreeTos: false return le.core.accounts
, rsaKeySize: 2048 .registerAsync({
}).then(function (/*account*/) { email: testEmail,
throw new Error("Should not register if 'agreeTos' is not truthy."); agreeTos: false,
}, function (err) { rsaKeySize: 2048
if (err.code !== 'E_ARGS') { })
throw err; .then(
} function(/*account*/) {
}); throw new Error(
} "Should not register if 'agreeTos' is not truthy."
, function () { );
return le.core.accounts.registerAsync({ },
email: testEmail function(err) {
, agreeTos: true if (err.code !== 'E_ARGS') {
, rsaKeySize: 1024 throw err;
}).then(function (/*account*/) { }
throw new Error("Should not register if 'rsaKeySize' is less than 2048."); }
}, function (err) { );
if (err.code !== 'E_ARGS') { },
throw err; function() {
} return le.core.accounts
}); .registerAsync({
} email: testEmail,
, function () { agreeTos: true,
return le.core.accounts.registerAsync({ rsaKeySize: 1024
email: fakeEmail })
, agreeTos: true .then(
, rsaKeySize: 2048 function(/*account*/) {
}).then(function (/*account*/) { throw new Error(
// TODO test mx record "Should not register if 'rsaKeySize' is less than 2048."
throw new Error("Registration should NOT succeed with a bad email address."); );
}, function (err) { },
if (err.code !== 'E_EMAIL') { function(err) {
throw err; if (err.code !== 'E_ARGS') {
} throw err;
}); }
} }
, function () { );
return le.core.accounts.registerAsync({ },
email: testEmail function() {
, agreeTos: true return le.core.accounts
, rsaKeySize: 2048 .registerAsync({
}).then(function (account) { email: fakeEmail,
testAccount = account; agreeTos: true,
rsaKeySize: 2048
})
.then(
function(/*account*/) {
// TODO test mx record
throw new Error(
'Registration should NOT succeed with a bad email address.'
);
},
function(err) {
if (err.code !== 'E_EMAIL') {
throw err;
}
}
);
},
function() {
return le.core.accounts
.registerAsync({
email: testEmail,
agreeTos: true,
rsaKeySize: 2048
})
.then(function(account) {
testAccount = account;
console.log(testEmail); console.log(testEmail);
console.log(testAccount); console.log(testAccount);
if (!account) { if (!account) {
throw new Error("Registration should always return a new account."); throw new Error(
} 'Registration should always return a new account.'
if (!account.email) { );
throw new Error("Registration should return the email."); }
} if (!account.email) {
if (!account.id) { throw new Error('Registration should return the email.');
throw new Error("Registration should return the account id."); }
} if (!account.id) {
}); throw new Error(
} 'Registration should return the account id.'
);
}
});
}
]; ];
function run() { function run() {
var test = tests.shift(); var test = tests.shift();
if (!test) { if (!test) {
console.info('All tests passed'); console.info('All tests passed');
return; return;
} }
test().then(run); test().then(run);
} }
run(); run();

View File

@ -2,16 +2,16 @@
var LE = require('../').LE; var LE = require('../').LE;
var le = LE.create({ var le = LE.create({
server: 'staging' server: 'staging',
, acme: require('le-acme-core').ACME.create() acme: require('le-acme-core').ACME.create(),
, store: require('le-store-certbot').create({ store: require('le-store-certbot').create({
configDir: '~/letsencrypt.test/etc' configDir: '~/letsencrypt.test/etc',
, webrootPath: '~/letsencrypt.test/var/:hostname' webrootPath: '~/letsencrypt.test/var/:hostname'
}) }),
, challenge: require('le-challenge-fs').create({ challenge: require('le-challenge-fs').create({
webrootPath: '~/letsencrypt.test/var/:hostname' webrootPath: '~/letsencrypt.test/var/:hostname'
}) }),
, debug: true debug: true
}); });
// TODO test generateRsaKey code path separately // TODO test generateRsaKey code path separately
@ -21,54 +21,62 @@ var le = LE.create({
var testId = 'test1000'; var testId = 'test1000';
var testEmail = 'coolaj86+le.' + testId + '@gmail.com'; var testEmail = 'coolaj86+le.' + testId + '@gmail.com';
// TODO integrate with Daplie Domains for junk domains to test with // TODO integrate with Daplie Domains for junk domains to test with
var testDomains = [ 'pokemap.hellabit.com', 'www.pokemap.hellabit.com' ]; var testDomains = ['pokemap.hellabit.com', 'www.pokemap.hellabit.com'];
var tests = [ var tests = [
function () { function() {
return le.core.certificates.checkAsync({ return le.core.certificates
domains: [ 'example.com', 'www.example.com' ] .checkAsync({
}).then(function (cert) { domains: ['example.com', 'www.example.com']
if (cert) { })
throw new Error("Bogus domain should not have certificate."); .then(function(cert) {
} if (cert) {
}); throw new Error(
} 'Bogus domain should not have certificate.'
);
}
});
},
, function () { function() {
return le.core.certificates.getAsync({ return le.core.certificates
email: testEmail .getAsync({
, domains: testDomains email: testEmail,
}).then(function (certs) { domains: testDomains
if (!certs) { })
throw new Error("Should have acquired certificate for domains."); .then(function(certs) {
} if (!certs) {
}); throw new Error(
} 'Should have acquired certificate for domains.'
);
}
});
}
]; ];
function run() { function run() {
//var express = require(express); //var express = require(express);
var server = require('http').createServer(le.middleware()); var server = require('http').createServer(le.middleware());
server.listen(80, function () { server.listen(80, function() {
console.log('Server running, proceeding to test.'); console.log('Server running, proceeding to test.');
function next() { function next() {
var test = tests.shift(); var test = tests.shift();
if (!test) { if (!test) {
server.close(); server.close();
console.info('All tests passed'); console.info('All tests passed');
return; return;
} }
test().then(next, function (err) { test().then(next, function(err) {
console.error('ERROR'); console.error('ERROR');
console.error(err.stack); console.error(err.stack);
server.close(); server.close();
}); });
} }
next(); next();
}); });
} }
run(); run();

View File

@ -2,16 +2,16 @@
var LE = require('../').LE; var LE = require('../').LE;
var le = LE.create({ var le = LE.create({
server: 'staging' server: 'staging',
, acme: require('le-acme-core').ACME.create() acme: require('le-acme-core').ACME.create(),
, store: require('le-store-certbot').create({ store: require('le-store-certbot').create({
configDir: '~/letsencrypt.test/etc' configDir: '~/letsencrypt.test/etc',
, webrootPath: '~/letsencrypt.test/var/:hostname' webrootPath: '~/letsencrypt.test/var/:hostname'
}) }),
, challenge: require('le-challenge-fs').create({ challenge: require('le-challenge-fs').create({
webrootPath: '~/letsencrypt.test/var/:hostname' webrootPath: '~/letsencrypt.test/var/:hostname'
}) }),
, debug: true debug: true
}); });
// TODO test generateRsaKey code path separately // TODO test generateRsaKey code path separately
@ -21,82 +21,117 @@ var le = LE.create({
var testId = 'test1000'; var testId = 'test1000';
var testEmail = 'coolaj86+le.' + testId + '@gmail.com'; var testEmail = 'coolaj86+le.' + testId + '@gmail.com';
// TODO integrate with Daplie Domains for junk domains to test with // TODO integrate with Daplie Domains for junk domains to test with
var testDomains = [ 'pokemap.hellabit.com', 'www.pokemap.hellabit.com' ]; var testDomains = ['pokemap.hellabit.com', 'www.pokemap.hellabit.com'];
var testCerts; var testCerts;
var tests = [ var tests = [
function () { function() {
// TODO test that an altname also fetches the proper certificate // TODO test that an altname also fetches the proper certificate
return le.core.certificates.checkAsync({ return le.core.certificates
domains: testDomains .checkAsync({
}).then(function (certs) { domains: testDomains
if (!certs) { })
throw new Error("Either certificates.registerAsync (in previous test)" .then(function(certs) {
+ " or certificates.checkAsync (in this test) failed."); if (!certs) {
} throw new Error(
'Either certificates.registerAsync (in previous test)' +
' or certificates.checkAsync (in this test) failed.'
);
}
testCerts = certs; testCerts = certs;
console.log('Issued At', new Date(certs.issuedAt).toISOString()); console.log(
console.log('Expires At', new Date(certs.expiresAt).toISOString()); 'Issued At',
new Date(certs.issuedAt).toISOString()
);
console.log(
'Expires At',
new Date(certs.expiresAt).toISOString()
);
if (certs.expiresAt <= Date.now()) { if (certs.expiresAt <= Date.now()) {
throw new Error("Certificates are already expired. They cannot be tested for duplicate or forced renewal."); throw new Error(
} 'Certificates are already expired. They cannot be tested for duplicate or forced renewal.'
}); );
} }
});
},
, function () { function() {
return le.core.certificates.renewAsync({ return le.core.certificates
email: testEmail .renewAsync(
, domains: testDomains {
}, testCerts).then(function () { email: testEmail,
throw new Error("Should not have renewed non-expired certificates."); domains: testDomains
}, function (err) { },
if ('E_NOT_RENEWABLE' !== err.code) { testCerts
throw err; )
} .then(
}); function() {
} throw new Error(
'Should not have renewed non-expired certificates.'
);
},
function(err) {
if ('E_NOT_RENEWABLE' !== err.code) {
throw err;
}
}
);
},
, function () { function() {
return le.core.certificates.renewAsync({ return le.core.certificates
email: testEmail .renewAsync(
, domains: testDomains {
, renewWithin: 720 * 24 * 60 * 60 * 1000 email: testEmail,
}, testCerts).then(function (certs) { domains: testDomains,
console.log('Issued At', new Date(certs.issuedAt).toISOString()); renewWithin: 720 * 24 * 60 * 60 * 1000
console.log('Expires At', new Date(certs.expiresAt).toISOString()); },
testCerts
)
.then(function(certs) {
console.log(
'Issued At',
new Date(certs.issuedAt).toISOString()
);
console.log(
'Expires At',
new Date(certs.expiresAt).toISOString()
);
if (certs.issuedAt === testCerts.issuedAt) { if (certs.issuedAt === testCerts.issuedAt) {
throw new Error("Should not have returned existing certificates."); throw new Error(
} 'Should not have returned existing certificates.'
}); );
} }
});
}
]; ];
function run() { function run() {
//var express = require(express); //var express = require(express);
var server = require('http').createServer(le.middleware()); var server = require('http').createServer(le.middleware());
server.listen(80, function () { server.listen(80, function() {
console.log('Server running, proceeding to test.'); console.log('Server running, proceeding to test.');
function next() { function next() {
var test = tests.shift(); var test = tests.shift();
if (!test) { if (!test) {
server.close(); server.close();
console.info('All tests passed'); console.info('All tests passed');
return; return;
} }
test().then(next, function (err) { test().then(next, function(err) {
console.error('ERROR'); console.error('ERROR');
console.error(err.stack); console.error(err.stack);
server.close(); server.close();
}); });
} }
next(); next();
}); });
} }
run(); run();