greenlock-express.js/lib/standalone.js

237 lines
6.5 KiB
JavaScript
Raw Normal View History

2015-12-16 22:34:01 +00:00
'use strict';
var path = require('path');
2015-12-17 05:08:14 +00:00
var challengeStore = require('./challenge-handlers');
var createSniCallback = require('./sni-callback').create;
2015-12-17 01:20:56 +00:00
var LE = require('letsencrypt');
2015-12-16 22:34:01 +00:00
2015-12-17 01:20:56 +00:00
function LEX(obj, app) {
2015-12-16 22:34:01 +00:00
var https = require('https');
var http = require('http');
var defaultPems = require('localhost.daplie.com-certificates');
if (!obj) {
obj = {};
}
2015-12-17 00:51:37 +00:00
if ('string' === typeof obj) {
obj = {
configDir: obj
};
}
if ('function' === typeof obj) {
2015-12-16 22:34:01 +00:00
obj = {
onRequest: obj
};
}
2015-12-17 05:08:14 +00:00
obj.debug = LEX.debug;
2015-12-17 00:51:37 +00:00
if ('function' === typeof app) {
obj.onRequest = obj.onRequest || app;
}
2015-12-16 22:34:01 +00:00
if (!obj.getChallenge) {
2015-12-16 23:06:00 +00:00
if (false !== obj.getChallenge) {
2015-12-17 01:20:56 +00:00
obj.getChallenge = challengeStore.get;
2015-12-16 23:06:00 +00:00
}
2015-12-16 22:34:01 +00:00
if (!obj.webrootPath) {
obj.webrootPath = path.join(require('os').tmpdir(), 'acme-challenge');
}
}
2015-12-17 00:51:37 +00:00
if (!obj.onRequest && false !== obj.onRequest) {
console.warn("You should either do args.onRequest = app or server.on('request', app),"
+ " otherwise only acme-challenge requests will be handled (and the rest will hang)");
console.warn("You can silence this warning by setting args.onRequest = false");
}
2015-12-17 01:20:56 +00:00
if (!obj.configDir) {
obj.configDir = require('os').homedir() + '/letsencrypt/etc';
}
if (!obj.server) {
obj.server = LEX.defaultServerUrl;
}
2015-12-17 00:51:37 +00:00
if (!obj.letsencrypt) {
//LE.merge(obj, );
2015-12-17 01:20:56 +00:00
// { configDir, webrootPath, server }
2015-12-17 00:51:37 +00:00
obj.letsencrypt = LE.create(obj, {
2015-12-17 01:20:56 +00:00
setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
2015-12-17 00:51:37 +00:00
});
2015-12-16 22:34:01 +00:00
}
function acmeResponder(req, res) {
2015-12-17 05:08:14 +00:00
if (LEX.debug) {
console.log('[LEX] ', req.method, req.headers.host, req.url);
}
2015-12-16 22:34:01 +00:00
var acmeChallengePrefix = '/.well-known/acme-challenge/';
if (0 !== req.url.indexOf(acmeChallengePrefix)) {
obj.onRequest(req, res);
return;
}
var key = req.url.slice(acmeChallengePrefix.length);
obj.getChallenge(obj, req.headers.host, key, function (err, val) {
2015-12-17 05:08:14 +00:00
if (LEX.debug) {
console.log('[LEX] challenge response:', key, err, val);
}
2015-12-16 22:34:01 +00:00
res.end(val || '_');
});
}
2015-12-17 01:20:56 +00:00
var httpsOptions = obj.httpsOptions || {};
var sniCallback = httpsOptions.SNICallback;
2015-12-16 22:34:01 +00:00
// https://nodejs.org/api/https.html
// pfx, key, cert, passphrase, ca, ciphers, rejectUnauthorized, secureProtocol
if (!httpsOptions.pfx) {
if (!(httpsOptions.cert || httpsOptions.key)) {
httpsOptions.key = defaultPems.key;
httpsOptions.cert = defaultPems.cert;
}
else if (!(httpsOptions.cert && httpsOptions.key)) {
if (!httpsOptions.cert) {
console.warn("You specified httpsOptions.cert, but not httpsOptions.key");
}
if (!httpsOptions.key) {
console.warn("You specified httpsOptions.key, but not httpsOptions.cert");
}
}
}
2015-12-17 05:08:14 +00:00
if (!obj.approveRegistration && LEX.defaultApproveRegistration) {
obj.approveRegistration = function (domain, cb) {
if (LEX.debug) {
console.log('[LEX] auto register against staging server');
}
cb(null, {
email: 'example@gmail.com'
, domains: [domain]
, agreeTos: true
, server: LEX.stagingServerUrl
});
};
}
2015-12-17 01:20:56 +00:00
if (obj.sniCallback) {
if (sniCallback) {
console.warn("You specified both args.sniCallback and args.httpsOptions.SNICallback,"
+ " but only args.sniCallback will be used.");
}
httpsOptions.SNICallback = obj.sniCallback;
}
else if (sniCallback) {
2015-12-17 03:03:07 +00:00
obj._sniCallback = createSniCallback(obj);
2015-12-17 01:20:56 +00:00
httpsOptions.SNICallback = function (domain, cb) {
2015-12-17 05:08:14 +00:00
console.log('[LEX] auto register against staging server');
2015-12-17 01:20:56 +00:00
sniCallback(domain, function (err, context) {
if (context) {
cb(err, context);
return;
}
2015-12-17 03:03:07 +00:00
obj._sniCallback(domain, cb);
2015-12-17 01:20:56 +00:00
});
};
}
else {
2015-12-17 03:03:07 +00:00
httpsOptions.SNICallback = createSniCallback(obj);
2015-12-17 01:20:56 +00:00
}
2015-12-16 22:34:01 +00:00
function listen(plainPorts, tlsPorts, onListening) {
var results = {
plainServers: []
, tlsServers: []
};
plainPorts = plainPorts || [80];
tlsPorts = tlsPorts || [443, 5001];
function defaultOnListening() {
/*jshint validthis: true*/
var server = this;
var protocol = ('honorCipherOrder' in server || 'rejectUnauthorized' in server) ? 'https': 'http';
var addr = server.address();
var port;
if (80 === addr.port || 443 === addr.port) {
port = '';
} else {
port = ':' + addr.port;
}
console.info('Listening ' + protocol + '://' + addr.address + port + '/');
}
plainPorts.forEach(function (addr) {
var port = addr.port || addr;
var address = addr.address || '';
var server = http.createServer(acmeResponder);
server.__le_onListening = addr.onListen;
server.__le_port = port;
server.__le_address = address;
results.plainServers.push(server);
});
tlsPorts.forEach(function (addr) {
var port = addr.port || addr;
var address = addr.address || '';
var options = addr.httpsOptions || httpsOptions;
var server = https.createServer(options, acmeResponder);
server.__le_onListen = addr.onListen;
server.__le_port = port;
server.__le_address = address;
results.tlsServers.push(server);
});
results.plainServers.forEach(function (server) {
var listen = server.__le_onListening || onListening || defaultOnListening;
server.listen(server.__le_port, server.__le_address, listen);
});
results.tlsServers.forEach(function (server) {
var listen = server.__le_onListening || onListening || defaultOnListening;
server.listen(server.__le_port, server.__le_address, listen);
});
// deleting creates a "slow object", but that's okay (we only use it once)
return results;
2015-12-17 00:51:37 +00:00
}
2015-12-16 22:34:01 +00:00
return {
listen: listen
};
}
2015-12-17 01:20:56 +00:00
module.exports = LEX;
2015-12-17 03:03:07 +00:00
2015-12-17 01:20:56 +00:00
LEX.create = LEX;
LEX.setChallenge = challengeStore.set;
LEX.getChallenge = challengeStore.get;
LEX.removeChallenge = challengeStore.remove;
2015-12-17 03:15:20 +00:00
LEX.createSniCallback = createSniCallback;
2015-12-17 01:20:56 +00:00
LEX.stagingServerUrl = LE.stagingServerUrl;
LEX.productionServerUrl = LE.productionServerUrl || LE.liveServerUrl;
LEX.defaultServerUrl = LEX.productionServerUrl;
LEX.testing = function () {
2015-12-17 05:08:14 +00:00
LEX.debug = true;
2015-12-17 01:20:56 +00:00
LEX.defaultServerUrl = LEX.stagingServerUrl;
2015-12-17 05:08:14 +00:00
LEX.defaultApproveRegistration = true;
console.log('[LEX] testing mode turned on');
console.log('[LEX] default server: ' + LEX.defaultServerUrl);
console.log('[LEX] automatic registration handling turned on for testing.');
return module.exports;
2015-12-17 01:20:56 +00:00
};