Added server instrumentation

This commit is contained in:
Ylian Saint-Hilaire 2019-05-01 15:02:03 -07:00
parent f73276bdea
commit d387a0cc8b
7 changed files with 258 additions and 27 deletions

View File

@ -549,14 +549,47 @@ module.exports.CertificateOperations = function (parent) {
// Accelerators, used to dispatch work to other processes // Accelerators, used to dispatch work to other processes
const fork = require("child_process").fork; const fork = require("child_process").fork;
const program = require("path").join(__dirname, "meshaccelerator.js"); const program = require("path").join(__dirname, "meshaccelerator.js");
const acceleratorTotalCount = require("os").cpus().length; const acceleratorTotalCount = 1; //require("os").cpus().length; // TODO: Check if this accelerator can scale.
var acceleratorCreateCount = acceleratorTotalCount; var acceleratorCreateCount = acceleratorTotalCount;
var freeAccelerators = []; var freeAccelerators = [];
var pendingAccelerator = []; var pendingAccelerator = [];
obj.acceleratorCertStore = null; obj.acceleratorCertStore = null;
// Accelerator Stats
var getAcceleratorFuncCalls = 0;
var acceleratorStartFuncCall = 0;
var acceleratorPerformSignatureFuncCall = 0;
var acceleratorPerformSignaturePushFuncCall = 0;
var acceleratorPerformSignatureRunFuncCall = 0;
var acceleratorMessage = 0;
var acceleratorMessageException = 0;
var acceleratorMessageLastException = null;
var acceleratorException = 0;
var acceleratorLastException = null;
// Get stats about the accelerators
obj.getAcceleratorStats = function () {
return {
acceleratorTotalCount: acceleratorTotalCount,
acceleratorCreateCount: acceleratorCreateCount,
freeAccelerators: freeAccelerators.length,
pendingAccelerator: pendingAccelerator.length,
getAcceleratorFuncCalls: getAcceleratorFuncCalls,
startFuncCall: acceleratorStartFuncCall,
performSignatureFuncCall: acceleratorPerformSignatureFuncCall,
performSignaturePushFuncCall: acceleratorPerformSignaturePushFuncCall,
performSignatureRunFuncCall: acceleratorPerformSignatureRunFuncCall,
message: acceleratorMessage,
messageException: acceleratorMessageException,
messageLastException: acceleratorMessageLastException,
exception: acceleratorException,
lastException: acceleratorLastException
};
}
// Create a new accelerator module // Create a new accelerator module
obj.getAccelerator = function () { obj.getAccelerator = function () {
getAcceleratorFuncCalls++;
if (obj.acceleratorCertStore == null) { return null; } if (obj.acceleratorCertStore == null) { return null; }
if (freeAccelerators.length > 0) { return freeAccelerators.pop(); } if (freeAccelerators.length > 0) { return freeAccelerators.pop(); }
if (acceleratorCreateCount > 0) { if (acceleratorCreateCount > 0) {
@ -564,23 +597,26 @@ module.exports.CertificateOperations = function (parent) {
var accelerator = fork(program, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] }); var accelerator = fork(program, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
accelerator.accid = acceleratorCreateCount; accelerator.accid = acceleratorCreateCount;
accelerator.on("message", function (message) { accelerator.on("message", function (message) {
this.func(this.tag, message); acceleratorMessage++;
try { this.func(this.tag, message); } catch (ex) { acceleratorMessageException++; acceleratorMessageLastException = ex; }
try {
delete this.tag; delete this.tag;
if (pendingAccelerator.length > 0) { if (pendingAccelerator.length > 0) {
var x = pendingAccelerator.shift(); var x = pendingAccelerator.shift();
if (x.tag) { this.tag = x.tag; delete x.tag; } if (x.tag) { this.tag = x.tag; delete x.tag; }
accelerator.send(x); accelerator.send(x);
} else { freeAccelerators.push(this); } } else { freeAccelerators.push(this); }
} catch (ex) { acceleratorException++; acceleratorLastException = ex; }
}); });
accelerator.send({ action: "setState", certs: obj.acceleratorCertStore }); accelerator.send({ action: "setState", certs: obj.acceleratorCertStore });
return accelerator; return accelerator;
} }
return null; return null;
}; };
// Set the state of the accelerators. This way, we don"t have to send certificate & keys to them each time. // Set the state of the accelerators. This way, we don"t have to send certificate & keys to them each time.
obj.acceleratorStart = function (certificates) { obj.acceleratorStart = function (certificates) {
acceleratorStartFuncCall++;
if (obj.acceleratorCertStore != null) { console.error("ERROR: Accelerators can only be started once."); return; } if (obj.acceleratorCertStore != null) { console.error("ERROR: Accelerators can only be started once."); return; }
obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }]; obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }];
if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); } if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); }
@ -588,19 +624,22 @@ module.exports.CertificateOperations = function (parent) {
// Perform any RSA signature, just pass in the private key and data. // Perform any RSA signature, just pass in the private key and data.
obj.acceleratorPerformSignature = function (privatekey, data, tag, func) { obj.acceleratorPerformSignature = function (privatekey, data, tag, func) {
acceleratorPerformSignatureFuncCall++;
if (acceleratorTotalCount <= 1) { if (acceleratorTotalCount <= 1) {
// No accelerators available // No accelerators available
if (typeof privatekey == "number") { privatekey = obj.acceleratorCertStore[privatekey].key; } if (typeof privatekey == "number") { privatekey = obj.acceleratorCertStore[privatekey].key; }
const sign = obj.crypto.createSign("SHA384"); const sign = obj.crypto.createSign("SHA384");
sign.end(Buffer.from(data, "binary")); sign.end(Buffer.from(data, "binary"));
func(tag, sign.sign(privatekey).toString("binary")); try { func(tag, sign.sign(privatekey).toString("binary")); } catch (ex) { acceleratorMessageException++; acceleratorMessageLastException = ex; }
} else { } else {
var acc = obj.getAccelerator(); var acc = obj.getAccelerator();
if (acc == null) { if (acc == null) {
// Add to pending accelerator workload // Add to pending accelerator workload
acceleratorPerformSignaturePushFuncCall++;
pendingAccelerator.push({ action: "sign", key: privatekey, data: data, tag: tag }); pendingAccelerator.push({ action: "sign", key: privatekey, data: data, tag: tag });
} else { } else {
// Send to accelerator now // Send to accelerator now
acceleratorPerformSignatureRunFuncCall++;
acc.func = func; acc.func = func;
acc.tag = tag; acc.tag = tag;
acc.send({ action: "sign", key: privatekey, data: data }); acc.send({ action: "sign", key: privatekey, data: data });

View File

@ -19,6 +19,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
const forge = parent.parent.certificateOperations.forge; const forge = parent.parent.certificateOperations.forge;
const common = parent.parent.common; const common = parent.parent.common;
const agentUpdateBlockSize = 65531; const agentUpdateBlockSize = 65531;
parent.agentStats.createMeshAgentCount++;
var obj = {}; var obj = {};
obj.domain = domain; obj.domain = domain;
@ -155,6 +156,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (meshcorehash == null) { if (meshcorehash == null) {
// Clear the core // Clear the core
obj.send(common.ShortToStr(10) + common.ShortToStr(0)); // MeshCommand_CoreModule, ask mesh agent to clear the core obj.send(common.ShortToStr(10) + common.ShortToStr(0)); // MeshCommand_CoreModule, ask mesh agent to clear the core
parent.agentStats.clearingCoreCount++;
parent.parent.debug(1, 'Clearing core'); parent.parent.debug(1, 'Clearing core');
} else { } else {
// Update new core // Update new core
@ -168,7 +170,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Send the updated code. // Send the updated code.
delete obj.agentCoreUpdatePending; delete obj.agentCoreUpdatePending;
obj.send(common.ShortToStr(10) + common.ShortToStr(0) + argument.hash + argument.core, function () { parent.parent.taskLimiter.completed(taskid); }); // MeshCommand_CoreModule, start core update obj.send(common.ShortToStr(10) + common.ShortToStr(0) + argument.hash + argument.core, function () { parent.parent.taskLimiter.completed(taskid); }); // MeshCommand_CoreModule, start core update
parent.parent.debug(1, 'Updating code ' + argument.name); parent.agentStats.updatingCoreCount++;
parent.parent.debug(1, 'Updating core ' + argument.name);
agentCoreIsStable(); agentCoreIsStable();
} else { } else {
// This agent is probably disconnected, nothing to do. // This agent is probably disconnected, nothing to do.
@ -359,6 +362,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
} else { } else {
// Check that the server hash matches our own web certificate hash (SHA384) // Check that the server hash matches our own web certificate hash (SHA384)
if ((getWebCertHash(domain) != msg.substring(2, 50)) && (getWebCertFullHash(domain) != msg.substring(2, 50))) { if ((getWebCertHash(domain) != msg.substring(2, 50)) && (getWebCertFullHash(domain) != msg.substring(2, 50))) {
parent.agentStats.agentBadWebCertHashCount++;
console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').'); console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').');
console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.'); console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.');
return; return;
@ -388,7 +392,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Check the agent signature if we can // Check the agent signature if we can
if (obj.unauthsign != null) { if (obj.unauthsign != null) {
if (processAgentSignature(obj.unauthsign) == false) { console.log('Agent connected with bad signature, holding connection (' + obj.remoteaddrport + ').'); return; } else { completeAgentConnection(); } if (processAgentSignature(obj.unauthsign) == false) {
parent.agentStats.agentBadSignature1Count++;
console.log('Agent connected with bad signature, holding connection (' + obj.remoteaddrport + ').'); return;
} else { completeAgentConnection(); }
} }
} }
else if (cmd == 2) { else if (cmd == 2) {
@ -403,7 +410,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + Buffer.from(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----'; obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + Buffer.from(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----';
// Check the agent signature if we can // Check the agent signature if we can
if (obj.agentnonce == null) { obj.unauthsign = msg.substring(4 + certlen); } else { if (processAgentSignature(msg.substring(4 + certlen)) == false) { console.log('Agent connected with bad signature, holding connection (' + obj.remoteaddrport + ').'); return; } } if (obj.agentnonce == null) { obj.unauthsign = msg.substring(4 + certlen); } else {
if (processAgentSignature(msg.substring(4 + certlen)) == false) {
parent.agentStats.agentBadSignature2Count++;
console.log('Agent connected with bad signature, holding connection (' + obj.remoteaddrport + ').'); return;
}
}
completeAgentConnection(); completeAgentConnection();
} }
else if (cmd == 3) { else if (cmd == 3) {
@ -541,7 +553,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
for (var i in parent.wsagents) { if (parent.wsagents[i].domain.id == domain.id) { domainAgentSessionCount++; } } for (var i in parent.wsagents) { if (parent.wsagents[i].domain.id == domain.id) { domainAgentSessionCount++; } }
// Check if we have too many user sessions // Check if we have too many user sessions
if (domainAgentSessionCount >= domain.limits.maxagentsessions) { return; } // Too many, hold the connection. if (domainAgentSessionCount >= domain.limits.maxagentsessions) {
// Too many, hold the connection.
parent.agentStats.agentMaxSessionHoldCount++;
return;
}
} }
/* /*
@ -593,6 +609,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Check if the mesh exists // Check if the mesh exists
if (mesh == null) { if (mesh == null) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours. // If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent.agentStats.invalidDomainMeshCount++;
console.log('Agent connected with invalid domain/mesh, holding connection (' + obj.remoteaddrport + ', ' + obj.dbMeshKey + ').'); console.log('Agent connected with invalid domain/mesh, holding connection (' + obj.remoteaddrport + ', ' + obj.dbMeshKey + ').');
return; return;
} }
@ -600,6 +617,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Check if the mesh is the right type // Check if the mesh is the right type
if (mesh.mtype != 2) { if (mesh.mtype != 2) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours. // If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent.agentStats.invalidMeshTypeCount++;
console.log('Agent connected with invalid mesh type, holding connection (' + obj.remoteaddrport + ').'); console.log('Agent connected with invalid mesh type, holding connection (' + obj.remoteaddrport + ').');
return; return;
} }
@ -634,6 +652,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Check if the mesh exists // Check if the mesh exists
if (mesh == null) { if (mesh == null) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours. // If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent.agentStats.invalidDomainMesh2Count++;
console.log('Agent connected with invalid domain/mesh, holding connection (' + obj.remoteaddrport + ', ' + obj.dbMeshKey + ').'); console.log('Agent connected with invalid domain/mesh, holding connection (' + obj.remoteaddrport + ', ' + obj.dbMeshKey + ').');
return; return;
} }
@ -641,6 +660,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Check if the mesh is the right type // Check if the mesh is the right type
if (mesh.mtype != 2) { if (mesh.mtype != 2) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours. // If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent.agentStats.invalidMeshType2Count++;
console.log('Agent connected with invalid mesh type, holding connection (' + obj.remoteaddrport + ').'); console.log('Agent connected with invalid mesh type, holding connection (' + obj.remoteaddrport + ').');
return; return;
} }
@ -686,6 +706,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
parent.wsagents[obj.dbNodeKey] = obj; parent.wsagents[obj.dbNodeKey] = obj;
if (dupAgent) { if (dupAgent) {
// Close the duplicate agent // Close the duplicate agent
parent.agentStats.duplicateAgentCount++;
if (obj.nodeid != null) { parent.parent.debug(1, 'Duplicate agent ' + obj.nodeid + ' (' + obj.remoteaddrport + ')'); } if (obj.nodeid != null) { parent.parent.debug(1, 'Duplicate agent ' + obj.nodeid + ' (' + obj.remoteaddrport + ')'); }
dupAgent.close(3); dupAgent.close(3);
} else { } else {
@ -769,6 +790,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
} }
function recoveryAgentCoreIsStable(mesh) { function recoveryAgentCoreIsStable(mesh) {
parent.agentStats.recoveryCoreIsStableCount++;
// Recovery agent is doing ok, lets perform main agent checking. // Recovery agent is doing ok, lets perform main agent checking.
//console.log('recoveryAgentCoreIsStable()'); //console.log('recoveryAgentCoreIsStable()');
@ -793,9 +816,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
} }
function agentCoreIsStable() { function agentCoreIsStable() {
parent.agentStats.coreIsStableCount++;
// Check that the mesh exists // Check that the mesh exists
const mesh = parent.meshes[obj.dbMeshKey]; const mesh = parent.meshes[obj.dbMeshKey];
if (mesh == null) { if (mesh == null) {
parent.agentStats.meshDoesNotExistCount++;
// TODO: Mark this agent as part of a mesh that does not exists. // TODO: Mark this agent as part of a mesh that does not exists.
return; // Probably not worth doing anything else. Hold this agent. return; // Probably not worth doing anything else. Hold this agent.
} }
@ -806,7 +832,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
return; return;
} }
// Fetch the the real agent nodeid // Fetch the the diagnostic agent nodeid
db.Get('ra' + obj.dbNodeKey, function (err, nodes) { db.Get('ra' + obj.dbNodeKey, function (err, nodes) {
if (nodes.length == 1) { if (nodes.length == 1) {
obj.diagnosticNodeKey = nodes[0].daid; obj.diagnosticNodeKey = nodes[0].daid;
@ -902,7 +928,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
verifier.update(buf); verifier.update(buf);
verified = verifier.verify(obj.unauth.nodeCertPem, sig, 'binary'); verified = verifier.verify(obj.unauth.nodeCertPem, sig, 'binary');
} }
if (verified == false) { return false; } // Not a valid signature if (verified == false) {
// Not a valid signature
parent.agentStats.invalidPkcsSignatureCount++;
return false;
}
} catch (ex) { }; } catch (ex) { };
} }
} }
@ -914,7 +944,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (verify.verify(obj.unauth.nodeCertPem, Buffer.from(msg, 'binary')) !== true) { if (verify.verify(obj.unauth.nodeCertPem, Buffer.from(msg, 'binary')) !== true) {
const verify2 = parent.crypto.createVerify('SHA384'); const verify2 = parent.crypto.createVerify('SHA384');
verify2.end(Buffer.from(getWebCertFullHash(domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the full cert hash verify2.end(Buffer.from(getWebCertFullHash(domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the full cert hash
if (verify2.verify(obj.unauth.nodeCertPem, Buffer.from(msg, 'binary')) !== true) { return false; } if (verify2.verify(obj.unauth.nodeCertPem, Buffer.from(msg, 'binary')) !== true) {
parent.agentStats.invalidRsaSignatureCount++;
return false;
}
} }
} }
} }
@ -927,6 +960,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
delete obj.unauth; delete obj.unauth;
delete obj.receivedCommands; delete obj.receivedCommands;
if (obj.unauthsign) delete obj.unauthsign; if (obj.unauthsign) delete obj.unauthsign;
parent.agentStats.verifiedAgentConnectionCount++;
parent.parent.debug(1, 'Verified agent connection to ' + obj.nodeid + ' (' + obj.remoteaddrport + ').'); parent.parent.debug(1, 'Verified agent connection to ' + obj.nodeid + ' (' + obj.remoteaddrport + ').');
obj.authenticated = 1; obj.authenticated = 1;
return true; return true;
@ -936,7 +970,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
function processAgentData(msg) { function processAgentData(msg) {
var i, str = msg.toString('utf8'), command = null; var i, str = msg.toString('utf8'), command = null;
if (str[0] == '{') { if (str[0] == '{') {
try { command = JSON.parse(str); } catch (ex) { console.log('Unable to parse agent JSON (' + obj.remoteaddrport + '): ' + str, ex); return; } // If the command can't be parsed, ignore it. try { command = JSON.parse(str); } catch (ex) { parent.agentStats.invalidJsonCount++; console.log('Unable to parse agent JSON (' + obj.remoteaddrport + '): ' + str, ex); return; } // If the command can't be parsed, ignore it.
if (typeof command != 'object') { return; } if (typeof command != 'object') { return; }
switch (command.action) { switch (command.action) {
case 'msg': case 'msg':
@ -1159,6 +1193,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
break; break;
} }
default: { default: {
parent.agentStats.unknownAgentActionCount++;
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.'); console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
break; break;
} }

View File

@ -216,7 +216,7 @@ function CreateMeshCentralServer(config, args) {
if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } } if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } }
callback(obj.currentVer, latestVer); callback(obj.currentVer, latestVer);
}); });
} catch (ex) { callback(obj.currentVer, null); } // If the system is running out of memory, an exception here can easily happen. } catch (ex) { callback(obj.currentVer, null, ex); } // If the system is running out of memory, an exception here can easily happen.
}; };
// Initiate server self-update // Initiate server self-update

View File

@ -520,7 +520,50 @@ 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,\r\n' r = 'Available commands: help, info, versions, args, resetserver, showconfig, usersessions, tasklimiter, setmaxtasks, cores,\r\n'
r += 'migrationagents, swarmstats, nodeconfig, heapdump, relays.'; r += 'migrationagents, agentstats, webstats, mpsstats, swarmstats, acceleratorsstats, updatecheck, serverupdate, nodeconfig, heapdump, relays.';
break;
}
case 'agentstats': {
var stats = parent.getAgentStats();
for (var i in stats) {
if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); }
}
break;
}
case 'webstats': {
var stats = parent.getStats();
for (var i in stats) {
if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); }
}
break;
}
case 'acceleratorsstats': {
var stats = parent.parent.certificateOperations.getAcceleratorStats();
for (var i in stats) {
if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); }
}
break;
}
case 'mpsstats': {
var stats = parent.parent.mpsserver.getStats();
for (var i in stats) {
if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); }
}
break;
}
case 'serverupdate': {
r = 'Performing server update...';
parent.parent.performServerUpdate();
break;
}
case 'updatecheck': {
parent.parent.getLatestServerVersion(function (currentVer, newVer, error) {
var r2 = 'Current Version: ' + currentVer + '\r\n';
if (newVer != null) { r2 += 'Available Version: ' + newVer + '\r\n'; }
if (error != null) { r2 += 'Exception: ' + ex + '\r\n'; }
try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { }
});
r = 'Checking server update...';
break; break;
} }
case 'info': { case 'info': {
@ -622,9 +665,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} else { } else {
for (var i in parent.parent.swarmserver.stats) { for (var i in parent.parent.swarmserver.stats) {
if (typeof parent.parent.swarmserver.stats[i] == 'object') { if (typeof parent.parent.swarmserver.stats[i] == 'object') {
r += i + ' ' + JSON.stringify(parent.parent.swarmserver.stats[i]) + '<br />'; r += i + ': ' + JSON.stringify(parent.parent.swarmserver.stats[i]) + '\r\n';
} else { } else {
r += i + ' ' + parent.parent.swarmserver.stats[i] + '<br />'; r += i + ': ' + parent.parent.swarmserver.stats[i] + '\r\n';
} }
} }
} }

View File

@ -106,7 +106,58 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
ResourceShortage: 4, ResourceShortage: 4,
}; };
// Stat counters
var connectionCount = 0;
var userAuthRequestCount = 0;
var incorrectPasswordCount = 0;
var meshNotFoundCount = 0;
var unknownTlsNodeCount = 0;
var unknownTlsMeshIdCount = 0;
var addedTlsDeviceCount = 0;
var unknownNodeCount = 0;
var unknownMeshIdCount = 0;
var addedDeviceCount = 0;
var ciraTimeoutCount = 0;
var protocolVersionCount = 0;
var badUserNameLengthCount = 0;
var channelOpenCount = 0;
var channelOpenConfirmCount = 0;
var channelOpenFailCount = 0;
var channelCloseCount = 0;
var disconnectCommandCount = 0;
var socketClosedCount = 0;
var socketErrorCount = 0;
// Return statistics about this MPS server
obj.getStats = function () {
return {
ciraConnections: Object.keys(obj.ciraConnections).length,
tlsSessionStore: Object.keys(tlsSessionStore).length,
connectionCount: connectionCount,
userAuthRequestCount: userAuthRequestCount,
incorrectPasswordCount: incorrectPasswordCount,
meshNotFoundCount: meshNotFoundCount,
unknownTlsNodeCount: unknownTlsNodeCount,
unknownTlsMeshIdCount: unknownTlsMeshIdCount,
addedTlsDeviceCount: addedTlsDeviceCount,
unknownNodeCount: unknownNodeCount,
unknownMeshIdCount: unknownMeshIdCount,
addedDeviceCount: addedDeviceCount,
ciraTimeoutCount: ciraTimeoutCount,
protocolVersionCount: protocolVersionCount,
badUserNameLengthCount: badUserNameLengthCount,
channelOpenCount: channelOpenCount,
channelOpenConfirmCount: channelOpenConfirmCount,
channelOpenFailCount: channelOpenFailCount,
channelCloseCount: channelCloseCount,
disconnectCommandCount: disconnectCommandCount,
socketClosedCount: socketClosedCount,
socketErrorCount: socketErrorCount
};
}
function onConnection(socket) { function onConnection(socket) {
connectionCount++;
if (obj.args.mpstlsoffload) { if (obj.args.mpstlsoffload) {
socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
} else { } else {
@ -117,7 +168,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
// Setup the CIRA keep alive timer // Setup the CIRA keep alive timer
socket.setTimeout(MAX_IDLE); socket.setTimeout(MAX_IDLE);
socket.on("timeout", () => { Debug(1, "MPS:CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } }); socket.on("timeout", () => { ciraTimeoutCount++; Debug(1, "MPS:CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } });
socket.addListener("data", function (data) { socket.addListener("data", function (data) {
if (args.mpsdebug) { var buf = Buffer.from(data, "binary"); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes if (args.mpsdebug) { var buf = Buffer.from(data, "binary"); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes
@ -156,12 +207,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.db.Set(device); obj.db.Set(device);
// Event the new node // Event the new node
addedTlsDeviceCount++;
var device2 = common.Clone(device); var device2 = common.Clone(device);
if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name; var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid }); obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid });
} else { } else {
// New CIRA connection for unknown node, disconnect. // New CIRA connection for unknown node, disconnect.
unknownTlsNodeCount++;
console.log('CIRA connection for unknown node with incorrect group type. meshid: ' + socket.tag.meshid); console.log('CIRA connection for unknown node with incorrect group type. meshid: ' + socket.tag.meshid);
socket.end(); socket.end();
return; return;
@ -177,6 +230,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll. obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
}); });
} else { } else {
unknownTlsMeshIdCount++;
console.log('ERROR: Intel AMT CIRA connected with unknown groupid: ' + socket.tag.meshid); console.log('ERROR: Intel AMT CIRA connected with unknown groupid: ' + socket.tag.meshid);
socket.end(); socket.end();
return; return;
@ -219,6 +273,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
} }
case APFProtocol.PROTOCOLVERSION: { case APFProtocol.PROTOCOLVERSION: {
if (len < 93) return 0; if (len < 93) return 0;
protocolVersionCount++;
socket.tag.MajorVersion = common.ReadInt(data, 1); socket.tag.MajorVersion = common.ReadInt(data, 1);
socket.tag.MinorVersion = common.ReadInt(data, 5); socket.tag.MinorVersion = common.ReadInt(data, 5);
socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase(); socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase();
@ -227,6 +282,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
} }
case APFProtocol.USERAUTH_REQUEST: { case APFProtocol.USERAUTH_REQUEST: {
if (len < 13) return 0; if (len < 13) return 0;
userAuthRequestCount++;
var usernameLen = common.ReadInt(data, 1); var usernameLen = common.ReadInt(data, 1);
var username = data.substring(5, 5 + usernameLen); var username = data.substring(5, 5 + usernameLen);
var serviceNameLen = common.ReadInt(data, 5 + usernameLen); var serviceNameLen = common.ReadInt(data, 5 + usernameLen);
@ -242,13 +298,13 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
Debug(3, 'MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); Debug(3, 'MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
// Check the CIRA password // Check the CIRA password
if ((args.mpspass != null) && (password != args.mpspass)) { Debug(1, 'MPS:Incorrect password', username, password); SendUserAuthFail(socket); return -1; } if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; Debug(1, 'MPS:Incorrect password', username, password); SendUserAuthFail(socket); return -1; }
// Check the CIRA username, which should be the start of the MeshID. // Check the CIRA username, which should be the start of the MeshID.
if (usernameLen != 16) { Debug(1, 'MPS:Username length not 16', username, password); SendUserAuthFail(socket); return -1; } if (usernameLen != 16) { badUserNameLengthCount++; Debug(1, 'MPS:Username length not 16', username, password); SendUserAuthFail(socket); return -1; }
var meshIdStart = '/' + username, mesh = null; var meshIdStart = '/' + username, mesh = null;
if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } } if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } }
if (mesh == null) { Debug(1, 'MPS:Mesh not found', username, password); SendUserAuthFail(socket); return -1; } if (mesh == null) { meshNotFoundCount++; Debug(1, 'MPS:Mesh not found', username, password); SendUserAuthFail(socket); return -1; }
// If this is a agent-less mesh, use the device guid 3 times as ID. // If this is a agent-less mesh, use the device guid 3 times as ID.
if (mesh.mtype == 1) { if (mesh.mtype == 1) {
@ -267,6 +323,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.db.Set(device); obj.db.Set(device);
// Event the new node // Event the new node
addedDeviceCount++;
var device2 = common.Clone(device); var device2 = common.Clone(device);
if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name; var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name;
@ -287,6 +344,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.db.getAmtUuidNode(mesh._id, socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes obj.db.getAmtUuidNode(mesh._id, socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes
if (nodes.length !== 1) { if (nodes.length !== 1) {
// New CIRA connection for unknown node, disconnect. // New CIRA connection for unknown node, disconnect.
unknownNodeCount++;
console.log('CIRA connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); console.log('CIRA connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId);
socket.end(); socket.end();
return; return;
@ -306,6 +364,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
}); });
} else { // Unknown mesh type } else { // Unknown mesh type
// New CIRA connection for unknown node, disconnect. // New CIRA connection for unknown node, disconnect.
unknownMeshIdCount++;
console.log('CIRA connection to a unknown group type. groupid: ' + socket.tag.meshid); console.log('CIRA connection to a unknown group type. groupid: ' + socket.tag.meshid);
socket.end(); socket.end();
return; return;
@ -393,6 +452,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen); var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen);
var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen); var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen);
channelOpenCount++;
Debug(3, 'MPS:CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort); Debug(3, 'MPS:CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort);
// Check if we understand this channel type // Check if we understand this channel type
@ -423,6 +483,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
if (cirachannel == null) { /*console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17; } if (cirachannel == null) { /*console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17; }
cirachannel.amtchannelid = SenderChannel; cirachannel.amtchannelid = SenderChannel;
cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize; cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize;
channelOpenConfirmCount++;
Debug(3, 'MPS:CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize); Debug(3, 'MPS:CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize);
if (cirachannel.closing == 1) { if (cirachannel.closing == 1) {
// Close this channel // Close this channel
@ -454,6 +515,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
if (len < 17) return 0; if (len < 17) return 0;
var RecipientChannel = common.ReadInt(data, 1); var RecipientChannel = common.ReadInt(data, 1);
var ReasonCode = common.ReadInt(data, 5); var ReasonCode = common.ReadInt(data, 5);
channelOpenFailCount++;
Debug(3, 'MPS:CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode); Debug(3, 'MPS:CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode);
var cirachannel = socket.tag.channels[RecipientChannel]; var cirachannel = socket.tag.channels[RecipientChannel];
if (cirachannel == null) { console.log("MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return 17; } if (cirachannel == null) { console.log("MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return 17; }
@ -468,6 +530,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
{ {
if (len < 5) return 0; if (len < 5) return 0;
var RecipientChannel = common.ReadInt(data, 1); var RecipientChannel = common.ReadInt(data, 1);
channelCloseCount++;
Debug(3, 'MPS:CHANNEL_CLOSE', RecipientChannel); Debug(3, 'MPS:CHANNEL_CLOSE', RecipientChannel);
var cirachannel = socket.tag.channels[RecipientChannel]; var cirachannel = socket.tag.channels[RecipientChannel];
if (cirachannel == null) { console.log("MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return 5; } if (cirachannel == null) { console.log("MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return 5; }
@ -526,6 +589,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
{ {
if (len < 7) return 0; if (len < 7) return 0;
var ReasonCode = common.ReadInt(data, 1); var ReasonCode = common.ReadInt(data, 1);
disconnectCommandCount++;
Debug(3, 'MPS:DISCONNECT', ReasonCode); Debug(3, 'MPS:DISCONNECT', ReasonCode);
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
@ -540,12 +604,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
} }
socket.addListener("close", function () { socket.addListener("close", function () {
socketClosedCount++;
Debug(1, 'MPS:CIRA connection closed'); Debug(1, 'MPS:CIRA connection closed');
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
}); });
socket.addListener("error", function () { socket.addListener("error", function () {
socketErrorCount++;
//console.log("MPS Error: " + socket.remoteAddress); //console.log("MPS Error: " + socket.remoteAddress);
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.3.3-n", "version": "0.3.3-o",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View File

@ -214,6 +214,54 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}); });
}); });
// Return statistics about this web server
obj.getStats = function () {
return {
users: Object.keys(obj.users).length,
meshes: Object.keys(obj.meshes).length,
dnsDomains: Object.keys(obj.dnsDomains).length,
relaySessionCount: obj.relaySessionCount,
relaySessionErrorCount: obj.relaySessionErrorCount,
wsagents: Object.keys(obj.wsagents).length,
wsagentsDisconnections: Object.keys(obj.wsagentsDisconnections).length,
wsagentsDisconnectionsTimer: Object.keys(obj.wsagentsDisconnectionsTimer).length,
wssessions: Object.keys(obj.wssessions).length,
wssessions2: Object.keys(obj.wssessions2).length,
wsPeerSessions: Object.keys(obj.wsPeerSessions).length,
wsPeerSessions2: Object.keys(obj.wsPeerSessions2).length,
wsPeerSessions3: Object.keys(obj.wsPeerSessions3).length,
sessionsCount: Object.keys(obj.sessionsCount).length,
wsrelays: Object.keys(obj.wsrelays).length,
wsPeerRelays: Object.keys(obj.wsPeerRelays).length,
tlsSessionStore: Object.keys(tlsSessionStore).length
};
}
// Agent counters
obj.agentStats = {
createMeshAgentCount: 0,
coreIsStableCount: 0,
verifiedAgentConnectionCount: 0,
clearingCoreCount: 0,
updatingCoreCount: 0,
recoveryCoreIsStableCount: 0,
meshDoesNotExistCount: 0,
invalidPkcsSignatureCount: 0,
invalidRsaSignatureCount: 0,
invalidJsonCount: 0,
unknownAgentActionCount: 0,
agentBadWebCertHashCount: 0,
agentBadSignature1Count: 0,
agentBadSignature2Count: 0,
agentMaxSessionHoldCount: 0,
invalidDomainMeshCount: 0,
invalidMeshTypeCount: 0,
invalidDomainMesh2Count: 0,
invalidMeshType2Count: 0,
duplicateAgentCount: 0
}
obj.getAgentStats = function () { return obj.agentStats; }
// Authenticate the user // Authenticate the user
obj.authenticate = function (name, pass, domain, fn) { obj.authenticate = function (name, pass, domain, fn) {
if ((typeof (name) != 'string') || (typeof (pass) != 'string') || (typeof (domain) != 'object')) { fn(new Error('invalid fields')); return; } if ((typeof (name) != 'string') || (typeof (pass) != 'string') || (typeof (domain) != 'object')) { fn(new Error('invalid fields')); return; }