minor refactor
This commit is contained in:
		
							parent
							
								
									c2fc1e76e1
								
							
						
					
					
						commit
						3a86624b8b
					
				
							
								
								
									
										145
									
								
								boot/master.js
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								boot/master.js
									
									
									
									
									
								
							@ -14,12 +14,23 @@ var cluster = require('cluster');
 | 
				
			|||||||
//var minWorkers = 2;
 | 
					//var minWorkers = 2;
 | 
				
			||||||
var numCores = 2; // Math.max(minWorkers, require('os').cpus().length);
 | 
					var numCores = 2; // Math.max(minWorkers, require('os').cpus().length);
 | 
				
			||||||
var workers = [];
 | 
					var workers = [];
 | 
				
			||||||
 | 
					var state = { firstRun: true };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function fork() {
 | 
				
			||||||
 | 
					  if (workers.length < numCores) {
 | 
				
			||||||
 | 
					    workers.push(cluster.fork());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cluster.on('online', function (worker) {
 | 
				
			||||||
 | 
					  console.info('[MASTER] Worker ' + worker.process.pid + ' is online');
 | 
				
			||||||
 | 
					  fork();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var config = {
 | 
					  var config = {
 | 
				
			||||||
    externalPort: 443                                       // world accessible
 | 
					    externalPort: 443                                       // world accessible
 | 
				
			||||||
  , externalPortInsecure: 80                                // world accessible
 | 
					  , externalPortInsecure: 80                                // world accessible
 | 
				
			||||||
  // TODO externalInsecurePort?
 | 
					  // TODO externalInsecurePort?
 | 
				
			||||||
  , locked: false // TODO XXX
 | 
					  , locked: false // TODO XXX
 | 
				
			||||||
, ipcKey: null
 | 
					 | 
				
			||||||
    // XXX
 | 
					    // XXX
 | 
				
			||||||
    // TODO needs mappings from db
 | 
					    // TODO needs mappings from db
 | 
				
			||||||
    // TODO autoconfig Caddy caddy
 | 
					    // TODO autoconfig Caddy caddy
 | 
				
			||||||
@ -29,7 +40,63 @@ var config = {
 | 
				
			|||||||
    , bin: '/usr/local/bin/caddy'
 | 
					    , bin: '/usr/local/bin/caddy'
 | 
				
			||||||
    , sitespath: path.join(__dirname, 'sites-enabled')
 | 
					    , sitespath: path.join(__dirname, 'sites-enabled')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
, redirects: [
 | 
					  };
 | 
				
			||||||
 | 
					  var useCaddy = require('fs').existsSync(config.caddy.bin);
 | 
				
			||||||
 | 
					  var caddy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config.localPort = process.argv[2] || (useCaddy ? 4080 : 443);   // system / local network
 | 
				
			||||||
 | 
					  config.insecurePort = process.argv[3] || (useCaddy ? 80 : 80);   // meh
 | 
				
			||||||
 | 
					  if (state.firstRun) {
 | 
				
			||||||
 | 
					    state.firstRun = false;
 | 
				
			||||||
 | 
					    if (useCaddy) {
 | 
				
			||||||
 | 
					      caddy = require('../lib/spawn-caddy').create(config);
 | 
				
			||||||
 | 
					      // relies on { localPort, locked }
 | 
				
			||||||
 | 
					      caddy.spawn(config);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO XXX Should these be configurable? If so, where?
 | 
				
			||||||
 | 
					  var certPaths = [
 | 
				
			||||||
 | 
					    path.join(__dirname, '..', '..', 'certs', 'live')
 | 
				
			||||||
 | 
					  , path.join(__dirname, '..', '..', 'letsencrypt', 'live')
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					  // TODO communicate config with environment vars?
 | 
				
			||||||
 | 
					  var info = {
 | 
				
			||||||
 | 
					    type: 'walnut.init'
 | 
				
			||||||
 | 
					  , conf: {
 | 
				
			||||||
 | 
					      protocol: useCaddy ? 'http' : 'https'
 | 
				
			||||||
 | 
					    , externalPort: config.externalPort
 | 
				
			||||||
 | 
					    , localPort: config.localPort
 | 
				
			||||||
 | 
					    , insecurePort: config.insecurePort
 | 
				
			||||||
 | 
					    , certPaths: useCaddy ? null : certPaths
 | 
				
			||||||
 | 
					    , trustProxy: useCaddy ? true : false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function touchMaster(msg) {
 | 
				
			||||||
 | 
					    if ('walnut.webserver.listening' !== msg.type) {
 | 
				
			||||||
 | 
					      console.warn('[MASTER] received unexpected message from worker');
 | 
				
			||||||
 | 
					      console.warn(msg);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // calls init if init has not been called
 | 
				
			||||||
 | 
					    state.caddy = caddy;
 | 
				
			||||||
 | 
					    state.workers = workers;
 | 
				
			||||||
 | 
					    require('../lib/master').touch(config, state).then(function (results) {
 | 
				
			||||||
 | 
					      //var memstore = results.memstore;
 | 
				
			||||||
 | 
					      var sqlstore = results.sqlstore;
 | 
				
			||||||
 | 
					      info.type = 'walnut.webserver.onrequest';
 | 
				
			||||||
 | 
					      // TODO let this load after server is listening
 | 
				
			||||||
 | 
					      info.conf['org.oauth3.consumer'] = config['org.oauth3.consumer'];
 | 
				
			||||||
 | 
					      info.conf['org.oauth3.provider'] = config['org.oauth3.provider'];
 | 
				
			||||||
 | 
					      info.conf.keys = config.keys;
 | 
				
			||||||
 | 
					      info.conf.memstoreSock = config.memstoreSock;
 | 
				
			||||||
 | 
					      info.conf.sqlite3Sock = config.sqlite3Sock;
 | 
				
			||||||
 | 
					      // TODO get this from db config instead
 | 
				
			||||||
 | 
					      info.conf.privkey = config.privkey;
 | 
				
			||||||
 | 
					      info.conf.pubkey = config.pubkey;
 | 
				
			||||||
 | 
					      info.conf.redirects = [
 | 
				
			||||||
        { "ip": false, "id": "*", "value": false } // default no-www
 | 
					        { "ip": false, "id": "*", "value": false } // default no-www
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      , { "ip": false, "id": "daplie.domains", "value": null }
 | 
					      , { "ip": false, "id": "daplie.domains", "value": null }
 | 
				
			||||||
@ -45,77 +112,19 @@ var config = {
 | 
				
			|||||||
      , { "ip": true, "id": "yes.daplie.domains", "value": true }
 | 
					      , { "ip": true, "id": "yes.daplie.domains", "value": true }
 | 
				
			||||||
      , { "ip": true, "id": "*.yes.daplie.domains", "value": true }
 | 
					      , { "ip": true, "id": "*.yes.daplie.domains", "value": true }
 | 
				
			||||||
      , { "ip": true, "id": "ns1.daplie.domains", "value": false }
 | 
					      , { "ip": true, "id": "ns1.daplie.domains", "value": false }
 | 
				
			||||||
  ]
 | 
					      ];
 | 
				
			||||||
      // TODO use sqlite3 or autogenerate ?
 | 
					      // TODO use sqlite3 or autogenerate ?
 | 
				
			||||||
, privkey: require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem', 'ascii')
 | 
					      info.conf.privkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem', 'ascii');
 | 
				
			||||||
, pubkey: require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem.pub', 'ascii')
 | 
					      info.conf.pubkey = require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem.pub', 'ascii');
 | 
				
			||||||
  // keys
 | 
					  // keys
 | 
				
			||||||
  // letsencrypt
 | 
					  // letsencrypt
 | 
				
			||||||
  // com.example.provider
 | 
					  // com.example.provider
 | 
				
			||||||
  // com.example.consumer
 | 
					  // com.example.consumer
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
var useCaddy = require('fs').existsSync(config.caddy.bin);
 | 
					 | 
				
			||||||
var state = {};
 | 
					 | 
				
			||||||
var caddy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
config.localPort = process.argv[2] || (useCaddy ? 4080 : 443);   // system / local network
 | 
					 | 
				
			||||||
config.insecurePort = process.argv[3] || (useCaddy ? 80 : 80);   // meh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function fork() {
 | 
					 | 
				
			||||||
  if (workers.length < numCores) {
 | 
					 | 
				
			||||||
    workers.push(cluster.fork());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cluster.on('online', function (worker) {
 | 
					 | 
				
			||||||
  console.info('[MASTER] Worker ' + worker.process.pid + ' is online');
 | 
					 | 
				
			||||||
  fork();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // TODO XXX Should these be configurable? If so, where?
 | 
					 | 
				
			||||||
  var certPaths = [
 | 
					 | 
				
			||||||
    path.join(__dirname, 'certs', 'live')
 | 
					 | 
				
			||||||
  , path.join(__dirname, 'letsencrypt', 'live')
 | 
					 | 
				
			||||||
  ];
 | 
					 | 
				
			||||||
  // TODO communicate config with environment vars?
 | 
					 | 
				
			||||||
  var info = {
 | 
					 | 
				
			||||||
    type: 'walnut.init'
 | 
					 | 
				
			||||||
  , conf: {
 | 
					 | 
				
			||||||
      protocol: useCaddy ? 'http' : 'https'
 | 
					 | 
				
			||||||
    , externalPort: config.externalPort
 | 
					 | 
				
			||||||
    , localPort: config.localPort
 | 
					 | 
				
			||||||
    , insecurePort: config.insecurePort
 | 
					 | 
				
			||||||
    , trustProxy: useCaddy ? true : false
 | 
					 | 
				
			||||||
    , certPaths: useCaddy ? null : certPaths
 | 
					 | 
				
			||||||
    , ipcKey: null
 | 
					 | 
				
			||||||
      // TODO let this load after server is listening
 | 
					 | 
				
			||||||
    , 'org.oauth3.consumer': config['org.oauth3.consumer']
 | 
					 | 
				
			||||||
    , 'org.oauth3.provider': config['org.oauth3.provider']
 | 
					 | 
				
			||||||
    , keys: config.keys
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  worker.send(info);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function touchMaster(msg) {
 | 
					 | 
				
			||||||
    if ('walnut.webserver.listening' !== msg.type) {
 | 
					 | 
				
			||||||
      console.warn('[MASTER] received unexpected message from worker');
 | 
					 | 
				
			||||||
      console.warn(msg);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // calls init if init has not been called
 | 
					 | 
				
			||||||
    state.caddy = caddy;
 | 
					 | 
				
			||||||
    state.workers = workers;
 | 
					 | 
				
			||||||
    require('../lib/master').touch(config, state).then(function () {
 | 
					 | 
				
			||||||
      info.type = 'walnut.webserver.onrequest';
 | 
					 | 
				
			||||||
      info.conf.ipcKey = config.ipcKey;
 | 
					 | 
				
			||||||
      info.conf.memstoreSock = config.memstoreSock;
 | 
					 | 
				
			||||||
      info.conf.sqlite3Sock = config.sqlite3Sock;
 | 
					 | 
				
			||||||
      // TODO get this from db config instead
 | 
					 | 
				
			||||||
      info.conf.privkey = config.privkey;
 | 
					 | 
				
			||||||
      info.conf.pubkey = config.pubkey;
 | 
					 | 
				
			||||||
      worker.send(info);
 | 
					      worker.send(info);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  worker.send(info);
 | 
				
			||||||
  worker.on('message', touchMaster);
 | 
					  worker.on('message', touchMaster);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -136,9 +145,3 @@ cluster.on('exit', function (worker, code, signal) {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fork();
 | 
					fork();
 | 
				
			||||||
 | 
					 | 
				
			||||||
if (useCaddy) {
 | 
					 | 
				
			||||||
  caddy = require('../lib/spawn-caddy').create(config);
 | 
					 | 
				
			||||||
  // relies on { localPort, locked }
 | 
					 | 
				
			||||||
  caddy.spawn(config);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -21,36 +21,30 @@ module.exports.create = function (securePort, insecurePort, info, serverCallback
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function redirectHttps(req, res) {
 | 
					  function redirectHttps(req, res) {
 | 
				
			||||||
 | 
					    if (req.headers.host && /^\/.well-known\/acme-challenge/.test(req.url) && useAppInsecurely(req, res)) {
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 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.redirects || info.conf.redirects) {
 | 
				
			||||||
 | 
					      redirectives = require('./hostname-redirects').compile(info.redirects || info.conf.redirects);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (require('./no-www').scrubTheDub(req, res, redirectives)) {
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Let it do this once they visit the https site
 | 
					    // Let it do this once they visit the https site
 | 
				
			||||||
    // res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload');
 | 
					    // res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var host = req.headers.host || '';
 | 
					    var host = req.headers.host || '';
 | 
				
			||||||
    var url = req.url;
 | 
					    var url = req.url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO
 | 
					    // TODO
 | 
				
			||||||
    // allow exceptions for the case of arduino and whatnot that cannot handle https?
 | 
					    // 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/
 | 
					    // http://evothings.com/is-it-possible-to-secure-micro-controllers-used-within-iot/
 | 
				
			||||||
    // needs ECDSA?
 | 
					    // needs ECDSA?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.warn('HARD-CODED HTTPS EXCEPTION in insecure-server.js');
 | 
					 | 
				
			||||||
    if (/redirect-www.org$/.test(host) && useAppInsecurely(req, res)) {
 | 
					 | 
				
			||||||
      return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (/^\/.well-known\/acme-challenge/.test(req.url) && useAppInsecurely(req, res)) {
 | 
					 | 
				
			||||||
      console.log('exception for acme-challenge');
 | 
					 | 
				
			||||||
      return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var escapeHtml = require('escape-html');
 | 
					    var escapeHtml = require('escape-html');
 | 
				
			||||||
    var newLocation = 'https://'
 | 
					    var newLocation = 'https://'
 | 
				
			||||||
      + host.replace(/:\d+/, ':' + securePort) + url
 | 
					      + host.replace(/:\d+/, ':' + securePort) + url
 | 
				
			||||||
 | 
				
			|||||||
@ -2,12 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var cluster = require('cluster');
 | 
					var cluster = require('cluster');
 | 
				
			||||||
var PromiseA = require('bluebird');
 | 
					var PromiseA = require('bluebird');
 | 
				
			||||||
var memstore;
 | 
					 | 
				
			||||||
var sqlstore;
 | 
					 | 
				
			||||||
// TODO
 | 
					// TODO
 | 
				
			||||||
// var rootMasterKey;
 | 
					// var rootMasterKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function init(conf/*, state*/) {
 | 
					function init(conf, state) {
 | 
				
			||||||
  if (!conf.ipcKey) {
 | 
					  if (!conf.ipcKey) {
 | 
				
			||||||
    conf.ipcKey = require('crypto').randomBytes(16).toString('base64');
 | 
					    conf.ipcKey = require('crypto').randomBytes(16).toString('base64');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -46,27 +44,20 @@ function init(conf/*, state*/) {
 | 
				
			|||||||
    , store: cluster.isMaster && null //new require('express-session/session/memory')()
 | 
					    , store: cluster.isMaster && null //new require('express-session/session/memory')()
 | 
				
			||||||
      // TODO implement
 | 
					      // TODO implement
 | 
				
			||||||
    , key: conf.ipcKey
 | 
					    , key: conf.ipcKey
 | 
				
			||||||
    }).then(function (_memstore) {
 | 
					 | 
				
			||||||
      memstore = _memstore;
 | 
					 | 
				
			||||||
      return memstore;
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  , sqlite3.createServer({
 | 
					  , sqlite3.createServer({
 | 
				
			||||||
      verbose: null
 | 
					      verbose: null
 | 
				
			||||||
    , sock: conf.sqlite3Sock
 | 
					    , sock: conf.sqlite3Sock
 | 
				
			||||||
    , ipcKey: conf.ipcKey
 | 
					    , ipcKey: conf.ipcKey
 | 
				
			||||||
    }).then(function (_sqlstore) {
 | 
					 | 
				
			||||||
      sqlstore = _sqlstore;
 | 
					 | 
				
			||||||
      return sqlstore;
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  ]).then(function (/*args*/) {
 | 
					  ]).then(function (args) {
 | 
				
			||||||
    return conf;
 | 
					    state.memstore = args[0];
 | 
				
			||||||
    /*
 | 
					    state.sqlstore = args[1];
 | 
				
			||||||
    {
 | 
					    return {
 | 
				
			||||||
      conf: conf
 | 
					      conf: conf
 | 
				
			||||||
    , memstore: memstore // args[0]
 | 
					    , memstore: args[0]
 | 
				
			||||||
    , sqlstore: sqlstore // args[1]
 | 
					    , sqlstore: args[1]
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return promise;
 | 
					  return promise;
 | 
				
			||||||
@ -83,13 +74,6 @@ function touch(conf, state) {
 | 
				
			|||||||
    conf.initialized = true;
 | 
					    conf.initialized = true;
 | 
				
			||||||
    return conf;
 | 
					    return conf;
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
  setInterval(function () {
 | 
					 | 
				
			||||||
    console.log('SIGUSR1 to caddy');
 | 
					 | 
				
			||||||
    return caddy.update(caddyConf);
 | 
					 | 
				
			||||||
  }, 10 * 60 * 1000);
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.init = init;
 | 
					module.exports.init = init;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user