Added mesh agent crash dump management web page.

Ylian Saint-Hilaire committed Sep 17, 2020 at 15:39 UTC 53f3c418dcf51a047b57b1c85652d7beea884dff
4 files changed +85 -11
meshcentral-config-schema.json
+1
@@ -40,6 +40,7 @@
40 "agentAliasDNS": { "type": "string", "format": "hostname", "description": "When set, specified the DNS name used by agents to connect to the agent-only port." },
41 "agentPortTls": { "type": "boolean", "default": true, "description": "Indicates if the agent-only port must perform TLS, this should be set to false if TLS is performed in front of this server." },
42 "agentCoreDump": { "type": "boolean", "default": false, "description": "Automatically activates and transfers any agent crash dump files to the server in meshcentral-data/coredumps." },
43 + "agentCoreDumpUsers": { "type": "array", "description": "List of non-administrator users that have access to mesh agent crash dumps." },
44 "exactPorts": { "type": "boolean", "default": false },
45 "allowLoginToken": { "type": "boolean", "default": false },
46 "allowFraming": { "type": "boolean", "default": false, "description": "When enabled, the MeshCentral web site can be embedded within another website's iframe." },
meshdesktopmultiplex.js
+1 -1
@@ -486,7 +486,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
486 var cmd = Buffer.alloc(10);
487 cmd.writeUInt16BE(5, 0); // Command 5, compression
488 cmd.writeUInt16BE(10, 2); // Command size, 10 bytes long
489 - cmd[4] = 1; // Image type, 1 = JPEN
489 + cmd[4] = 1; // Image type, 1 = JPEG
490 cmd[5] = obj.imageCompression; // Image compression level
491 cmd.writeUInt16BE(obj.imageScaling, 6); // Scaling level
492 cmd.writeUInt16BE(obj.imageFrameRate, 8); // Frame rate timer
public/scripts/agent-desktop-0.0.2.js
+6 -6
@@ -100,7 +100,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
100 }
101
102 obj.send = function (x) {
103 - if (obj.debugmode > 1) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); }
103 + if (obj.debugmode > 2) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); }
104 if (obj.parent != null) { obj.parent.send(x); }
105 }
106
@@ -156,13 +156,13 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
156 }
157
158 obj.SendUnPause = function () {
159 - //obj.Debug("SendUnPause");
159 + if (obj.debugmode > 1) { console.log("SendUnPause"); }
160 //obj.xxStateChange(3);
161 obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
162 }
163
164 obj.SendPause = function () {
165 - //obj.Debug("SendPause");
165 + if (obj.debugmode > 1) { console.log("SendPause"); }
166 //obj.xxStateChange(2);
167 obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
168 }
@@ -196,7 +196,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
196 obj.ProcessBinaryCommand = function (cmd, cmdsize, view) {
197 var X, Y;
198 if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; }
199 - //console.log('CMD', cmd, cmdsize, X, Y);
199 + if (obj.debugmode > 2) { console.log('CMD', cmd, cmdsize, X, Y); }
200
201 // Record the command if needed
202 if (obj.recordedData != null) {
@@ -250,7 +250,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
250 break;
251 case 17: // MNG_KVM_MESSAGE
252 var str = String.fromCharCode.apply(null, data.slice(4));
253 - obj.Debug("Got KVM Message: " + str);
253 + console.log("Got KVM Message: " + str);
254 if (obj.onMessage != null) obj.onMessage(str, obj);
255 break;
256 case 65: // Alert
@@ -502,7 +502,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
502 if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
503 }
504 obj.FirstDraw = false;
505 - //obj.Debug("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight);
505 + if (obj.debugmode > 1) { console.log("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight); }
506 }
507
508 obj.xxMouseInputGrab = false;
webserver.js
+77 -4
@@ -4265,19 +4265,92 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4265 if (domain == null) return;
4266 if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
4267 if ((req.session == null) || (req.session.userid == null)) { res.sendStatus(404); return; }
4268 + var user = null, coreDumpsAllowed = false;
4269 + if (typeof req.session.userid == 'string') { user = obj.users[req.session.userid]; }
4270 + if (user == null) { res.sendStatus(404); return; }
4271 +
4272 + // Check if this user has access to agent core dumps
4273 + if ((obj.parent.config.settings.agentcoredump === true) && ((user.siteadmin == 0xFFFFFFFF) || ((Array.isArray(obj.parent.config.settings.agentcoredumpusers)) && (obj.parent.config.settings.agentcoredumpusers.indexOf(user._id) >= 0)))) {
4274 + coreDumpsAllowed = true;
4275 +
4276 + if ((req.query.dldump != null) && obj.common.IsFilenameValid(req.query.dldump)) {
4277 + // Download a dump file
4278 + var dumpFile = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', req.query.dldump);
4279 + if (obj.fs.existsSync(dumpFile)) {
4280 + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename="' + req.query.dldump + '' });
4281 + res.sendFile(dumpFile); return;
4282 + } else {
4283 + res.sendStatus(404); return;
4284 + }
4285 + }
4286 +
4287 + if ((req.query.deldump != null) && obj.common.IsFilenameValid(req.query.deldump)) {
4288 + // Delete a dump file
4289 + try { obj.fs.unlinkSync(obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', req.query.deldump)); } catch (ex) { console.log(ex); }
4290 + }
4291 +
4292 + if ((req.query.dumps != null) || (req.query.deldump != null)) {
4293 + // Send list of agent core dumps
4294 + var response = '<html><head><title>Mesh Agents Core Dumps</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body style=overflow:auto><table>';
4295 + response += '<tr style="background-color:lightgray"><th>ID</th><th>Upload Date</th><th>Description</th><th>Current</th><th>Dump</th><th>Size</th><th>Agent</th><th>Agent SHA384</th><th>NodeID</th><th></th></tr>';
4296 +
4297 + var coreDumpPath = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps');
4298 + if (obj.fs.existsSync(coreDumpPath)) {
4299 + var files = obj.fs.readdirSync(coreDumpPath);
4300 + var coredumps = [];
4301 + for (var i in files) {
4302 + var file = files[i];
4303 + if (file.endsWith('.dmp')) {
4304 + var fileSplit = file.substring(0, file.length - 4).split('-');
4305 + if (fileSplit.length == 3) {
4306 + var agentid = parseInt(fileSplit[0]);
4307 + if ((isNaN(agentid) == false) && (obj.parent.meshAgentBinaries[agentid] != null)) {
4308 + var agentinfo = obj.parent.meshAgentBinaries[agentid];
4309 + var filestats = obj.fs.statSync(obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', file));
4310 + coredumps.push({
4311 + fileSplit: fileSplit,
4312 + agentinfo: agentinfo,
4313 + filestats: filestats,
4314 + currentAgent: agentinfo.hashhex.startsWith(fileSplit[1].toLowerCase()),
4315 + downloadUrl: req.originalUrl.split('?')[0] + '?dldump=' + file + (req.query.key ? ('&key=' + req.query.key) : ''),
4316 + deleteUrl: req.originalUrl.split('?')[0] + '?deldump=' + file + (req.query.key ? ('&key=' + req.query.key) : ''),
4317 + agentUrl: req.originalUrl.split('?')[0] + '?id=' + agentinfo.id + (req.query.key ? ('&key=' + req.query.key) : ''),
4318 + time: new Date(filestats.ctime)
4319 + });
4320 + }
4321 + }
4322 + }
4323 + }
4324 + coredumps.sort(function (a, b) { if (a.time > b.time) return -1; if (a.time < b.time) return 1; return 0; });
4325 + for (var i in coredumps) {
4326 + var d = coredumps[i];
4327 + response += '<tr><td>' + d.agentinfo.id + '</td><td>' + d.time.toDateString().split(' ').join('&nbsp;') + '</td><td>' + d.agentinfo.desc.split(' ').join('&nbsp;') + '</td>';
4328 + response += '<td style=text-align:center>' + d.currentAgent + '</td><td><a download href="' + d.downloadUrl + '">Download</a></td><td style=text-align:right>' + d.filestats.size + '</td>';
4329 + if (d.currentAgent) { response += '<td><a download href="' + d.agentUrl + '">Download</a></td>'; } else { response += '<td></td>'; }
4330 + response += '<td>' + d.fileSplit[1].toLowerCase() + '</td><td>' + d.fileSplit[2] + '</td><td><a href="' + d.deleteUrl + '">Delete</a></td></tr>';
4331 + }
4332 + }
4333 + response += '</table><a href="' + req.originalUrl.split('?')[0] + (req.query.key ? ('?key=' + req.query.key) : '') + '">Mesh Agents</a></body></html>';
4334 + res.send(response);
4335 + return;
4336 + }
4337 + }
4338
4339 // Send a list of available mesh agents
4270 - var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
4340 + var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body style=overflow:auto><table>';
4341 response += '<tr style="background-color:lightgray"><th>ID</th><th>Description</th><th>Link</th><th>Size</th><th>SHA384</th><th>MeshCmd</th></tr>';
4342 + var originalUrl = req.originalUrl.split('?')[0];
4343 for (var agentid in obj.parent.meshAgentBinaries) {
4344 + if (agentid >= 10000) continue;
4345 var agentinfo = obj.parent.meshAgentBinaries[agentid];
4274 - var originalUrl = req.originalUrl.split('?')[0];
4275 - response += '<tr><td>' + agentinfo.id + '</td><td>' + agentinfo.desc + '</td>';
4346 + response += '<tr><td>' + agentinfo.id + '</td><td>' + agentinfo.desc.split(' ').join('&nbsp;') + '</td>';
4347 response += '<td><a download href="' + originalUrl + '?id=' + agentinfo.id + (req.query.key ? ('&key=' + req.query.key) : '') + '">' + agentinfo.rname + '</a></td>';
4348 response += '<td>' + agentinfo.size + '</td><td>' + agentinfo.hashhex + '</td>';
4349 response += '<td><a download href="' + originalUrl + '?meshcmd=' + agentinfo.id + (req.query.key ? ('&key=' + req.query.key) : '') + '">' + agentinfo.rname.replace('agent', 'cmd') + '</a></td></tr>';
4350 }
4280 - response += '</table></body></html>';
4351 + response += '</table>';
4352 + if (coreDumpsAllowed) { response += '<a href="' + originalUrl + '?dumps=1' + (req.query.key ? ('&key=' + req.query.key) : '') + '">MeshAgent Crash Dumps</a>'; }
4353 + response += '</body></html>';
4354 res.send(response);
4355 }
4356 };