Reporting improvements.

This commit is contained in:
Ylian Saint-Hilaire 2021-09-10 17:28:08 -07:00
parent 5899667918
commit ea027d73a1
3 changed files with 143 additions and 38 deletions

View File

@ -22,6 +22,18 @@ Protocol numbers
13 - SSH-FILES
*/
// Protocol Numbers
const PROTOCOL_TERMINAL = 1;
const PROTOCOL_DESKTOP = 2;
const PROTOCOL_FILES = 5;
const PROTOCOL_AMTWSMAN = 100;
const PROTOCOL_AMTREDIR = 101;
const PROTOCOL_MESSENGER = 200;
const PROTOCOL_WEBRDP = 201;
const PROTOCOL_WEBSSH = 202;
const PROTOCOL_WEBSFTP = 203;
const PROTOCOL_WEBVNC = 204;
// Construct a MSTSC Relay object, called upon connection
// This is a bit of a hack as we are going to run the RDP connection thru a loopback connection.
@ -42,11 +54,19 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
obj.close = function (arg) {
if (obj.ws == null) return;
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
//var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
//if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
//console.log('WinRDP - in', inTraffc, 'out', outTraffc);
// Event the session ending
if ((obj.startTime) && (obj.meshid != null)) {
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
var user = parent.users[obj.cookie.userid];
var username = (user != null) ? user.name : null;
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, msgid: 125, msgArgs: [sessionSeconds], msg: "Left Web-RDP session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBRDP, bytesin: inTraffc, bytesout: outTraffc };
parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
delete obj.startTime;
}
if (obj.wsClient) { obj.wsClient.close(); delete obj.wsClient; }
if (obj.tcpServer) { obj.tcpServer.close(); delete obj.tcpServer; }
@ -55,7 +75,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
if (arg == 2) { try { ws._socket._parent.end(); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
obj.ws.removeAllListeners();
obj.relayActive = false;
delete obj.ws;
delete obj.nodeid;
delete obj.meshid;
delete obj.userid;
};
// Start the looppback server
@ -116,6 +140,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
}).on('connect', function () {
send(['rdp-connect']);
if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
obj.startTime = Date.now();
}).on('bitmap', function (bitmap) {
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
delete bitmap.data;
@ -148,7 +173,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
// Event node change if needed
if (changed) {
// Event the node change
var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" };
var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.cookie.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" };
if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event);
}
@ -166,16 +191,18 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
// Decode the authentication cookie
obj.cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) return;
if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) { obj.close(); return; }
obj.nodeid = obj.cookie.nodeid;
obj.userid = obj.cookie.userid;
// Check is you need to load server stored credentials
if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
const node = nodes[0];
// Get node
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
if ((err != null) || (nodes == null) || (nodes.length != 1)) { obj.close(); return; }
const node = nodes[0];
obj.meshid = node.meshid;
// Check if we need to load server stored credentials
if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
// Check if RDP credentials exist
if ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) {
obj.infos.domain = node.rdp.d;
@ -189,10 +216,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
obj.infos.password = '';
startTcpServer();
}
});
} else {
startTcpServer();
}
} else {
startTcpServer();
}
});
break;
}
case 'mouse': { if (rdpClient) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
@ -253,11 +280,19 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
obj.close = function (arg) {
if (obj.ws == null) return;
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
//var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
//if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
//console.log('WinSSH - in', inTraffc, 'out', outTraffc);
// Event the session ending
if ((obj.startTime) && (obj.meshid != null)) {
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
var user = parent.users[obj.cookie.userid];
var username = (user != null) ? user.name : null;
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: obj.cookie.userid, username: username, msgid: 123, msgArgs: [sessionSeconds], msg: "Left Web-SSH session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSSH, bytesin: inTraffc, bytesout: outTraffc };
parent.parent.DispatchEvent(['*', obj.nodeid, obj.cookie.userid, obj.meshid], obj, event);
delete obj.startTime;
}
if (obj.sshShell) {
obj.sshShell.destroy();
@ -287,6 +322,8 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
obj.relayActive = false;
delete obj.termSize;
delete obj.cookie;
delete obj.nodeid;
delete obj.meshid;
delete obj.ws;
};
@ -347,6 +384,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
if (obj.keep === true) saveSshCredentials();
obj.startTime = Date.now();
obj.sshClient.shell(function (err, stream) { // Start a remote shell
if (err) { obj.close(); return; }
@ -466,7 +504,15 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
// If the web socket is closed
ws.on('close', function (req) { parent.parent.debug('relay', 'SSH: Browser websocket closed'); obj.close(); });
// Get the meshid for this device
parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
if ((err != null) || (nodes == null) || (nodes.length != 1)) { parent.parent.debug('relay', 'SSH: Invalid device'); obj.close(); }
const node = nodes[0];
obj.nodeid = node._id; // Store the NodeID
obj.meshid = node.meshid; // Store the MeshID
});
return obj;
};
@ -497,11 +543,17 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
obj.close = function (arg) {
if (obj.ws == null) return;
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
//var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
//if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
//console.log('WinSSH - in', inTraffc, 'out', outTraffc);
// Event the session ending
if (obj.startTime) {
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: user._id, username: user.name, msgid: 123, msgArgs: [sessionSeconds], msg: "Left Web-SSH session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSSH, bytesin: inTraffc, bytesout: outTraffc };
parent.parent.DispatchEvent(['*', obj.nodeid, user._id, obj.meshid], obj, event);
delete obj.startTime;
}
if (obj.sshShell) {
obj.sshShell.destroy();
@ -531,6 +583,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
obj.relayActive = false;
delete obj.termSize;
delete obj.cookie;
delete obj.nodeid;
delete obj.meshid;
delete obj.ws;
};
@ -587,6 +641,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
if (obj.keep === true) saveSshCredentials();
obj.startTime = Date.now();
obj.sshClient.shell(function (err, stream) { // Start a remote shell
if (err) { obj.close(); return; }
@ -712,6 +767,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
if ((rights != 0xFFFFFFFF) && (rights & 0x00000200)) { obj.close(); return; } // MESHRIGHT_NOTERMINAL is set
obj.mtype = node.mtype; // Store the device group type
obj.nodeid = node._id; // Store the NodeID
obj.meshid = node.meshid; // Store the MeshID
// Check the SSH port
obj.tcpport = 22;
@ -776,11 +832,17 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
obj.close = function (arg) {
if (obj.ws == null) return;
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
//var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
//if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
//console.log('WinSSH - in', inTraffc, 'out', outTraffc);
// Event the session ending
if (obj.startTime) {
// Collect how many raw bytes where received and sent.
// We sum both the websocket and TCP client in this case.
var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten;
if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; }
const sessionSeconds = Math.round((Date.now() - obj.startTime) / 1000);
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, nodeid: obj.nodeid, userid: user._id, username: user.name, msgid: 124, msgArgs: [sessionSeconds], msg: "Left Web-SFTP session after " + sessionSeconds + " second(s).", protocol: PROTOCOL_WEBSFTP, bytesin: inTraffc, bytesout: outTraffc };
parent.parent.DispatchEvent(['*', obj.nodeid, user._id, obj.meshid], obj, event);
delete obj.startTime;
}
if (obj.sshClient) {
obj.sshClient.destroy();
@ -803,6 +865,8 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
obj.relayActive = false;
delete obj.cookie;
delete obj.sftp;
delete obj.nodeid;
delete obj.meshid;
delete obj.ws;
};
@ -859,6 +923,8 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
if (obj.keep === true) saveSshCredentials();
obj.startTime = Date.now();
obj.sshClient.sftp(function(err, sftp) {
if (err) { obj.close(); return; }
obj.connected = true;
@ -1165,6 +1231,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
if ((rights != 0xFFFFFFFF) && (rights & 0x00000200)) { obj.close(); return; } // MESHRIGHT_NOTERMINAL is set
obj.mtype = node.mtype; // Store the device group type
obj.nodeid = node._id; // Store the NodeID
obj.meshid = node.meshid; // Store the MeshID
// Check the SSH port
obj.tcpport = 22;

View File

@ -69,6 +69,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const SITERIGHT_ALLEVENTS = 0x00000800; // 2048
const SITERIGHT_ADMIN = 0xFFFFFFFF;
// Protocol Numbers
const PROTOCOL_TERMINAL = 1;
const PROTOCOL_DESKTOP = 2;
const PROTOCOL_FILES = 5;
const PROTOCOL_AMTWSMAN = 100;
const PROTOCOL_AMTREDIR = 101;
const PROTOCOL_MESSENGER = 200;
const PROTOCOL_WEBRDP = 201;
const PROTOCOL_WEBSSH = 202;
const PROTOCOL_WEBSFTP = 203;
const PROTOCOL_WEBVNC = 204;
// Events
/*
var eventsMessageId = {
@ -5428,7 +5440,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Get the events in the time range
// MySQL or MariaDB query will ignore the MsgID filter.
db.GetEventsTimeRange(ids, domain.id, [5, 10, 11, 12, 122], new Date(command.start * 1000), new Date(command.end * 1000), function (err, docs) {
db.GetEventsTimeRange(ids, domain.id, [5, 10, 11, 12, 122, 123, 124, 125, 126], new Date(command.start * 1000), new Date(command.end * 1000), function (err, docs) {
if (err != null) return;
var data = { groups: {} };
@ -5443,10 +5455,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
data.columns = [{ id: 'time', title: "time", format: 'time' }, { id: 'nodeid', title: "device", format: 'node' }, { id: 'userid', title: "user", format: 'user' }, { id: 'protocol', title: "session", format: 'protocol', align: 'center' }, { id: 'length', title: "length", format: 'seconds', align: 'center', sumBy: 'protocol' } ];
}
// Add traffic colums
if (command.showTraffic) {
data.columns.push({ id: 'bytesin', title: "Bytes In", format: 'bytes', align: 'center', sumBy: 'protocol' });
data.columns.push({ id: 'bytesout', title: "Bytes Out", format: 'bytes', align: 'center', sumBy: 'protocol' });
}
// Rows
for (var i in docs) {
// If MySQL or MariaDB query, we can't filter on MsgID, so we have to do it here.
if ((docs[i].msgid != 5) && (docs[i].msgid != 10) && (docs[i].msgid != 11) && (docs[i].msgid != 12) && (docs[i].msgid != 122)) continue;
if ((docs[i].msgid != 5) && (docs[i].msgid != 10) && (docs[i].msgid != 11) && (docs[i].msgid != 12) && ((docs[i].msgid < 122) && (docs[i].msgid > 126))) continue;
if ((command.devGroup != null) && (docs[i].ids != null) && (docs[i].ids.indexOf(command.devGroup) == -1)) continue;
var entry = { time: docs[i].time.valueOf() };
@ -5456,9 +5474,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (command.groupBy != 2) { entry.nodeid = docs[i].nodeid; }
entry.protocol = docs[i].protocol;
// Add traffic data
if (command.showTraffic) { entry.bytesin = docs[i].bytesin; entry.bytesout = docs[i].bytesout; }
// Session length
if (((docs[i].msgid >= 10) && (docs[i].msgid <= 12)) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[3] == 'number')) { entry.length = docs[i].msgArgs[3]; }
else if ((docs[i].msgid == 122) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[0] == 'number')) { entry.length = docs[i].msgArgs[0]; }
else if ((docs[i].msgid >= 122) && (docs[i].msgid <= 126) && (docs[i].msgArgs != null) && (typeof docs[i].msgArgs == 'object') && (typeof docs[i].msgArgs[0] == 'number')) { entry.length = docs[i].msgArgs[0]; }
if (command.groupBy == 1) { // Add entry to per user group
if (data.groups[docs[i].userid] == null) { data.groups[docs[i].userid] = { entries: [] }; }

View File

@ -12892,6 +12892,10 @@
120: "Started local relay session \"{0}\", protocol {1} to {2}",
121: "Ended local relay session \"{0}\", protocol {1} to {2}, {3} second(s)",
122: "Left the desktop multiplex session after {0} second(s).",
123: "Left Web-SSH session after {0} second(s).",
124: "Left Web-SFTP session after {0} second(s).",
125: "Left Web-RDP session after {0} second(s).",
126: "Left Web-VNC session after {0} second(s)."
};
// Highlights the device being hovered
@ -14942,6 +14946,10 @@
x += addHtmlValue("Time Range", '<input id=d2timeRangeSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
x += '</div>';
x += '<div id=d2showTrafficDiv style=display:none>';
x += addHtmlValue("", '<div style=width:250px><label><input type=checkbox id=d2showTraffic ' + ((settings.showTraffic) ? 'checked' : '') + '>' + "Show Traffic" + '</label><div>');
x += '</div>';
setDialogMode(2, "Generate Report", 3, generateReportDialogEx, x);
generateReportDialogValidate();
@ -14953,6 +14961,7 @@
function generateReportDialogValidate() {
QV('d2timeRangeDiv', Q('d2timeRange').value == 0);
QV('d2showTrafficDiv', Q('d2reportType').value == 1);
}
function generateReportDialogEx(b, tag) {
@ -14969,8 +14978,8 @@
try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (ex) {}
var devGroup = decodeURIComponent(Q('d2groupId').value);
if (devGroup == 0) { devGroup = null; }
putstore('_ReportSettings', JSON.stringify({ type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), timeRange: parseInt(Q('d2timeRange').value), devGroup: devGroup }));
meshserver.send({ action: 'report', type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), start: start, end: end, tz: tz, tf: new Date().getTimezoneOffset(), l: getLang(), devGroup: devGroup });
putstore('_ReportSettings', JSON.stringify({ type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), timeRange: parseInt(Q('d2timeRange').value), devGroup: devGroup, showTraffic: Q('d2showTraffic').checked }));
meshserver.send({ action: 'report', type: parseInt(Q('d2reportType').value), groupBy: parseInt(Q('d2groupBy').value), start: start, end: end, tz: tz, tf: new Date().getTimezoneOffset(), l: getLang(), devGroup: devGroup, showTraffic: Q('d2showTraffic').checked });
}
function renderReport(r) {
@ -15071,13 +15080,21 @@
}
function renderReportFormat(v, f) {
if (v == null) return '';
if (f == 'datetime') { return printDateTime(new Date(v)).split(' ').join('&nbsp'); }
if (f == 'time') { return printTime(new Date(v)).split(' ').join('&nbsp'); }
if (f == 'protocol') {
if (v == 1) return "Terminal";
if (v == 2) return "Desktop";
if (v == 5) return "Files";
return "Unknown";
if (v == 100) return "AMT-WSMAN";
if (v == 101) return "AMT-Redir";
if (v == 200) return "Messenger";
if (v == 201) return "Web-RDP";
if (v == 202) return "Web-SSH";
if (v == 203) return "Web-SFTP";
if (v == 204) return "Web-VNC";
return "Unknown" + ' (' + v + ')';
}
if (f == 'seconds') {
var seconds = v % 60;