From 53f3c418dcf51a047b57b1c85652d7beea884dff Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 17 Sep 2020 15:39:33 -0700 Subject: [PATCH] Added mesh agent crash dump management web page. --- meshcentral-config-schema.json | 1 + meshdesktopmultiplex.js | 2 +- public/scripts/agent-desktop-0.0.2.js | 12 ++-- webserver.js | 81 +++++++++++++++++++++++++-- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 946d38d3..c2922f7f 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -40,6 +40,7 @@ "agentAliasDNS": { "type": "string", "format": "hostname", "description": "When set, specified the DNS name used by agents to connect to the agent-only port." }, "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." }, "agentCoreDump": { "type": "boolean", "default": false, "description": "Automatically activates and transfers any agent crash dump files to the server in meshcentral-data/coredumps." }, + "agentCoreDumpUsers": { "type": "array", "description": "List of non-administrator users that have access to mesh agent crash dumps." }, "exactPorts": { "type": "boolean", "default": false }, "allowLoginToken": { "type": "boolean", "default": false }, "allowFraming": { "type": "boolean", "default": false, "description": "When enabled, the MeshCentral web site can be embedded within another website's iframe." }, diff --git a/meshdesktopmultiplex.js b/meshdesktopmultiplex.js index a0f40b0a..35a33b50 100644 --- a/meshdesktopmultiplex.js +++ b/meshdesktopmultiplex.js @@ -486,7 +486,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) { var cmd = Buffer.alloc(10); cmd.writeUInt16BE(5, 0); // Command 5, compression cmd.writeUInt16BE(10, 2); // Command size, 10 bytes long - cmd[4] = 1; // Image type, 1 = JPEN + cmd[4] = 1; // Image type, 1 = JPEG cmd[5] = obj.imageCompression; // Image compression level cmd.writeUInt16BE(obj.imageScaling, 6); // Scaling level cmd.writeUInt16BE(obj.imageFrameRate, 8); // Frame rate timer diff --git a/public/scripts/agent-desktop-0.0.2.js b/public/scripts/agent-desktop-0.0.2.js index ab2b31b0..8cc36515 100644 --- a/public/scripts/agent-desktop-0.0.2.js +++ b/public/scripts/agent-desktop-0.0.2.js @@ -100,7 +100,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { } obj.send = function (x) { - if (obj.debugmode > 1) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); } + if (obj.debugmode > 2) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); } if (obj.parent != null) { obj.parent.send(x); } } @@ -156,13 +156,13 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { } obj.SendUnPause = function () { - //obj.Debug("SendUnPause"); + if (obj.debugmode > 1) { console.log("SendUnPause"); } //obj.xxStateChange(3); obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00)); } obj.SendPause = function () { - //obj.Debug("SendPause"); + if (obj.debugmode > 1) { console.log("SendPause"); } //obj.xxStateChange(2); obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01)); } @@ -196,7 +196,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { obj.ProcessBinaryCommand = function (cmd, cmdsize, view) { var X, Y; if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; } - //console.log('CMD', cmd, cmdsize, X, Y); + if (obj.debugmode > 2) { console.log('CMD', cmd, cmdsize, X, Y); } // Record the command if needed if (obj.recordedData != null) { @@ -250,7 +250,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { break; case 17: // MNG_KVM_MESSAGE var str = String.fromCharCode.apply(null, data.slice(4)); - obj.Debug("Got KVM Message: " + str); + console.log("Got KVM Message: " + str); if (obj.onMessage != null) obj.onMessage(str, obj); break; case 65: // Alert @@ -502,7 +502,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); } obj.FirstDraw = false; - //obj.Debug("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight); + if (obj.debugmode > 1) { console.log("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight); } } obj.xxMouseInputGrab = false; diff --git a/webserver.js b/webserver.js index 7e853267..fbca1886 100644 --- a/webserver.js +++ b/webserver.js @@ -4265,19 +4265,92 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (domain == null) return; if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key if ((req.session == null) || (req.session.userid == null)) { res.sendStatus(404); return; } + var user = null, coreDumpsAllowed = false; + if (typeof req.session.userid == 'string') { user = obj.users[req.session.userid]; } + if (user == null) { res.sendStatus(404); return; } + + // Check if this user has access to agent core dumps + 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)))) { + coreDumpsAllowed = true; + + if ((req.query.dldump != null) && obj.common.IsFilenameValid(req.query.dldump)) { + // Download a dump file + var dumpFile = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', req.query.dldump); + if (obj.fs.existsSync(dumpFile)) { + 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 + '' }); + res.sendFile(dumpFile); return; + } else { + res.sendStatus(404); return; + } + } + + if ((req.query.deldump != null) && obj.common.IsFilenameValid(req.query.deldump)) { + // Delete a dump file + try { obj.fs.unlinkSync(obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', req.query.deldump)); } catch (ex) { console.log(ex); } + } + + if ((req.query.dumps != null) || (req.query.deldump != null)) { + // Send list of agent core dumps + var response = 'Mesh Agents Core Dumps'; + response += ''; + + var coreDumpPath = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps'); + if (obj.fs.existsSync(coreDumpPath)) { + var files = obj.fs.readdirSync(coreDumpPath); + var coredumps = []; + for (var i in files) { + var file = files[i]; + if (file.endsWith('.dmp')) { + var fileSplit = file.substring(0, file.length - 4).split('-'); + if (fileSplit.length == 3) { + var agentid = parseInt(fileSplit[0]); + if ((isNaN(agentid) == false) && (obj.parent.meshAgentBinaries[agentid] != null)) { + var agentinfo = obj.parent.meshAgentBinaries[agentid]; + var filestats = obj.fs.statSync(obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', file)); + coredumps.push({ + fileSplit: fileSplit, + agentinfo: agentinfo, + filestats: filestats, + currentAgent: agentinfo.hashhex.startsWith(fileSplit[1].toLowerCase()), + downloadUrl: req.originalUrl.split('?')[0] + '?dldump=' + file + (req.query.key ? ('&key=' + req.query.key) : ''), + deleteUrl: req.originalUrl.split('?')[0] + '?deldump=' + file + (req.query.key ? ('&key=' + req.query.key) : ''), + agentUrl: req.originalUrl.split('?')[0] + '?id=' + agentinfo.id + (req.query.key ? ('&key=' + req.query.key) : ''), + time: new Date(filestats.ctime) + }); + } + } + } + } + coredumps.sort(function (a, b) { if (a.time > b.time) return -1; if (a.time < b.time) return 1; return 0; }); + for (var i in coredumps) { + var d = coredumps[i]; + response += ''; + response += ''; + if (d.currentAgent) { response += ''; } else { response += ''; } + response += ''; + } + } + response += '
IDUpload DateDescriptionCurrentDumpSizeAgentAgent SHA384NodeID
' + d.agentinfo.id + '' + d.time.toDateString().split(' ').join(' ') + '' + d.agentinfo.desc.split(' ').join(' ') + '' + d.currentAgent + 'Download' + d.filestats.size + 'Download' + d.fileSplit[1].toLowerCase() + '' + d.fileSplit[2] + 'Delete
Mesh Agents'; + res.send(response); + return; + } + } // Send a list of available mesh agents - var response = 'Mesh Agents'; + var response = 'Mesh Agents
'; response += ''; + var originalUrl = req.originalUrl.split('?')[0]; for (var agentid in obj.parent.meshAgentBinaries) { + if (agentid >= 10000) continue; var agentinfo = obj.parent.meshAgentBinaries[agentid]; - var originalUrl = req.originalUrl.split('?')[0]; - response += ''; + response += ''; response += ''; response += ''; response += ''; } - response += '
IDDescriptionLinkSizeSHA384MeshCmd
' + agentinfo.id + '' + agentinfo.desc + '
' + agentinfo.id + '' + agentinfo.desc.split(' ').join(' ') + '' + agentinfo.rname + '' + agentinfo.size + '' + agentinfo.hashhex + '' + agentinfo.rname.replace('agent', 'cmd') + '
'; + response += ''; + if (coreDumpsAllowed) { response += 'MeshAgent Crash Dumps'; } + response += ''; res.send(response); } };