yunkong2.js-controller/lib/letsencrypt.js
2018-09-17 20:32:19 +08:00

182 lines
8.9 KiB
JavaScript

'use strict';
function createServer(app, settings, certificates, leSettings, log) {
let server;
const leLog = function (debug, arg1, arg2, arg3) {
if (debug) {
const args = Array.prototype.slice.call(arguments);
args.shift(); // remove debug argument
// skip "no match"
if (args[0][0] === 'n' && args[0][1] === 'o') return;
log.info(arg1 + (arg2 || '') + (arg3 || ''));
}
};
if (settings.secure) {
if (leSettings && (!leSettings.email || !leSettings.email) && settings.leEnabled) {
log.error('Please specify the email address and domains to use Let\'s Encrypt certificates!');
}
if (leSettings && leSettings.email && leSettings.domains && settings.leEnabled) {
const tools = require(__dirname + '/tools');
const tls = require('tls');
const fs = require('fs');
const LE = require('greenlock');
let leDir;
let configPath = tools.getConfigFileName().replace(/\\/g, '/');
let parts = configPath.split('/');
parts.pop();
configPath = parts.join('/');
if (leSettings.path && (leSettings.path[0] === '/' || leSettings.path.match(/^[A-Za-z]:/))) {
leDir = leSettings.path;
} else {
leDir = configPath + '/' + (leSettings.path || 'letsencrypt');
}
// for lex outputs
if (!console.debug) console.debug = console.log;
if (!fs.existsSync(leDir)) fs.mkdirSync(leDir);
// prepare domains
if (typeof leSettings.domains === 'string') {
leSettings.domains = leSettings.domains.split(',');
for (let d = leSettings.domains.length - 1; d >= 0; d--) {
leSettings.domains[d] = leSettings.domains[d].trim();
if (!leSettings.domains[d]) leSettings.domainss.splice(d, 1);
}
}
let lex = LE.create({
debug: true,
configDir: leDir,
agreeTos: true,
store: require(__dirname + '/letsencryptStore.js').create({debug: true, log: leLog}),
server: 'https://acme-v01.api.letsencrypt.org/directory', //'staging',
email: leSettings.email,
approvedDomains: leSettings.domains,
log: leLog
});
if (settings.leUpdate) {
settings.lePort = parseInt(settings.lePort, 10) || 80;
// handles acme-challenge and redirects to https
// used for validation of requests like http://example.com/.well-known/acme-challenge/BLABALBAL
require('http').createServer(lex.middleware(function redirectHttps(req, res) {
res.setHeader('Location', 'https://' + req.headers.host + req.url);
res.statusCode = 302;
res.end('<!-- Hello Developer Person! Please use HTTPS instead -->');
})).listen(settings.lePort, function () {
log.info('LetsEncrypt challenge server is started on ' + settings.lePort);
});
}
let options = JSON.parse(JSON.stringify(certificates));
let defaultTls = tls.createSecureContext(certificates);
let hostTls;
let running;
options.SNICallback = function (hostname, cb) {
if (leSettings.domains.indexOf(hostname) !== -1) {
if (settings.leUpdate) {
if (running === true) {
cb(null, hostTls || defaultTls);
} else
if (running) {
running.push(cb);
} else {
running = [cb];
return lex.httpsOptions.SNICallback(hostname, function (err, tls) {
if (tls) log.debug('Got valid certificates from letsencrypt');
if (err) log.error('Cannot get certificates: ' + err);
lex.debug = false;
hostTls = tls;
for (let r = 0; r < running.length; r++) {
running[r](err, tls || defaultTls);
}
running = true;
});
}
} else {
if (!hostTls) {
// validate certificates
lex.check({domains: leSettings.domains}).then(function (certInfo) {
if (certInfo) {
hostTls = tls.createSecureContext({
key: certInfo.privkey || certInfo.key, // privkey.pem
cert: certInfo.fullchain || certInfo.cert, // fullchain.pem (cert.pem + '\n' + chain.pem)
ca: certInfo.ca
});
cb(null, hostTls);
} else {
log.error('No letsencrypt certificates found in "' + leDir + '"');
cb(null, defaultTls);
// do not register domain
/*
if (!running) {
running = [cb];
lex.letsencrypt.register({
domains: leSettings.domains,
email: leSettings.email,
agreeTos: true
}, function (err, certInfo) {
//log.debug("[LEX] '" + hostname + "' register completed", err && err.stack || null, certInfo);
if ((!err || !err.stack) && !certInfo) {
log.error((new Error('[LEX] SANITY FAIL: no error and yet no certs either')).stack);
}
if (certInfo) {
hostTls = tls.createSecureContext({
key: certInfo.privkey || certInfo.key, // privkey.pem
cert: certInfo.fullchain || certInfo.cert, // fullchain.pem (cert.pem + '\n' + chain.pem)
ca: certInfo.ca
});
for (var r = 0; r < running.length; r++) {
running[r](null, hostTls);
}
} else {
for (var r = 0; r < running.length; r++) {
running[r](null, defaultTls);
}
}
running = true;
});
} else {
if (running === true) {
cb(null, defaultTls);
} else {
running.push(cb);
}
}
*/
}
},
function (err) {
if (err) log.error(err);
cb(null, defaultTls);
});
} else {
cb(null, hostTls);
}
}
} else {
cb(null, defaultTls);
}
};
server = require('https').createServer(options, lex.middleware(app));
} else {
server = require('https').createServer(certificates, app);
}
} else {
server = require('http').createServer(app);
}
return server;
}
exports.createServer = createServer;