Improved dependency management, main web port now uses TLS 1.2 only.

This commit is contained in:
Ylian Saint-Hilaire 2018-01-31 16:10:15 -08:00
parent 682573d262
commit 49927f0abc
4 changed files with 66 additions and 49 deletions

2
db.js
View File

@ -25,7 +25,7 @@ module.exports.CreateDB = function (args, datapath) {
if (args.mongodb) { if (args.mongodb) {
// Use MongoDB // Use MongoDB
obj.databaseType = 2; obj.databaseType = 2;
var Datastore = require("mongojs"); var Datastore = require('mongojs');
var db = Datastore(args.mongodb); var db = Datastore(args.mongodb);
var dbcollection = 'meshcentral'; var dbcollection = 'meshcentral';
if (args.mongodbcol) { dbcollection = args.mongodbcol; } if (args.mongodbcol) { dbcollection = args.mongodbcol; }

View File

@ -9,7 +9,7 @@
// If app metrics is available // If app metrics is available
if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } } if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } }
function CreateMeshCentralServer() { function CreateMeshCentralServer(config) {
var obj = {}; var obj = {};
obj.db; obj.db;
obj.webserver; obj.webserver;
@ -33,7 +33,7 @@ function CreateMeshCentralServer() {
obj.connectivityByNode = {}; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect) obj.connectivityByNode = {}; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
obj.peerConnectivityByNode = {}; // This object keeps a list of all connected CIRA and agents of peers, by serverid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect) obj.peerConnectivityByNode = {}; // This object keeps a list of all connected CIRA and agents of peers, by serverid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
obj.debugLevel = 0; obj.debugLevel = 0;
obj.config = {}; // Configuration file obj.config = config; // Configuration file
obj.dbconfig = {}; // Persistance values, loaded from database obj.dbconfig = {}; // Persistance values, loaded from database
obj.certificateOperations = null; obj.certificateOperations = null;
obj.defaultMeshCmd = null; obj.defaultMeshCmd = null;
@ -82,6 +82,7 @@ function CreateMeshCentralServer() {
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen']; var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen'];
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; } if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
if ((obj.args.help == true) || (obj.args['?'] == true)) { if ((obj.args.help == true) || (obj.args['?'] == true)) {
console.log('MeshCentral2 Beta 2, a web-based remote computer management web portal.\r\n'); console.log('MeshCentral2 Beta 2, a web-based remote computer management web portal.\r\n');
@ -199,22 +200,6 @@ function CreateMeshCentralServer() {
if (obj.args.datapath) { obj.datapath = obj.args.datapath; } if (obj.args.datapath) { obj.datapath = obj.args.datapath; }
if (obj.args.filespath) { obj.filespath = obj.args.filespath; } if (obj.args.filespath) { obj.filespath = obj.args.filespath; }
// Read configuration file if present and change arguments.
var configFilePath = obj.path.join(obj.datapath, 'config.json');
if (obj.fs.existsSync(configFilePath)) {
// Load and validate the configuration file
try { obj.config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return; }
if (obj.config.domains == null) { obj.config.domains = {}; }
for (var i in obj.config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return; } }
// Set the command line arguments to the config file if they are not present
if (obj.config.settings) { for (var i in obj.config.settings) { if (obj.args[i] == null) obj.args[i] = obj.config.settings[i]; } }
} else {
// Copy the "sample-config.json" to give users a starting point
var sampleConfigPath = obj.path.join(__dirname, 'sample-config.json');
if (obj.fs.existsSync(sampleConfigPath)) { obj.fs.createReadStream(sampleConfigPath).pipe(obj.fs.createWriteStream(configFilePath)); }
}
obj.common.objKeysToLower(obj.config); // Lower case all keys in the config file
// Read environment variables. For a subset of arguments, we allow them to be read from environment variables. // Read environment variables. For a subset of arguments, we allow them to be read from environment variables.
var xenv = ['user', 'port', 'mpsport', 'redirport', 'exactport', 'debug']; var xenv = ['user', 'port', 'mpsport', 'redirport', 'exactport', 'debug'];
for (var i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } } for (var i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } }
@ -1033,10 +1018,49 @@ function CreateMeshCentralServer() {
return obj; return obj;
} }
// Return the server configuration
function getConfig() {
// Figure out the datapath location
var fs = require('fs');
var path = require('path');
var datapath = null;
var args = require('minimist')(process.argv.slice(2));
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) {
datapath = path.join(__dirname, '../../meshcentral-data');
} else {
datapath = path.join(__dirname, '../meshcentral-data');
}
if (args.datapath) { datapath = args.datapath; }
try { fs.mkdirSync(datapath); } catch (e) { }
// Read configuration file if present and change arguments.
var config = {}, configFilePath = path.join(datapath, 'config.json');
if (fs.existsSync(configFilePath)) {
// Load and validate the configuration file
try { config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return null; }
if (config.domains == null) { config.domains = {}; }
for (var i in config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return null; } }
} else {
// Copy the "sample-config.json" to give users a starting point
var sampleConfigPath = path.join(__dirname, 'sample-config.json');
if (fs.existsSync(sampleConfigPath)) { fs.createReadStream(sampleConfigPath).pipe(fs.createWriteStream(configFilePath)); }
}
// Set the command line arguments to the config file if they are not present
if (!config.settings) { config.settings = {}; }
for (var i in args) { config.settings[i] = args[i]; }
// Lower case all keys in the config file
require('./common.js').objKeysToLower(config);
return config;
}
// Check if a list of modules are present and install any missing ones
function InstallModules(modules, func) { function InstallModules(modules, func) {
if (modules.length > 0) { InstallModule(modules.shift(), InstallModules, modules, func); } else { func(); } if (modules.length > 0) { InstallModule(modules.shift(), InstallModules, modules, func); } else { func(); }
} }
// Check if a module is present and install it if missing
function InstallModule(modulename, func, tag1, tag2) { function InstallModule(modulename, func, tag1, tag2) {
try { try {
var module = require(modulename); var module = require(modulename);
@ -1057,10 +1081,20 @@ function InstallModule(modulename, func, tag1, tag2) {
// Detect CTRL-C on Linux and stop nicely // Detect CTRL-C on Linux and stop nicely
process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop(); meshserver = null; } console.log('Server Ctrl-C exit...'); process.exit(); }); process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop(); meshserver = null; } console.log('Server Ctrl-C exit...'); process.exit(); });
// Build the list of required modules // Load the really basic modules
var modules = ['nedb', 'https', 'unzip', 'xmldom', 'express', 'mongojs', 'archiver', 'minimist', 'nodemailer', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push("node-sspi"); modules.push("node-windows"); }
// Run as a command line, if we are not using service arguments, don't need to install the service package.
var meshserver = null; var meshserver = null;
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(); meshserver.Start(); }); InstallModules(['minimist'], function () {
// Get the server configuration
var config = getConfig();
if (config == null) { process.exit(); }
// Build the list of required modules
var modules = ['ws', 'nedb', 'https', 'unzip', 'xmldom', 'express', 'mongojs', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push('node-sspi'); modules.push('node-windows'); } // Add Windows modules
if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
// Install any missing modules and launch the server
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config); meshserver.Start(); });
});

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.1.3-r", "version": "0.1.3-v",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",
@ -29,32 +29,20 @@
"archiver": "^1.3.0", "archiver": "^1.3.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
"compression": "^1.7.1", "compression": "^1.7.1",
"connect-redis": "^3.3.2", "connect-redis": "^3.3.3",
"express": "^4.16.2", "express": "^4.16.2",
"express-handlebars": "^3.0.0", "express-handlebars": "^3.0.0",
"express-session": "^1.15.6", "express-session": "^1.15.6",
"express-ws": "^2.0.0", "express-ws": "^2.0.0",
"meshcentral": "*", "meshcentral": "*",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mongojs": "^2.4.1",
"multiparty": "^4.1.3", "multiparty": "^4.1.3",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"node-forge": "^0.6.49", "node-forge": "^0.6.49",
"node-windows": "^0.1.14",
"nodemailer": "^4.4.1",
"unzip": "^0.1.11", "unzip": "^0.1.11",
"ws": "^3.2.0", "ws": "^3.3.3",
"xmldom": "^0.1.27" "xmldom": "^0.1.27"
}, },
"optionalDependencies": {
"node-sspi": "^0.2.2",
"node-windows": "^0.1.14",
"mongojs": "^2.4.0",
"greenlock": "^2.1.18",
"le-store-certbot": "^2.0.5",
"le-challenge-fs": "^2.0.8",
"le-acme-core": "^2.1.1"
},
"devDependencies": {}, "devDependencies": {},
"readme": "readme.txt" "readme": "readme.txt"
} }

View File

@ -72,7 +72,6 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
obj.tlsSniCredentials; obj.tlsSniCredentials;
obj.dnsDomains = {}; obj.dnsDomains = {};
// Mesh Rights // Mesh Rights
const MESHRIGHT_EDITMESH = 1; const MESHRIGHT_EDITMESH = 1;
const MESHRIGHT_MANAGEUSERS = 2; const MESHRIGHT_MANAGEUSERS = 2;
@ -140,14 +139,10 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
// Setup the HTTP server without TLS // Setup the HTTP server without TLS
obj.expressWs = require('express-ws')(obj.app); obj.expressWs = require('express-ws')(obj.app);
} else { } else {
// Setup the HTTP server with TLS // Setup the HTTP server with TLS, use only TLS 1.2 and higher.
if (obj.tlsSniCredentials != null) { var tlsOptions = { cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true, secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_NO_TLSv1 | obj.constants.SSL_OP_NO_TLSv11 };
// We have multiple web server certificate used depending on the domain name if (obj.tlsSniCredentials != null) { tlsOptions.SNICallback = TlsSniCallback; } // We have multiple web server certificate used depending on the domain name
obj.tlsServer = require('https').createServer({ SNICallback: TlsSniCallback, cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true }, obj.app); obj.tlsServer = require('https').createServer(tlsOptions, obj.app);
} else {
// We have a single web server certificate
obj.tlsServer = require('https').createServer({ cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true }, obj.app);
}
obj.expressWs = require('express-ws')(obj.app, obj.tlsServer); obj.expressWs = require('express-ws')(obj.app, obj.tlsServer);
} }