Server memory instrumentation and fixes.

This commit is contained in:
Ylian Saint-Hilaire 2019-03-07 19:56:24 -08:00
parent 68e7d6f6f7
commit cee5f2ac00
9 changed files with 2141508 additions and 28 deletions

File diff suppressed because it is too large Load Diff

View File

@ -714,6 +714,16 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.close(1); obj.close(1);
} }
obj.sendUpdatedIntelAmtPolicy = function() {
var mesh = obj.parent.meshes[obj.dbMeshKey];
if (mesh != null) {
// Send Intel AMT policy
var amtPolicy = null;
if (mesh.amt != null) { amtPolicy = mesh.amt; }
obj.send(JSON.stringify({ action: 'amtPolicy', amtPolicy: amtPolicy }));
}
}
function agentCoreIsStable() { function agentCoreIsStable() {
// Check that the mesh exists // Check that the mesh exists
var mesh = obj.parent.meshes[obj.dbMeshKey]; var mesh = obj.parent.meshes[obj.dbMeshKey];

View File

@ -96,7 +96,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. 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 // 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', 'dbexportmin', 'dbimport', 'dbmerge', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath']; 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', 'dbmerge', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath', 'memorytracking'];
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 (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. 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.
@ -200,18 +200,20 @@ function CreateMeshCentralServer(config, args) {
// Get current and latest MeshCentral server versions using NPM // Get current and latest MeshCentral server versions using NPM
obj.getLatestServerVersion = function (callback) { obj.getLatestServerVersion = function (callback) {
if (callback == null) return; if (callback == null) return;
if (typeof obj.args.selfupdate == 'string') { callback(obj.currentVer, obj.args.selfupdate); return; } // If we are targetting a specific version, return that one as current. try {
var child_process = require('child_process'); if (typeof obj.args.selfupdate == 'string') { callback(obj.currentVer, obj.args.selfupdate); return; } // If we are targetting a specific version, return that one as current.
var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm'); var child_process = require('child_process');
var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000 }, function (error, stdout, stderr) { }); var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm');
xprocess.data = ''; var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000 }, function (error, stdout, stderr) { });
xprocess.stdout.on('data', function (data) { xprocess.data += data; }); xprocess.data = '';
xprocess.stderr.on('data', function (data) { }); xprocess.stdout.on('data', function (data) { xprocess.data += data; });
xprocess.on('close', function (code) { xprocess.stderr.on('data', function (data) { });
var latestVer = null; xprocess.on('close', function (code) {
if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } } var latestVer = null;
callback(obj.currentVer, latestVer); if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } }
}); callback(obj.currentVer, latestVer);
});
} catch (ex) { callback(obj.currentVer, null); } // If the system is running out of memory, an exception here can easily happen.
}; };
// Initiate server self-update // Initiate server self-update
@ -510,6 +512,20 @@ function CreateMeshCentralServer(config, args) {
// Write the server state // Write the server state
obj.updateServerState('state', 'starting'); obj.updateServerState('state', 'starting');
// Start memory tracking if requested
if (typeof obj.args.memorytracking == 'number') {
var info = process.memoryUsage(), txt = [];
info.time = Date.now();
for (var i in info) { txt.push(i); }
obj.fs.appendFile(obj.getConfigFilePath('memorytracking.txt'), txt.join(',') + '\r\n', function (err) { });
setInterval(function () {
var info = process.memoryUsage(), txt = [];
info.time = Date.now();
for (var i in info) { txt.push(info[i]); }
obj.fs.appendFile(obj.getConfigFilePath('memorytracking.txt'), txt.join(',') + '\r\n', function (err) { });
}, (obj.args.memorytracking * 1000));
}
// Look to see if data and/or file path is specified // Look to see if data and/or file path is specified
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; }

View File

@ -470,7 +470,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
switch (cmd) { switch (cmd) {
case 'help': { case 'help': {
r = 'Available commands: help, info, versions, args, resetserver, showconfig, usersessions, tasklimiter, setmaxtasks, cores, migrationagents, swarmstats, nodeconfig.'; r = 'Available commands: help, info, versions, args, resetserver, showconfig, usersessions, tasklimiter, setmaxtasks, cores,\r\n'
r += 'migrationagents, swarmstats, nodeconfig.';
break; break;
} }
case 'info': { case 'info': {
@ -580,6 +581,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
break; break;
} }
case 'heapdump': {
var heapdump = null;
try { heapdump = require('heapdump'); } catch (ex) { }
if (heapdump == null) {
r = 'Heapdump module not installed, run "npm install heapdump".';
} else {
heapdump.writeSnapshot(function (err, filename) {
if (err != null) {
try { ws.send(JSON.stringify({ action: 'serverconsole', value: 'Unable to write heapdump: ' + err })); } catch (ex) { }
} else {
try { ws.send(JSON.stringify({ action: 'serverconsole', value: 'Wrote heapdump at ' + filename })); } catch (ex) { }
}
});
}
break;
}
default: { // This is an unknown command, return an error message default: { // This is an unknown command, return an error message
r = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.'; r = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
break; break;
@ -1272,7 +1289,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
for (var i = 0; i < command.nodeids.length; i++) { for (var i = 0; i < command.nodeids.length; i++) {
obj.db.Get(command.nodeids[i], function (err, nodes) { obj.db.Get(command.nodeids[i], function (err, nodes) {
if (nodes.length != 1) return; if (nodes.length != 1) return;
var node = nodes[0]; const node = nodes[0];
// Check if already in the right mesh // Check if already in the right mesh
if (node.meshid == command.meshid) return; if (node.meshid == command.meshid) return;
@ -1281,21 +1298,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { if (obj.parent.meshes[node.meshid].mtype != obj.parent.meshes[command.meshid].mtype) return; } catch (e) { return; }; try { if (obj.parent.meshes[node.meshid].mtype != obj.parent.meshes[command.meshid].mtype) return; } catch (e) { return; };
// Make sure that we have rights on both source and destination mesh // Make sure that we have rights on both source and destination mesh
var sourceMeshRights = user.links[node.meshid].rights; const sourceMeshRights = user.links[node.meshid].rights;
var targetMeshRights = user.links[command.meshid].rights; const targetMeshRights = user.links[command.meshid].rights;
if (((sourceMeshRights & 4) == 0) || ((targetMeshRights & 4) == 0)) return; if (((sourceMeshRights & 4) == 0) || ((targetMeshRights & 4) == 0)) return;
// Perform the switch, start by saving the node with the new meshid. // Perform the switch, start by saving the node with the new meshid.
var oldMeshId = node.meshid; const oldMeshId = node.meshid;
node.meshid = command.meshid; node.meshid = command.meshid;
obj.db.Set(node); obj.db.Set(node);
// If the device is connected on this server, switch it now. // If the device is connected on this server, switch it now.
var agentSession = obj.parent.wsagents[node._id]; var agentSession = obj.parent.wsagents[node._id];
if (agentSession != null) { agentSession.dbMeshKey = command.meshid; agentSession.meshid = command.meshid.split('/')[2]; } if (agentSession != null) {
agentSession.dbMeshKey = command.meshid; // Switch the agent mesh
agentSession.meshid = command.meshid.split('/')[2]; // Switch the agent mesh
agentSession.sendUpdatedIntelAmtPolicy(); // Send the new Intel AMT policy
}
// Add the connection state // Add the connection state
var state = obj.parent.parent.GetConnectivityState(node._id); const state = obj.parent.parent.GetConnectivityState(node._id);
if (state) { if (state) {
node.conn = state.connectivity; node.conn = state.connectivity;
node.pwr = state.powerState; node.pwr = state.powerState;

1961
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.2.9-q", "version": "0.2.9-r",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",
@ -35,6 +35,7 @@
"express": "^4.16.4", "express": "^4.16.4",
"express-handlebars": "^3.0.0", "express-handlebars": "^3.0.0",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"heapdump": "^0.3.12",
"ipcheck": "^0.1.0", "ipcheck": "^0.1.0",
"meshcentral": "*", "meshcentral": "*",
"minimist": "^1.2.0", "minimist": "^1.2.0",
@ -42,6 +43,7 @@
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"node-forge": "^0.7.6", "node-forge": "^0.7.6",
"node-windows": "^0.1.14",
"otplib": "^10.0.1", "otplib": "^10.0.1",
"util.promisify": "^1.0.0", "util.promisify": "^1.0.0",
"ws": "^6.1.2", "ws": "^6.1.2",

View File

@ -23,7 +23,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
obj.legacyAgentConnections = {}; obj.legacyAgentConnections = {};
obj.migrationAgents = {}; obj.migrationAgents = {};
obj.agentActionCount = {}; obj.agentActionCount = {};
obj.stats = { blockedConnect: 0, connectCount: 0, clientCertConnectCount: 0, noCertConnectCount: 0, bytesIn: 0, bytesOut: 0, httpGetRequest: 0, pushedAgents: {}, close: 0, onclose: 0 } obj.stats = { blockedConnect: 0, connectCount: 0, clientCertConnectCount: 0, noCertConnectCount: 0, bytesIn: 0, bytesOut: 0, httpGetRequest: 0, pushedAgents: {}, close: 0, onclose: 0, agentType: {} }
const tls = require('tls'); const tls = require('tls');
const forge = require('node-forge'); const forge = require('node-forge');
const common = require('./common.js'); const common = require('./common.js');
@ -178,7 +178,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
if ((this.tag.clientCert == null) || (this.tag.clientCert.subject == null)) { if ((this.tag.clientCert == null) || (this.tag.clientCert.subject == null)) {
/*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/ /*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/
this.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.'); this.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.');
this.end(); //this.end(); // If we don't close the connection, it may lead to less reconnection traffic.
return; return;
} }
@ -209,6 +209,9 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
if (socket.pingTimer == null) { socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000); } if (socket.pingTimer == null) { socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000); }
Debug(3, 'Swarm:NODEPUSH:' + JSON.stringify(nodeblock)); Debug(3, 'Swarm:NODEPUSH:' + JSON.stringify(nodeblock));
// Log the agent type
if (obj.stats.agenttype[nodeblock.agenttype] == null) { obj.stats.agenttype[nodeblock.agenttype] = 1; } else { obj.stats.agenttype[nodeblock.agenttype]++; }
// Check if this agent is asking of updates over and over again. // Check if this agent is asking of updates over and over again.
var actionCount = obj.agentActionCount[nodeblock.nodeidhex]; var actionCount = obj.agentActionCount[nodeblock.nodeidhex];
if (actionCount == null) { actionCount = 0; } if (actionCount == null) { actionCount = 0; }
@ -230,7 +233,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
// Update stats // Update stats
if (obj.stats.pushedAgents[nodeblock.agenttype] == null) { obj.stats.pushedAgents[nodeblock.agenttype] = {}; } if (obj.stats.pushedAgents[nodeblock.agenttype] == null) { obj.stats.pushedAgents[nodeblock.agenttype] = {}; }
if (obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] == null) { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] = 0; } else { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion]++; } if (obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] == null) { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] = 1; } else { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion]++; }
// Start the agent download using the task limiter so not to flood the server. Low priority task // Start the agent download using the task limiter so not to flood the server. Low priority task
obj.parent.taskLimiter.launch(function (socket, taskid, taskLimiterQueue) { obj.parent.taskLimiter.launch(function (socket, taskid, taskLimiterQueue) {

File diff suppressed because one or more lines are too long

View File

@ -6359,7 +6359,7 @@
// Display the users using the sorted list // Display the users using the sorted list
var x = '<table style=width:100% cellpadding=0 cellspacing=0>', addHeader = true; var x = '<table style=width:100% cellpadding=0 cellspacing=0>', addHeader = true;
x += '<th style=color:gray>Name<th style=color:gray>Groups<th style=color:gray>Last Access<th style=color:gray>Permissions'; x += '<th style=color:gray>Name<th style=color:gray;width:80px>Groups<th style=color:gray;width:120px>Last&nbsp;Access<th style=color:gray;width:120px>Permissions';
// Online users // Online users
for (var i in sortedUserIds) { for (var i in sortedUserIds) {
@ -6408,12 +6408,12 @@
msg = "<span style=float:right;margin-top:1px;margin-right:4px title=Chat><a onclick=userChat(event,\"" + encodeURIComponent(user._id) + "\",\"" + encodeURIComponent(user.name) + "\")><img src='images/icon-chat.png' height=16 width=16 style=padding-top:2px /></a></span>"; msg = "<span style=float:right;margin-top:1px;margin-right:4px title=Chat><a onclick=userChat(event,\"" + encodeURIComponent(user._id) + "\",\"" + encodeURIComponent(user.name) + "\")><img src='images/icon-chat.png' height=16 width=16 style=padding-top:2px /></a></span>";
msg += "<span style=float:right;margin-top:1px;margin-left:4px;margin-right:4px title=Notify><a onclick=showUserAlertDialog(event,\"" + encodeURIComponent(user._id) + "\")><img src='images/icon-notify.png' height=16 width=16 style=padding-top:2px /></a></span>"; msg += "<span style=float:right;margin-top:1px;margin-left:4px;margin-right:4px title=Notify><a onclick=showUserAlertDialog(event,\"" + encodeURIComponent(user._id) + "\")><img src='images/icon-notify.png' height=16 width=16 style=padding-top:2px /></a></span>";
} }
if (sessions == 1) { lastAccess += '1 session'; } else { lastAccess += sessions + ' sessions'; } if (sessions == 1) { lastAccess += '1&nbsp;session'; } else { lastAccess += sessions + '&nbsp;sessions'; }
} else { } else {
if (user.login) { lastAccess += '<span title="Last login: ' + new Date(user.login * 1000).toLocaleString() + '">' + new Date(user.login * 1000).toLocaleDateString() + '</span>'; } if (user.login) { lastAccess += '<span title="Last login: ' + new Date(user.login * 1000).toLocaleString() + '">' + new Date(user.login * 1000).toLocaleDateString() + '</span>'; }
} }
if (self) { permissions += "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + encodeURIComponent(user._id) + "\")>"; } if (self) { permissions += "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + encodeURIComponent(user._id) + "\")>"; }
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked, "; } if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked,&nbsp;"; }
permissions += "<span title='Server Permissions'>"; permissions += "<span title='Server Permissions'>";
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) {
permissions += "User"; permissions += "User";