greenlock.js/index.js

230 lines
7.4 KiB
JavaScript
Raw Normal View History

2015-12-11 14:22:46 +00:00
'use strict';
2015-12-13 05:03:48 +00:00
// TODO handle www and no-www together somehow?
2015-12-12 15:05:45 +00:00
var PromiseA = require('bluebird');
2015-12-17 04:59:47 +00:00
var leCore = require('letiny-core');
2016-08-04 22:49:35 +00:00
var utils = require('./lib/common');
2015-12-17 08:46:40 +00:00
var merge = require('./lib/common').merge;
var tplCopy = require('./lib/common').tplCopy;
2015-12-12 15:05:45 +00:00
2015-12-13 01:04:12 +00:00
var LE = module.exports;
2016-08-04 22:49:35 +00:00
LE.merge = require('./lib/common').merge;
2016-02-13 02:33:50 +00:00
LE.defaults = {
2016-08-04 22:49:35 +00:00
server: leCore.productionServerUrl
, stagingServer: leCore.stagingServerUrl
, liveServer: leCore.productionServerUrl
, productionServerUrl: leCore.productionServerUrl
, stagingServerUrl: leCore.stagingServerUrl
, acmeChallengePrefix: leCore.acmeChallengePrefix
2016-02-13 02:33:50 +00:00
};
2015-12-20 10:41:17 +00:00
2015-12-16 09:11:31 +00:00
// backwards compat
2016-08-04 22:49:35 +00:00
Object.keys(LE.defaults).forEach(function (key) {
LE[key] = LE.defaults[key];
});
2015-12-13 01:04:12 +00:00
2015-12-16 09:11:31 +00:00
// backend, defaults, handlers
LE.create = function (defaults, handlers, backend) {
2016-08-04 22:49:35 +00:00
var Backend = require('./lib/core');
if (!backend) { backend = require('./lib/pycompat').create(defaults); }
2015-12-13 01:04:12 +00:00
if (!handlers) { handlers = {}; }
2015-12-13 05:03:48 +00:00
if (!handlers.lifetime) { handlers.lifetime = 90 * 24 * 60 * 60 * 1000; }
if (!handlers.renewWithin) { handlers.renewWithin = 3 * 24 * 60 * 60 * 1000; }
2015-12-13 01:04:12 +00:00
if (!handlers.memorizeFor) { handlers.memorizeFor = 1 * 24 * 60 * 60 * 1000; }
2015-12-13 05:03:48 +00:00
if (!handlers.sniRegisterCallback) {
handlers.sniRegisterCallback = function (args, cache, cb) {
// TODO when we have ECDSA, just do this automatically
cb(null, null);
};
}
if (!handlers.getChallenge) {
2015-12-16 12:57:53 +00:00
if (!defaults.manual && !defaults.webrootPath) {
// GET /.well-known/acme-challenge/{{challengeKey}} should return {{tokenValue}}
throw new Error("handlers.getChallenge or defaults.webrootPath must be set");
}
handlers.getChallenge = function (hostname, key, done) {
// TODO associate by hostname?
// hmm... I don't think there's a direct way to associate this with
// the request it came from... it's kinda stateless in that way
// but realistically there only needs to be one handler and one
// "directory" for this. It's not that big of a deal.
2016-08-04 22:49:35 +00:00
var defaultos = LE.merge({}, defaults);
var getChallenge = require('./lib/default-handlers').getChallenge;
2016-08-04 22:49:35 +00:00
var copy = merge({ domains: [hostname] }, defaults);
2015-12-17 08:46:40 +00:00
tplCopy(copy);
defaultos.domains = [hostname];
2015-12-17 08:46:40 +00:00
if (3 === getChallenge.length) {
2016-08-04 22:49:35 +00:00
console.warn('[WARNING] Deprecated use. Define getChallenge as function (opts, domain, key, cb) { }');
getChallenge(defaultos, key, done);
}
else if (4 === getChallenge.length) {
getChallenge(defaultos, hostname, key, done);
}
else {
done(new Error("handlers.getChallenge [1] receives the wrong number of arguments"));
}
};
}
2015-12-15 11:38:21 +00:00
if (!handlers.setChallenge) {
if (!defaults.webrootPath) {
// GET /.well-known/acme-challenge/{{challengeKey}} should return {{tokenValue}}
throw new Error("handlers.setChallenge or defaults.webrootPath must be set");
}
2015-12-15 12:01:05 +00:00
handlers.setChallenge = require('./lib/default-handlers').setChallenge;
2015-12-15 11:38:21 +00:00
}
if (!handlers.removeChallenge) {
if (!defaults.webrootPath) {
// GET /.well-known/acme-challenge/{{challengeKey}} should return {{tokenValue}}
throw new Error("handlers.removeChallenge or defaults.webrootPath must be set");
2015-12-15 11:38:21 +00:00
}
2015-12-15 12:01:46 +00:00
handlers.removeChallenge = require('./lib/default-handlers').removeChallenge;
2015-12-15 11:38:21 +00:00
}
if (!handlers.agreeToTerms) {
if (defaults.agreeTos) {
console.warn("[WARN] Agreeing to terms by default is risky business...");
}
2015-12-15 13:12:16 +00:00
handlers.agreeToTerms = require('./lib/default-handlers').agreeToTerms;
2015-12-15 11:38:21 +00:00
}
2015-12-17 08:46:40 +00:00
2016-08-04 22:49:35 +00:00
backend = Backend.create(defaults, handlers);
2015-12-13 05:03:48 +00:00
backend = PromiseA.promisifyAll(backend);
2015-12-13 01:04:12 +00:00
2015-12-11 14:22:46 +00:00
//var attempts = {}; // should exist in master process only
2015-12-12 14:20:12 +00:00
var le;
2015-12-11 14:22:46 +00:00
2015-12-12 15:05:45 +00:00
// TODO check certs on initial load
// TODO expect that certs expire every 90 days
// TODO check certs with setInterval?
//options.cacheContextsFor = options.cacheContextsFor || (1 * 60 * 60 * 1000);
2015-12-11 14:22:46 +00:00
2015-12-12 14:20:12 +00:00
le = {
2015-12-15 12:12:15 +00:00
backend: backend
2015-12-20 10:41:17 +00:00
, pyToJson: function (pyobj) {
if (!pyobj) {
return null;
}
var jsobj = {};
Object.keys(pyobj).forEach(function (key) {
jsobj[key] = pyobj[key];
});
jsobj.__lines = undefined;
jsobj.__keys = undefined;
return jsobj;
}
2016-08-04 18:26:49 +00:00
, register: function (args, cb) {
if (defaults.debug || args.debug) {
console.log('[LE] register');
}
if (!Array.isArray(args.domains)) {
cb(new Error('args.domains should be an array of domains'));
2015-12-13 01:04:12 +00:00
return;
}
2016-08-04 22:49:35 +00:00
var copy = LE.merge(args, defaults);
2015-12-13 01:04:12 +00:00
var err;
2015-12-12 14:20:12 +00:00
if (!utils.isValidDomain(args.domains[0])) {
2016-08-04 18:26:49 +00:00
err = new Error("invalid domain name: '" + args.domains + "'");
2015-12-13 01:04:12 +00:00
err.code = "INVALID_DOMAIN";
cb(err);
return;
2015-12-11 14:22:46 +00:00
}
2015-12-12 14:20:12 +00:00
2016-08-04 18:26:49 +00:00
if ((!args.domains.length && args.domains.every(le.isValidDomain))) {
// NOTE: this library can't assume to handle the http loopback
// (or dns-01 validation may be used)
// so we do not check dns records or attempt a loopback here
cb(new Error("node-letsencrypt: invalid hostnames: " + args.domains.join(',')));
return;
}
2015-12-13 01:04:12 +00:00
2016-08-04 18:26:49 +00:00
if (defaults.debug || args.debug) {
console.log("[NLE]: begin registration");
}
return backend.registerAsync(copy).then(function (pems) {
2016-02-10 20:41:15 +00:00
if (defaults.debug || args.debug) {
2016-08-04 18:26:49 +00:00
console.log("[NLE]: end registration");
}
2016-08-04 18:26:49 +00:00
cb(null, pems);
//return le.fetch(args, cb);
}, cb);
2015-12-12 14:20:12 +00:00
}
2016-08-04 18:26:49 +00:00
, fetch: function (args, cb) {
if (defaults.debug || args.debug) {
console.log('[LE] fetch');
}
2016-08-04 22:49:35 +00:00
// TODO figure out what TPLs are needed
var copy = merge(args, defaults);
tplCopy(copy);
2015-12-13 05:03:48 +00:00
return backend.fetchAsync(args).then(function (certInfo) {
if (args.debug) {
2015-12-17 08:46:40 +00:00
console.log('[LE] raw fetch certs', certInfo && Object.keys(certInfo));
}
if (!certInfo) { cb(null, null); return; }
2015-12-13 05:03:48 +00:00
// key, cert, issuedAt, lifetime, expiresAt
if (!certInfo.expiresAt) {
certInfo.expiresAt = certInfo.issuedAt + (certInfo.lifetime || handlers.lifetime);
}
if (!certInfo.lifetime) {
certInfo.lifetime = (certInfo.lifetime || handlers.lifetime);
}
// a pretty good hard buffer
certInfo.expiresAt -= (1 * 24 * 60 * 60 * 100);
cb(null, certInfo);
2015-12-13 05:03:48 +00:00
}, cb);
}
2015-12-20 10:41:17 +00:00
, getConfig: function (args, cb) {
2015-12-21 17:27:57 +00:00
if (defaults.debug || args.debug) {
console.log('[LE] getConfig');
}
2015-12-20 10:41:17 +00:00
backend.getConfigAsync(args).then(function (pyobj) {
cb(null, le.pyToJson(pyobj));
}, function (err) {
2016-02-10 20:41:15 +00:00
console.error("[letsencrypt/index.js] getConfig");
2015-12-20 10:41:17 +00:00
console.error(err.stack);
return cb(null, []);
});
}
, getConfigs: function (args, cb) {
2015-12-21 17:27:57 +00:00
if (defaults.debug || args.debug) {
console.log('[LE] getConfigs');
}
2015-12-20 10:41:17 +00:00
backend.getConfigsAsync(args).then(function (configs) {
cb(null, configs.map(le.pyToJson));
}, function (err) {
if ('ENOENT' === err.code) {
cb(null, []);
} else {
2016-02-10 20:41:15 +00:00
console.error("[letsencrypt/index.js] getConfigs");
2015-12-20 10:41:17 +00:00
console.error(err.stack);
cb(err);
}
});
}
, setConfig: function (args, cb) {
2015-12-21 17:27:57 +00:00
if (defaults.debug || args.debug) {
console.log('[LE] setConfig');
}
2015-12-20 10:41:17 +00:00
backend.configureAsync(args).then(function (pyobj) {
cb(null, le.pyToJson(pyobj));
});
}
2015-12-12 14:20:12 +00:00
};
2015-12-11 14:22:46 +00:00
2015-12-12 14:20:12 +00:00
return le;
2015-12-11 14:22:46 +00:00
};