mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-22 22:17:31 +03:00
Fixed database and webserver race condition.
This commit is contained in:
parent
6d38294aea
commit
8f352de5b9
@ -156,7 +156,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
else if (cmdid == 12) { // MeshCommand_AgentHash
|
||||
if ((msg.length == 52) && (obj.agentExeInfo != null) && (obj.agentExeInfo.update == true)) {
|
||||
var agenthash = obj.common.rstr2hex(msg.substring(4)).toLowerCase();
|
||||
if (agenthash != obj.agentExeInfo.hash) {
|
||||
if ((agenthash != obj.agentExeInfo.hash) && (agenthash != '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')) {
|
||||
// Mesh agent update required
|
||||
if (obj.nodeid != null) { obj.parent.parent.debug(1, 'Agent update required, NodeID=0x' + obj.nodeid.substring(0, 16) + ', ' + obj.agentExeInfo.desc); }
|
||||
obj.fs.open(obj.agentExeInfo.path, 'r', function (err, fd) {
|
||||
|
@ -90,7 +90,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
|
||||
|
||||
// Check for invalid arguments
|
||||
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', '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', 'swarmallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore'];
|
||||
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', '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', 'dbexportmin', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'swarmallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore'];
|
||||
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; }
|
||||
for (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.
|
||||
@ -279,6 +279,16 @@ function CreateMeshCentralServer(config, args) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (obj.args.dbexportmin) {
|
||||
// Export a minimal database to a JSON file. Export only users, meshes and nodes.
|
||||
// This is a useful command to look at the database.
|
||||
if (obj.args.dbexportmin == true) { obj.args.dbexportmin = obj.getConfigFilePath('meshcentral.db.json'); }
|
||||
obj.db.GetAllType({ $in: ['user', 'node', 'mesh'] }, function (err, docs) {
|
||||
obj.fs.writeFileSync(obj.args.dbexportmin, JSON.stringify(docs));
|
||||
console.log('Exported ' + docs.length + ' objects(s) to ' + obj.args.dbexportmin + '.'); process.exit();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (obj.args.dbimport) {
|
||||
// Import the entire database from a JSON file
|
||||
if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); }
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.2.6-q",
|
||||
"version": "0.2.6-t",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
@ -1755,7 +1755,6 @@
|
||||
var deviceHeaders = {};
|
||||
var oldviewmode = 0;
|
||||
function updateDevices() {
|
||||
if (xxcurrentView != 1) return;
|
||||
var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
|
||||
QV('xdevices', view < 4);
|
||||
QV('xdevicesmap', view == 4);
|
||||
|
84
webserver.js
84
webserver.js
@ -175,35 +175,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
//function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||||
|
||||
if (obj.args.notls || obj.args.tlsoffload) {
|
||||
// Setup the HTTP server without TLS
|
||||
obj.expressWs = require('express-ws')(obj.app);
|
||||
} else {
|
||||
// Setup the HTTP server with TLS, use only TLS 1.2 and higher with perfect forward secrecy (PFS).
|
||||
const tlsOptions = { cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!RSA:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 };
|
||||
if (obj.tlsSniCredentials != null) { tlsOptions.SNICallback = TlsSniCallback; } // We have multiple web server certificate used depending on the domain name
|
||||
obj.tlsServer = require('https').createServer(tlsOptions, obj.app);
|
||||
obj.tlsServer.on('secureConnection', function () { /*console.log('tlsServer secureConnection');*/ });
|
||||
obj.tlsServer.on('error', function () { console.log('tlsServer error'); });
|
||||
obj.tlsServer.on('newSession', function (id, data, cb) { if (tlsSessionStoreCount > 1000) { tlsSessionStoreCount = 0; tlsSessionStore = {}; } tlsSessionStore[id.toString('hex')] = data; tlsSessionStoreCount++; cb(); });
|
||||
obj.tlsServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
|
||||
obj.expressWs = require('express-ws')(obj.app, obj.tlsServer);
|
||||
}
|
||||
|
||||
// Setup middleware
|
||||
obj.app.engine('handlebars', obj.exphbs({})); // defaultLayout: 'main'
|
||||
obj.app.set('view engine', 'handlebars');
|
||||
if (obj.args.tlsoffload) { obj.app.set('trust proxy', obj.args.tlsoffload); } // Reverse proxy should add the "X-Forwarded-*" headers
|
||||
obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
|
||||
var sessionOptions = {
|
||||
name: 'xid', // Recommended security practice to not use the default cookie name
|
||||
httpOnly: true,
|
||||
keys: [obj.args.sessionkey], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
|
||||
secure: (obj.args.notls != true) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
|
||||
}
|
||||
if (obj.args.sessiontime != null) { sessionOptions.maxAge = (obj.args.sessiontime * 60 * 1000); }
|
||||
obj.app.use(obj.session(sessionOptions));
|
||||
|
||||
// Session-persisted message middleware
|
||||
obj.app.use(function (req, res, next) {
|
||||
var err = null, msg = null, passhint = null;
|
||||
@ -234,10 +205,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch all meshes from the database, keep this in memory
|
||||
obj.db.GetAllType('mesh', function (err, docs) { obj.common.unEscapeAllLinksFieldName(docs); for (var i in docs) { obj.meshes[docs[i]._id] = docs[i]; } });
|
||||
obj.db.GetAllType('mesh', function (err, docs) {
|
||||
obj.common.unEscapeAllLinksFieldName(docs);
|
||||
for (var i in docs) { obj.meshes[docs[i]._id] = docs[i]; }
|
||||
|
||||
// We loaded the users and mesh state, start the server
|
||||
serverStart();
|
||||
});
|
||||
});
|
||||
|
||||
// Authenticate the user
|
||||
obj.authenticate = function (name, pass, domain, fn) {
|
||||
@ -764,7 +741,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
user = obj.users[req.session.userid];
|
||||
if ((user == null) || (user.sid != req.session.usersid)) {
|
||||
// Create the domain user
|
||||
var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid };
|
||||
var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid, creation: Date.now() };
|
||||
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
|
||||
if (usercount == 0) { user2.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin.
|
||||
obj.users[req.session.userid] = user2;
|
||||
@ -1873,6 +1850,38 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
res.send(meshsettings);
|
||||
};
|
||||
|
||||
// Starts the HTTPS server, this should be called after the user/mesh tables are loaded
|
||||
function serverStart() {
|
||||
// Start the server, only after users and meshes are loaded from the database.
|
||||
if (obj.args.notls || obj.args.tlsoffload) {
|
||||
// Setup the HTTP server without TLS
|
||||
obj.expressWs = require('express-ws')(obj.app);
|
||||
} else {
|
||||
// Setup the HTTP server with TLS, use only TLS 1.2 and higher with perfect forward secrecy (PFS).
|
||||
const tlsOptions = { cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca, rejectUnauthorized: true, ciphers: "HIGH:!aNULL:!eNULL:!EXPORT:!RSA:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 };
|
||||
if (obj.tlsSniCredentials != null) { tlsOptions.SNICallback = TlsSniCallback; } // We have multiple web server certificate used depending on the domain name
|
||||
obj.tlsServer = require('https').createServer(tlsOptions, obj.app);
|
||||
obj.tlsServer.on('secureConnection', function () { /*console.log('tlsServer secureConnection');*/ });
|
||||
obj.tlsServer.on('error', function () { console.log('tlsServer error'); });
|
||||
obj.tlsServer.on('newSession', function (id, data, cb) { if (tlsSessionStoreCount > 1000) { tlsSessionStoreCount = 0; tlsSessionStore = {}; } tlsSessionStore[id.toString('hex')] = data; tlsSessionStoreCount++; cb(); });
|
||||
obj.tlsServer.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null); });
|
||||
obj.expressWs = require('express-ws')(obj.app, obj.tlsServer);
|
||||
}
|
||||
|
||||
// Setup middleware
|
||||
obj.app.engine('handlebars', obj.exphbs({})); // defaultLayout: 'main'
|
||||
obj.app.set('view engine', 'handlebars');
|
||||
if (obj.args.tlsoffload) { obj.app.set('trust proxy', obj.args.tlsoffload); } // Reverse proxy should add the "X-Forwarded-*" headers
|
||||
obj.app.use(obj.bodyParser.urlencoded({ extended: false }));
|
||||
var sessionOptions = {
|
||||
name: 'xid', // Recommended security practice to not use the default cookie name
|
||||
httpOnly: true,
|
||||
keys: [obj.args.sessionkey], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
|
||||
secure: (obj.args.notls != true) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
|
||||
}
|
||||
if (obj.args.sessiontime != null) { sessionOptions.maxAge = (obj.args.sessiontime * 60 * 1000); }
|
||||
obj.app.use(obj.session(sessionOptions));
|
||||
|
||||
// Add HTTP security headers to all responses
|
||||
obj.app.use(function (req, res, next) {
|
||||
res.removeHeader("X-Powered-By");
|
||||
@ -1997,6 +2006,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
obj.app.use(url, obj.express.static(obj.path.join(__dirname, 'public')));
|
||||
}
|
||||
|
||||
// Start server on a free port
|
||||
CheckListenPort(obj.args.port, StartWebServer);
|
||||
}
|
||||
|
||||
// Authenticates a session and forwards
|
||||
function PerformWSSessionAuth(ws, req, noAuthOk, func) {
|
||||
try {
|
||||
@ -2131,8 +2144,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
agent.send(obj.common.ShortToStr(11) + obj.common.ShortToStr(0)); // Command 11, ask for mesh core hash.
|
||||
} else if (coretype == 'custom') {
|
||||
agent.agentCoreCheck = 1000; // Tell the agent object we are using a custom core.
|
||||
const hash = obj.crypto.createHash('sha384').update(Buffer.from(core, 'binary')).digest().toString('binary'); // Perform a SHA384 hash on the core module
|
||||
agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + core); // Send the code module to the agent
|
||||
const hash = obj.crypto.createHash('sha384').update(Buffer.from(coredata, 'binary')).digest().toString('binary'); // Perform a SHA384 hash on the core module
|
||||
agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + coredata); // Send the code module to the agent
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2165,9 +2178,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
else if (arguments.length == 7) { console.log(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); }
|
||||
}
|
||||
|
||||
// Start server on a free port
|
||||
CheckListenPort(obj.args.port, StartWebServer);
|
||||
|
||||
/*
|
||||
obj.wssessions = {}; // UserId --> Array Of Sessions
|
||||
obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd)
|
||||
|
Loading…
Reference in New Issue
Block a user