removed letsencrypt and other

This commit is contained in:
AJ ONeal 2017-05-04 23:09:56 -06:00
parent 7525b7d0a7
commit 54fe53dbfb
9 changed files with 116 additions and 607 deletions

View File

@ -1,59 +0,0 @@
'use strict';
// Note the odd use of callbacks (instead of promises) here
// It's to avoid loading bluebird yet (see sni-server.js for explanation)
module.exports.create = function (lex, certPaths, port, conf, serverCallback) {
function initServer(err, server) {
var app;
var promiseApp;
if (err) {
serverCallback(err);
return;
}
server.on('error', serverCallback);
server.listen(port, function () {
// is it even theoritically possible for
// a request to come in before this callback has fired?
// I'm assuming this event must fire before any request event
promiseApp = serverCallback(null, server);
});
/*
server.listen(port, '::::', function () {
// is it even theoritically possible for
// a request to come in before this callback has fired?
// I'm assuming this event must fire before any request event
promiseApp = serverCallback(null, server);
});
*/
// Get up and listening as absolutely quickly as possible
function onRequest(req, res) {
// this is a hot piece of code, so we cache the result
if (app) {
app(req, res);
return;
}
promiseApp.then(function (_app) {
console.log('[Server]', req.method, req.host || req.headers['x-forwarded-host'] || req.headers.host, req.url);
app = _app;
app(req, res);
});
}
if (lex) {
var LEX = require('letsencrypt-express');
server.on('request', LEX.createAcmeResponder(lex, onRequest));
} else {
server.on('request', onRequest);
}
}
if (certPaths) {
require('../lib/sni-server').create(lex, certPaths, initServer);
} else {
initServer(null, require('http').createServer());
}
};

View File

@ -29,39 +29,17 @@ var walnut = tryConf(
path.join('..', '..', 'config.walnut')
, { externalPort: 443
, externalInsecurePort: 80
, certspath: path.join(__dirname, '..', '..', 'certs', 'live')
}
);
var caddy = tryConf(
path.join('..', '..', 'config.caddy')
, { conf: path.join(__dirname, '..', '..', 'Caddyfile')
, bin: null // '/usr/local/bin/caddy'
, sitespath: null // path.join(__dirname, 'sites-enabled')
, locked: false // true
}
);
var letsencrypt = tryConf(
path.join('..', '..', 'config.letsencrypt')
, { configDir: path.join(__dirname, '..', '..', 'letsencrypt')
, email: null
, agreeTos: false
}
);
var useCaddy = caddy.bin && require('fs').existsSync(caddy.bin);
var info = {
type: 'walnut.init'
, conf: {
protocol: useCaddy ? 'http' : 'https'
, externalPort: walnut.externalPort
, externalPortInsecure: walnut.externalInsecurePort // TODO externalInsecurePort
, localPort: walnut.localPort || (useCaddy ? 4080 : 443) // system / local network
, insecurePort: walnut.insecurePort || (useCaddy ? 80 : 80) // meh
, certPaths: useCaddy ? null : [
walnut.certspath
, path.join(letsencrypt.configDir, 'live')
]
, trustProxy: useCaddy ? true : false
, lexConf: letsencrypt
protocol: 'http'
, externalPort: walnut.externalPort || 443
, externalPortInsecure: walnut.externalInsecurePort || 80 // TODO externalInsecurePort
, localPort: walnut.localPort || 4080 // system / local network
, trustProxy: true
, varpath: path.join(__dirname, '..', '..', 'var')
, etcpath: path.join(__dirname, '..', '..', 'etc')
}
@ -79,11 +57,6 @@ cluster.on('online', function (worker) {
if (state.firstRun) {
state.firstRun = false;
if (useCaddy) {
caddy = require('../lib/spawn-caddy').create(caddy);
// relies on { localPort, locked }
caddy.spawn(caddy);
}
// TODO dyndns in master?
}
@ -94,7 +67,6 @@ cluster.on('online', function (worker) {
return;
}
state.caddy = caddy;
state.workers = workers;
// calls init if init has not been called
require('../lib/master').touch(info.conf, state).then(function (newConf) {

View File

@ -1,168 +1,15 @@
'use strict';
module.exports.create = function (opts) {
module.exports.create = function () {
var id = '0';
var promiseApp;
function createAndBindInsecure(lex, conf, getOrCreateHttpApp) {
// TODO conditional if 80 is being served by caddy
var appPromise = null;
var app = null;
var http = require('http');
var insecureServer = http.createServer();
function onRequest(req, res) {
if (app) {
app(req, res);
return;
}
if (!appPromise) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end('{ "error": { "code": "E_SANITY_FAIL", "message": "should have an express app, but didn\'t" } }');
return;
}
appPromise.then(function (_app) {
appPromise = null;
app = _app;
app(req, res);
});
}
insecureServer.listen(conf.insecurePort, function () {
console.info("#" + id + " Listening on http://"
+ insecureServer.address().address + ":" + insecureServer.address().port, '\n');
appPromise = getOrCreateHttpApp(null, insecureServer);
if (!appPromise) {
throw new Error('appPromise returned nothing');
}
});
insecureServer.on('request', onRequest);
}
function walkLe(domainname) {
var PromiseA = require('bluebird');
if (!domainname) {
return PromiseA.reject(new Error('no domainname given for walkLe'));
}
var fs = PromiseA.promisifyAll(require('fs'));
var path = require('path');
var parts = domainname.split('.'); //.replace(/^www\./, '').split('.');
var configname = parts.join('.') + '.json';
var configpath = path.join(__dirname, '..', '..', 'config', configname);
if (parts.length < 2) {
return PromiseA.resolve(null);
}
// TODO configpath a la varpath
return fs.readFileAsync(configpath, 'utf8').then(function (text) {
var data = JSON.parse(text);
data.name = configname;
return data;
}, function (/*err*/) {
parts.shift();
return walkLe(parts.join('.'));
});
}
function createLe(lexConf, conf) {
var LEX = require('letsencrypt-express');
var lex = LEX.create({
configDir: lexConf.configDir // i.e. __dirname + '/letsencrypt.config'
, approveRegistration: function (hostname, cb) {
// TODO cache/report unauthorized
if (!hostname) {
cb(new Error("[lex.approveRegistration] undefined hostname"), null);
return;
}
walkLe(hostname).then(function (leAuth) {
// TODO should still check dns for hostname (and mx for email)
if (leAuth && leAuth.email && leAuth.agreeTos) {
cb(null, {
domains: [hostname] // TODO handle www and bare on the same cert
, email: leAuth.email
, agreeTos: leAuth.agreeTos
});
}
else {
// TODO report unauthorized
cb(new Error("Valid LetsEncrypt config with email and agreeTos not found for '" + hostname + "'"), null);
}
});
/*
letsencrypt.getConfig({ domains: [domain] }, function (err, config) {
if (!(config && config.checkpoints >= 0)) {
cb(err, null);
return;
}
cb(null, {
email: config.email
// can't remember which it is, but the pyconf is different that the regular variable
, agreeTos: config.tos || config.agree || config.agreeTos
, server: config.server || LE.productionServerUrl
, domains: config.domains || [domain]
});
});
*/
}
});
conf.letsencrypt = lex.letsencrypt;
conf.lex = lex;
conf.walkLe = walkLe;
return lex;
}
function createAndBindServers(conf, getOrCreateHttpApp) {
var lex;
if (conf.lexConf) {
lex = createLe(conf.lexConf, conf);
}
// NOTE that message.conf[x] will be overwritten when the next message comes in
require('./local-server').create(lex, conf.certPaths, conf.localPort, conf, function (err, webserver) {
if (err) {
console.error('[ERROR] worker.js');
console.error(err.stack);
throw err;
}
console.info("#" + id + " Listening on " + conf.protocol + "://" + webserver.address().address + ":" + webserver.address().port, '\n');
// we don't need time to pass, just to be able to return
process.nextTick(function () {
createAndBindInsecure(lex, conf, getOrCreateHttpApp);
});
// we are returning the promise result to the caller
return getOrCreateHttpApp(null, null, webserver, conf);
});
}
//
// Worker Mode
//
function waitForConfig(realMessage) {
if ('walnut.init' !== realMessage.type) {
console.warn('[Worker] 0 got unexpected message:');
console.warn(realMessage);
return;
}
var conf = realMessage.conf;
process.removeListener('message', waitForConfig);
function createAndBind(conf) {
// NOTE: this callback must return a promise for an express app
function getExpressApp(err, insecserver, webserver/*, newMessage*/) {
function getOrCreateHttpApp(err, insecserver, webserver/*, newMessage*/) {
var PromiseA = require('bluebird');
if (promiseApp) {
@ -198,41 +45,93 @@ module.exports.create = function (opts) {
return promiseApp;
}
createAndBindServers(conf, getExpressApp);
}
//
// Standalone Mode
//
if (opts) {
// NOTE: this callback must return a promise for an express app
createAndBindServers(opts, function (err, insecserver, webserver/*, conf*/) {
var PromiseA = require('bluebird');
if (promiseApp) {
return promiseApp;
function serverCallback(err, webserver) {
if (err) {
console.error('[ERROR] worker.js');
console.error(err.stack);
throw err;
}
promiseApp = new PromiseA(function (resolve) {
opts.getConfig(function (srvmsg) {
resolve(require('../lib/worker').create(webserver, srvmsg));
});
}).then(function (app) {
console.info('[Standalone Ready]');
return app;
});
console.info("#" + id + " Listening on " + conf.protocol + "://" + webserver.address().address + ":" + webserver.address().port, '\n');
return promiseApp;
});
} else {
// we are in cluster mode, as opposed to standalone mode
id = require('cluster').worker.id.toString();
// We have to wait to get the configuration from the master process
// before we can start our webserver
console.info('[Worker #' + id + '] online!');
process.on('message', waitForConfig);
// we are returning the promise result to the caller
return getOrCreateHttpApp(null, null, webserver, conf);
}
// Note the odd use of callbacks (instead of promises) here
// It's to avoid loading bluebird yet (see sni-server.js for explanation)
function localServerCreate(port) {
function initServer(err, server) {
var app;
var promiseApp;
if (err) {
serverCallback(err);
return;
}
server.on('error', serverCallback);
server.listen(port, function () {
// is it even theoritically possible for
// a request to come in before this callback has fired?
// I'm assuming this event must fire before any request event
promiseApp = serverCallback(null, server);
});
/*
server.listen(port, '::::', function () {
// is it even theoritically possible for
// a request to come in before this callback has fired?
// I'm assuming this event must fire before any request event
promiseApp = serverCallback(null, server);
});
*/
// Get up and listening as absolutely quickly as possible
function onRequest(req, res) {
// this is a hot piece of code, so we cache the result
if (app) {
app(req, res);
return;
}
promiseApp.then(function (_app) {
console.log('[Server]', req.method, req.host || req.headers['x-forwarded-host'] || req.headers.host, req.url);
app = _app;
app(req, res);
});
}
server.on('request', onRequest);
}
initServer(null, require('http').createServer());
}
// NOTE that message.conf[x] will be overwritten when the next message comes in
localServerCreate(conf.localPort);
}
function waitForConfig(realMessage) {
console.log('realMessage', realMessage);
if ('walnut.init' !== realMessage.type) {
console.warn('[Worker] 0 got unexpected message:');
console.warn(realMessage);
return;
}
var conf = realMessage.conf;
process.removeListener('message', waitForConfig);
createAndBind(conf);
}
// we are in cluster mode, as opposed to standalone mode
id = require('cluster').worker.id.toString();
// We have to wait to get the configuration from the master process
// before we can start our webserver
console.info('[Worker #' + id + '] online!');
process.on('message', waitForConfig);
//
// Debugging
//

View File

@ -13,54 +13,8 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
var CORS;
var cors;
function redirectHttpsHelper(req, res) {
var host = req.hostname || req.headers.host || '';
var url = req.url;
// 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?
var escapeHtml = require('escape-html');
var newLocation = 'https://'
+ host.replace(/:\d+/, ':' + xconfx.externalPort) + url
;
var safeLocation = escapeHtml(newLocation);
var metaRedirect = ''
+ '<html>\n'
+ '<head>\n'
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
+ ' <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n'
+ '</head>\n'
+ '<body style="display: none;">\n'
+ ' <p>You requested an insecure resource. Please use this instead: \n'
+ ' <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n'
+ '</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.
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(metaRedirect);
}
function redirectSetup(reason, req, res/*, next*/) {
console.log('xconfx', xconfx);
var url = 'https://cloud.' + xconfx.primaryDomain;
if (443 !== xconfx.externalPort) {
@ -74,48 +28,6 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
res.end();
}
function redirectHttps(req, res) {
if (localCache.le[req.hostname]) {
if (localCache.le[req.hostname].conf) {
redirectHttpsHelper(req, res);
return;
}
else {
// TODO needs IPC to expire cache
redirectSetup(req.hostname, req, res);
return;
/*
if (Date.now() - localCache.le[req.hostname].createdAt < (5 * 60 * 1000)) {
// TODO link to dbconf.primaryDomain
res.send({ error: { message: "Security Error: Encryption for '" + req.hostname + "' has not been configured."
+ " Please use the management interface to set up ACME / Let's Encrypt (or another solution)." } });
return;
}
*/
}
}
return xconfx.walkLe(req.hostname).then(function (leAuth) {
if (!leAuth) {
redirectSetup(req.hostname, req, res);
return;
}
localCache.le[req.hostname] = { conf: leAuth, createdAt: Date.now() };
redirectHttps(req, res);
}, function (err) {
console.error('[Error] lib/main.js walkLe');
if (err.stack) {
console.error(err.stack);
}
else {
console.error(new Error('getstack').stack);
console.error(err);
}
res.send({ error: { message: "failed to get tls certificate for '" + (req.hostname || '') + "'" } });
});
}
function disallowSymLinks(req, res) {
res.end(
"Symbolic Links are not supported on all platforms and are therefore disallowed."
@ -245,15 +157,6 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
var appIdParts = appId.split('#');
var appIdPart;
if (!req.secure) {
// did not come from https
if (/\.(appcache|manifest)\b/.test(req.url)) {
require('./unbrick-appcache').unbrick(req, res);
return;
}
return redirectHttps(req, res);
}
// TODO configuration for allowing www
if (/^www\./.test(req.hostname)) {
// NOTE: acme responder and appcache unbricker must come before scrubTheDub

View File

@ -2,17 +2,27 @@
var cluster = require('cluster');
var PromiseA = require('bluebird');
var path = require('path');
var os = require('os');
function init(conf, state) {
var newConf = {};
function rand(n) {
var HEX = 16;
var BASE_36 = 36;
var rnd = require('crypto').randomBytes(n || 16).toString('hex');
return parseInt(rnd, HEX).toString(BASE_36);
}
if (!conf.ipcKey) {
conf.ipcKey = newConf.ipcKey = require('crypto').randomBytes(16).toString('base64');
conf.ipcKey = newConf.ipcKey = rand(16);
}
if (!conf.sqlite3Sock) {
conf.sqlite3Sock = newConf.sqlite3Sock = '/tmp/sqlite3.' + require('crypto').randomBytes(4).toString('hex') + '.sock';
conf.sqlite3Sock = newConf.sqlite3Sock = path.join(os.tmpdir(), 'sqlite3.' + rand(8) + '.sock');
}
if (!conf.memstoreSock) {
conf.memstoreSock = newConf.memstoreSock = '/tmp/memstore.' + require('crypto').randomBytes(4).toString('hex') + '.sock';
conf.memstoreSock = newConf.memstoreSock = path.join(os.tmpdir(), 'memstore.' + rand(8) + '.sock');
}
try {

View File

@ -1,58 +0,0 @@
'use strict';
// Note the odd use of callbacks here.
// We're targetting low-power platforms and so we're trying to
// require everything as lazily as possible until our server
// is actually listening on the socket. Bluebird is heavy.
// Even the built-in modules can take dozens of milliseconds to require
module.exports.create = function (lex, certPaths, serverCallback) {
// Recognize that this secureContexts cache is local to this CPU core
var secureContexts = {};
var ciphers = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS:!AES256';
function createSecureServer() {
var domainname = 'www.example.com';
var fs = require('fs');
var secureOpts = {
// TODO create backup file just in case this one is ever corrupted
// NOTE synchronous is faster in this case of initialization
// NOTE certsPath[0] must be the default (LE) directory (another may be used for OV and EV certs)
key: fs.readFileSync(certPaths[0] + '/' + domainname + '/privkey.pem', 'ascii')
, cert: fs.readFileSync(certPaths[0] + '/' + domainname + '/fullchain.pem', 'ascii')
// https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
// https://nodejs.org/api/tls.html
// removed :ECDH+AES256:DH+AES256 and added :!AES256 because AES-256 wastes CPU
, ciphers: ciphers
, honorCipherOrder: true
};
secureContexts['www.example.com'] = require('tls').createSecureContext(secureOpts);
secureContexts['example.com'] = secureContexts['www.example.com'];
//SNICallback is passed the domain name, see NodeJS docs on TLS
secureOpts.SNICallback = function (domainname, cb) {
// NOTE: '*.proxyable.*' domains will be truncated
require('./load-certs').load(secureContexts, certPaths, domainname).then(function (context) {
cb(null, context);
}, function (err) {
console.error('[SNI Callback]');
console.error(err.stack);
cb(err);
});
};
serverCallback(null, require('https').createServer(secureOpts));
}
function createLeServer() {
lex.httpsOptions.ciphers = ciphers;
lex.httpsOptions.honorCipherOrder = true;
serverCallback(null, require('https').createServer(lex.httpsOptions));
}
if (lex) {
createLeServer();
} else {
createSecureServer();
}
};

View File

@ -1,159 +0,0 @@
'use strict';
function tplCaddyfile(caddyConf) {
var contents = [];
caddyConf.domains.forEach(function (hostname) {
var content = "";
var pagesname = hostname;
// TODO prefix
content += "https://" + hostname + " {\n"
+ " gzip\n"
+ " tls "
+ "/srv/walnut/certs/live/" + hostname + "/fullchain.pem "
+ "/srv/walnut/certs/live/" + hostname + "/privkey.pem\n"
;
if (caddyConf.locked) {
content += " root /srv/walnut/init.public/\n";
} else {
content += " root " + caddyConf.sitespath + "/" + pagesname + "/\n";
}
content +=
" proxy /api http://localhost:" + caddyConf.localPort.toString() + " {\n"
+ " proxy_header Host {host}\n"
+ " proxy_header X-Forwarded-Host {host}\n"
+ " proxy_header X-Forwarded-Proto {scheme}\n"
// # TODO internal
+ " }\n"
+ "}";
contents.push(content);
});
return contents.join('\n\n');
}
module.exports.tplCaddyfile = tplCaddyfile;
module.exports.create = function (caddyConf) {
var spawn = require('child_process').spawn;
var caddyBin = caddyConf.bin;
var caddyfilePath = caddyConf.conf;
// TODO put up a booting / lock screen on boot
// and wait for all to be grabbed from db
// NOTE caddy cannot yet support multiple roots
// (needed for example.com/appname instead of appname.example.com)
var caddy;
var fs = require('fs');
// TODO this should be expanded to include proxies a la proxydyn
function writeCaddyfile(caddyConf, cb) {
fs.readdir(caddyConf.sitespath, function (err, nodes) {
if (err) {
if (cb) {
cb(err);
return;
}
console.error('[writeCaddyFile] 0');
console.error(err.stack);
throw err;
}
caddyConf.domains = nodes.filter(function (node) {
return /\./.test(node) && !/(^\.)|([\/\:\\])/.test(node);
});
var contents = tplCaddyfile(caddyConf);
fs.writeFile(caddyfilePath, contents, 'utf8', function (err) {
if (err) {
if (cb) {
cb(err);
return;
}
console.error('[writeCaddyFile] 1');
console.error(err.stack);
throw err;
}
if (cb) { cb(null); }
});
});
}
function spawnCaddy(caddyConf, cb) {
console.log('[CADDY] start');
writeCaddyfile(caddyfilePath, function (err) {
if (err) {
console.error('[writeCaddyfile]');
console.error(err.stack);
throw err;
}
if (caddy) {
caddy.kill('SIGUSR1');
return caddy;
// TODO caddy.kill('SIGKILL'); if SIGTERM fails
// https://github.com/mholt/caddy/issues/107
// SIGUSR1
//caddy.kill('SIGTERM');
}
try {
require('child_process').execSync('killall caddy');
} catch(e) {
// ignore
// Command failed: killall caddy
// caddy: no process found
}
caddy = spawn(caddyBin, ['-conf', caddyfilePath], { stdio: ['ignore', 'pipe', 'pipe'] });
caddy.stdout.on('data', function (str) {
console.error('[Caddy]', str.toString('utf8'));
});
caddy.stderr.on('data', function (errstr) {
console.error('[Caddy]', errstr.toString('utf8'));
});
caddy.on('close', function (code, signal) {
// TODO catch if caddy doesn't exist
console.log('[Caddy]');
console.log(code, signal);
caddy = null;
setTimeout(function () {
spawnCaddy(caddyConf);
}, 1 * 1000);
});
try {
if ('function' === typeof cb) { cb(null, caddy); }
} catch(e) {
console.error('ERROR: [spawn-caddy.js]');
console.error(e.stack);
}
});
}
function sighup() {
if (caddy) {
caddy.kill('SIGUSR1');
return;
}
// sudo kill -s SIGUSR1 `cat caddy.pid`
fs.readFileAsync('/srv/walnut/caddy.pid', 'utf8').then(function (pid) {
console.log('[caddy] pid', pid);
caddy = spawn('/bin/kill', ['-s', 'SIGUSR1', pid]);
});
}
return {
spawn: spawnCaddy
, update: function (caddyConf) {
return writeCaddyfile(caddyConf, sighup);
}
, sighup: sighup
};
};

View File

@ -70,11 +70,13 @@ module.exports.create = function (webserver, xconfx, state) {
, indices: [ 'createdAt', 'updatedAt' ]
}
];
console.log('config directive', dir);
function scopeMemstore(expId) {
var scope = expId + '|';
return {
getAsync: function (id) {
id = id.replace(/\|/, );
return memstore.getAsync(scope + id);
}
, setAsync: function (id, data) {
@ -194,20 +196,15 @@ module.exports.create = function (webserver, xconfx, state) {
}));
app.use('/api', recase);
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
app.use('/', function (req, res) {
if (!req.secure) {
if (!(req.encrypted || req.secure)) {
// did not come from https
if (/\.(appcache|manifest)\b/.test(req.url)) {
require('./unbrick-appcache').unbrick(req, res);
return;
}
}
if (xconfx.lex && /\.well-known\/acme-challenge\//.test(req.url)) {
var LEX = require('letsencrypt-express');
xconfx.lex.debug = true;
xconfx.acmeResponder = xconfx.acmeResponder || LEX.createAcmeResponder(xconfx.lex/*, next*/);
xconfx.acmeResponder(req, res);
res.end("Connection is not encrypted. That's no bueno or, as we say in Hungarian, nem szabad!");
return;
}

View File

@ -4,6 +4,9 @@ var cluster = require('cluster');
var crypto;
var stacks = {};
function realRandom() {
return parseFloat(('0.' + (parseInt(crypto.randomBytes(8).toString('hex'), 16))).replace(/(^0)|(0$)/g, ''));
}
Math.random = function () {
var err = new Error("Math.random() was used");
@ -16,7 +19,8 @@ Math.random = function () {
crypto = require('crypto');
}
return parseFloat(('0.' + (parseInt(crypto.randomBytes(8).toString('hex'), 16))).replace(/(^0)|(0$)/g, ''));
Math.random = realRandom;
return realRandom();
};
if (cluster.isMaster) {
@ -26,5 +30,5 @@ if (cluster.isMaster) {
alternately we could use this and then check require.main
cluster.setupMaster({ exec : "app.js", });
*/
require('./boot/worker').create(null);
require('./boot/worker').create(null, null);
}