'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(''); })).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;