2015-02-19 06:06:56 +00:00
|
|
|
'use strict';
|
|
|
|
|
2016-03-29 19:03:09 +00:00
|
|
|
module.exports.create = function (lex, securePort, insecurePort, info, serverCallback) {
|
2015-03-06 03:04:51 +00:00
|
|
|
var PromiseA = require('bluebird').Promise;
|
2015-11-21 12:36:22 +00:00
|
|
|
var appPromise;
|
|
|
|
//var app;
|
2015-03-06 03:04:51 +00:00
|
|
|
var http = require('http');
|
2015-11-21 12:36:22 +00:00
|
|
|
var redirectives;
|
|
|
|
|
|
|
|
function useAppInsecurely(req, res) {
|
|
|
|
if (!appPromise) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
appPromise.then(function (app) {
|
|
|
|
req._WALNUT_SECURITY_EXCEPTION = true;
|
|
|
|
app(req, res);
|
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-03-06 03:04:51 +00:00
|
|
|
|
2015-02-19 06:06:56 +00:00
|
|
|
function redirectHttps(req, res) {
|
2015-11-21 12:36:22 +00:00
|
|
|
// Let it do this once they visit the https site
|
|
|
|
// res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload');
|
2015-09-25 08:06:47 +00:00
|
|
|
|
2015-02-19 06:06:56 +00:00
|
|
|
var host = req.headers.host || '';
|
|
|
|
var url = req.url;
|
|
|
|
|
2015-11-21 12:36:22 +00:00
|
|
|
// TODO
|
|
|
|
// XXX NOTE: info.conf.redirects may or may not be loaded at first
|
|
|
|
// the object will be modified when the config is loaded
|
|
|
|
if (!redirectives && info.conf.redirects) {
|
|
|
|
redirectives = require('./hostname-redirects').compile(info.conf.redirects);
|
|
|
|
}
|
|
|
|
if (require('./no-www').scrubTheDub(req, res, redirectives)) {
|
|
|
|
return true;
|
2015-11-14 04:25:12 +00:00
|
|
|
}
|
|
|
|
|
2015-11-21 12:36:22 +00:00
|
|
|
// TODO
|
|
|
|
// allow exceptions for the case of arduino and whatnot that cannot handle https?
|
|
|
|
// http://evothings.com/is-it-possible-to-secure-micro-controllers-used-within-iot/
|
|
|
|
// needs ECDSA?
|
|
|
|
|
2015-11-23 09:31:54 +00:00
|
|
|
if (/redirect-www.org$/.test(host) && useAppInsecurely(req, res)) {
|
2015-11-21 12:36:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2015-02-19 06:06:56 +00:00
|
|
|
|
2015-07-14 21:44:02 +00:00
|
|
|
var escapeHtml = require('escape-html');
|
2015-02-19 06:06:56 +00:00
|
|
|
var newLocation = 'https://'
|
|
|
|
+ host.replace(/:\d+/, ':' + securePort) + url
|
|
|
|
;
|
2015-07-09 03:20:57 +00:00
|
|
|
var safeLocation = escapeHtml(newLocation);
|
2015-02-19 06:06:56 +00:00
|
|
|
|
|
|
|
var metaRedirect = ''
|
|
|
|
+ '<html>\n'
|
|
|
|
+ '<head>\n'
|
|
|
|
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
|
2015-07-09 03:20:57 +00:00
|
|
|
+ ' <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n'
|
2015-02-19 06:06:56 +00:00
|
|
|
+ '</head>\n'
|
|
|
|
+ '<body style="display: none;">\n'
|
|
|
|
+ ' <p>You requested an insecure resource. Please use this instead: \n'
|
2015-07-09 03:20:57 +00:00
|
|
|
+ ' <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n'
|
2015-02-19 06:06:56 +00:00
|
|
|
+ '</body>\n'
|
|
|
|
+ '</html>\n'
|
|
|
|
;
|
|
|
|
|
|
|
|
// DO NOT HTTP REDIRECT
|
|
|
|
/*
|
|
|
|
res.setHeader('Location', newLocation);
|
|
|
|
res.statusCode = 302;
|
|
|
|
*/
|
|
|
|
|
|
|
|
// BAD NEWS BEARS
|
|
|
|
//
|
|
|
|
// When people are experimenting with the API and posting tutorials
|
|
|
|
// they'll use cURL and they'll forget to prefix with https://
|
|
|
|
// If we allow that, then many users will be sending private tokens
|
|
|
|
// and such with POSTs in clear text and, worse, it will work!
|
|
|
|
// To minimize this, we give browser users a mostly optimal experience,
|
|
|
|
// but people experimenting with the API get a message letting them know
|
|
|
|
// that they're doing it wrong and thus forces them to ensure they encrypt.
|
2015-07-09 03:20:57 +00:00
|
|
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
2015-02-19 06:06:56 +00:00
|
|
|
res.end(metaRedirect);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO localhost-only server shutdown mechanism
|
|
|
|
// that closes all sockets, waits for them to finish,
|
|
|
|
// and then hands control over completely to respawned server
|
|
|
|
|
|
|
|
//
|
2015-02-20 22:34:17 +00:00
|
|
|
// Redirect HTTP to HTTPS
|
2015-02-19 06:06:56 +00:00
|
|
|
//
|
|
|
|
// This simply redirects from the current insecure location to the encrypted location
|
|
|
|
//
|
2015-02-20 22:34:17 +00:00
|
|
|
var insecureServer;
|
2015-02-19 06:06:56 +00:00
|
|
|
insecureServer = http.createServer();
|
2015-02-20 22:34:17 +00:00
|
|
|
insecureServer.listen(insecurePort, function () {
|
2015-11-21 12:36:22 +00:00
|
|
|
console.log("\nListening on http://localhost:" + insecureServer.address().port);
|
|
|
|
console.log("(handling any explicit redirects and redirecting all other traffic to https)\n");
|
|
|
|
if (serverCallback) {
|
|
|
|
appPromise = serverCallback(null, insecureServer);
|
|
|
|
}
|
2015-02-19 06:06:56 +00:00
|
|
|
});
|
2016-03-29 19:03:09 +00:00
|
|
|
|
|
|
|
if (lex) {
|
|
|
|
var LEX = require('letsencrypt-express');
|
|
|
|
insecureServer.on('request', LEX.createAcmeResponder(lex, redirectHttps));
|
|
|
|
} else {
|
|
|
|
insecureServer.on('request', redirectHttps);
|
|
|
|
}
|
2015-03-06 03:04:51 +00:00
|
|
|
|
|
|
|
return PromiseA.resolve(insecureServer);
|
2015-02-19 06:06:56 +00:00
|
|
|
};
|