Added recording space/count quota.

This commit is contained in:
Ylian Saint-Hilaire 2020-04-30 02:02:23 -07:00
parent 9f9fdf7b92
commit ea9edefad1
7 changed files with 102 additions and 12 deletions

View File

@ -633,7 +633,7 @@ function CreateMeshCentralServer(config, args) {
} }
// Check top level configuration for any unreconized values // Check top level configuration for any unreconized values
if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } } if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".'); } } }
if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { config.settings.userallowedip = obj.args.userallowedip = null; } else { config.settings.userallowedip = obj.args.userallowedip = obj.args.userallowedip.split(','); } } if (typeof obj.args.userallowedip == 'string') { if (obj.args.userallowedip == '') { config.settings.userallowedip = obj.args.userallowedip = null; } else { config.settings.userallowedip = obj.args.userallowedip = obj.args.userallowedip.split(','); } }
if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { config.settings.userblockedip = obj.args.userblockedip = null; } else { config.settings.userblockedip = obj.args.userblockedip = obj.args.userblockedip.split(','); } } if (typeof obj.args.userblockedip == 'string') { if (obj.args.userblockedip == '') { config.settings.userblockedip = obj.args.userblockedip = null; } else { config.settings.userblockedip = obj.args.userblockedip = obj.args.userblockedip.split(','); } }
@ -1009,6 +1009,10 @@ function CreateMeshCentralServer(config, args) {
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in config.json."); return; } } } for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in config.json."); return; } } }
for (i in obj.config.domains) { for (i in obj.config.domains) {
// Apply default domain settings if present
if (typeof obj.config.domaindefaults == 'object') { for (var j in obj.config.domaindefaults) { if (obj.config.domains[i][j] == null) { obj.config.domains[i][j] = obj.config.domaindefaults[j]; } } }
// Perform domain setup
if (typeof obj.config.domains[i] != 'object') { console.log("ERROR: Invalid domain configuration in config.json."); process.exit(); return; } if (typeof obj.config.domains[i] != 'object') { console.log("ERROR: Invalid domain configuration in config.json."); process.exit(); return; }
if ((i.length > 0) && (i[0] == '_')) { delete obj.config.domains[i]; continue; } // Remove any domains with names that start with _ if ((i.length > 0) && (i[0] == '_')) { delete obj.config.domains[i]; continue; } // Remove any domains with names that start with _
if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); } if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); }

View File

@ -216,6 +216,7 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
parent.parent.fs.close(fd); parent.parent.fs.close(fd);
// Now that the recording file is closed, check if we need to index this file. // Now that the recording file is closed, check if we need to index this file.
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', filename); } if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', filename); }
cleanUpRecordings();
}, rf.filename); }, rf.filename);
} }
@ -663,6 +664,34 @@ function CreateDesktopMultiplexor(parent, domain, nodeid, func) {
} catch (ex) { console.log(ex); func(fd, tag); } } catch (ex) { console.log(ex); func(fd, tag); }
} }
// If there is a recording quota, remove any old recordings if needed
function cleanUpRecordings() {
if (domain.sessionrecording && ((typeof domain.sessionrecording.maxrecordings == 'number') || (typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number'))) {
var recPath = null, fs = require('fs');
if (domain.sessionrecording.filepath) { recPath = domain.sessionrecording.filepath; } else { recPath = parent.parent.recordpath; }
fs.readdir(recPath, function (err, files) {
if ((err != null) || (files == null)) return;
var recfiles = [];
for (var i in files) {
if (files[i].endsWith('.mcrec')) {
var j = files[i].indexOf('-');
if (j > 0) { recfiles.push({ n: files[i], r: files[i].substring(j + 1), s: fs.statSync(parent.parent.path.join(recPath, files[i])).size }); }
}
}
recfiles.sort(function (a, b) { if (a.r < b.r) return 1; if (a.r > b.r) return -1; return 0; });
var totalFiles = 0, totalSize = 0;
for (var i in recfiles) {
var overQuota = false;
if ((typeof domain.sessionrecording.maxrecordings == 'number') && (totalFiles >= domain.sessionrecording.maxrecordings)) { overQuota = true; }
else if ((typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number') && (totalSize >= (domain.sessionrecording.maxrecordingsizemegabytes * 1048576))) { overQuota = true; }
if (overQuota) { fs.unlinkSync(parent.parent.path.join(recPath, recfiles[i].n)); }
totalFiles++;
totalSize += recfiles[i].s;
}
});
}
}
recordingSetup(domain, function () { func(obj); }); recordingSetup(domain, function () { func(obj); });
return obj; return obj;
} }

View File

@ -408,6 +408,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
parent.parent.fs.close(fd); parent.parent.fs.close(fd);
// Now that the recording file is closed, check if we need to index this file. // Now that the recording file is closed, check if we need to index this file.
if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); } if (domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
cleanUpRecordings();
}, { ws: ws, pws: peer.ws, logfile: logfile }); }, { ws: ws, pws: peer.ws, logfile: logfile });
} }
@ -500,6 +501,34 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
} }
} }
// If there is a recording quota, remove any old recordings if needed
function cleanUpRecordings() {
if (domain.sessionrecording && ((typeof domain.sessionrecording.maxrecordings == 'number') || (typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number'))) {
var recPath = null, fs = require('fs');
if (domain.sessionrecording.filepath) { recPath = domain.sessionrecording.filepath; } else { recPath = parent.parent.recordpath; }
fs.readdir(recPath, function (err, files) {
if ((err != null) || (files == null)) return;
var recfiles = [];
for (var i in files) {
if (files[i].endsWith('.mcrec')) {
var j = files[i].indexOf('-');
if (j > 0) { recfiles.push({ n: files[i], r: files[i].substring(j + 1), s: fs.statSync(parent.parent.path.join(recPath, files[i])).size }); }
}
}
recfiles.sort(function (a, b) { if (a.r < b.r) return 1; if (a.r > b.r) return -1; return 0; });
var totalFiles = 0, totalSize = 0;
for (var i in recfiles) {
var overQuota = false;
if ((typeof domain.sessionrecording.maxrecordings == 'number') && (totalFiles >= domain.sessionrecording.maxrecordings)) { overQuota = true; }
else if ((typeof domain.sessionrecording.maxrecordingsizemegabytes == 'number') && (totalSize >= (domain.sessionrecording.maxrecordingsizemegabytes * 1048576))) { overQuota = true; }
if (overQuota) { fs.unlinkSync(parent.parent.path.join(recPath, recfiles[i].n)); }
totalFiles++;
totalSize += recfiles[i].s;
}
});
}
}
// If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session. // If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session.
performRelay(); performRelay();
return obj; return obj;

View File

@ -3569,7 +3569,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (actionTaken) { parent.db.SetUser(user); } if (actionTaken) { parent.db.SetUser(user); }
// Return one time passwords for this user // Return one time passwords for this user
if (user.otpsecret || ((user.otphkeys != null) && (user.otphkeys.length > 0))) { if (count2factoraAuths() > 0) {
ws.send(JSON.stringify({ action: 'otpauth-getpasswords', passwords: user.otpkeys ? user.otpkeys.keys : null })); ws.send(JSON.stringify({ action: 'otpauth-getpasswords', passwords: user.otpkeys ? user.otpkeys.keys : null }));
} }
@ -4260,5 +4260,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
} }
// Return the number of 2nd factor for this account
function count2factoraAuths() {
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.parent.mailserver != null));
var sms2fa = ((parent.parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)));
var authFactorCount = 0;
if (user.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
if (email2fa && (user.otpekey != null)) { authFactorCount++; } // EMail factor
if (sms2fa && (user.phone != null)) { authFactorCount++; } // SMS factor
if (user.otphkeys != null) { authFactorCount += user.otphkeys.length; } // FIDO hardware factor
if ((authFactorCount > 0) && (user.otpkeys != null)) { authFactorCount++; } // Backup keys
return authFactorCount;
}
return obj; return obj;
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.5.17", "version": "0.5.18",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View File

@ -39,6 +39,7 @@
"_NpmPath": "c:\\npm.exe", "_NpmPath": "c:\\npm.exe",
"_NpmProxy": "http://1.2.3.4:80", "_NpmProxy": "http://1.2.3.4:80",
"_AllowHighQualityDesktop": true, "_AllowHighQualityDesktop": true,
"_DesktopMultiplex": true,
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24", "_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
"_UserBlockedIP": "127.0.0.1,::1,192.168.0.100", "_UserBlockedIP": "127.0.0.1,::1,192.168.0.100",
"_AgentAllowedIP": "192.168.0.100/24", "_AgentAllowedIP": "192.168.0.100/24",
@ -78,6 +79,12 @@
"_MaxInvalidLogin": { "time": 10, "count": 10, "coolofftime": 10 }, "_MaxInvalidLogin": { "time": 10, "count": 10, "coolofftime": 10 },
"_Plugins": { "enabled": true } "_Plugins": { "enabled": true }
}, },
"_domaindefaults": {
"__comment__": "Any settings in this section is used as default setting for all domains",
"Title": "MyDefaultTitle",
"Footer": "Default page footer",
"NewAccounts": false
},
"_domains": { "_domains": {
"": { "": {
"Title": "MyServer", "Title": "MyServer",
@ -137,6 +144,8 @@
"_SessionRecording": { "_SessionRecording": {
"_filepath": "C:\\temp", "_filepath": "C:\\temp",
"_index": true, "_index": true,
"_maxRecordings": 10,
"_maxRecordingSizeMegabytes": 3,
"__protocols__": "Is an array: 1 = Terminal, 2 = Desktop, 5 = Files, 100 = Intel AMT WSMAN, 101 = Intel AMT Redirection", "__protocols__": "Is an array: 1 = Terminal, 2 = Desktop, 5 = Files, 100 = Intel AMT WSMAN, 101 = Intel AMT Redirection",
"protocols": [ 1, 2, 101 ] "protocols": [ 1, 2, 101 ]
} }

View File

@ -1694,11 +1694,23 @@
} }
} }
// Return the number of 2nd factor for this account
function count2factoraAuths() {
var authFactorCount = 0;
if (userinfo.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor
if ((features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor
if (userinfo.otphkeys != null) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor
if ((authFactorCount > 0) && (userinfo.otpkeys == 1)) { authFactorCount++; } // Backup keys
return authFactorCount;
}
var backupCodesWarningDone = false; var backupCodesWarningDone = false;
function updateSelf() { function updateSelf() {
var authFactorCount = count2factoraAuths(); // Get the number of 2nd factors
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)); QV('manageOtp', authFactorCount > 0);
QV('authPhoneNumberCheck', (userinfo.phone != null)); QV('authPhoneNumberCheck', (userinfo.phone != null));
QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true)); QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
QV('authAppSetupCheck', userinfo.otpsecret == 1); QV('authAppSetupCheck', userinfo.otpsecret == 1);
@ -1707,12 +1719,6 @@
masterUpdate(4 + 128 + 4096); masterUpdate(4 + 128 + 4096);
// Check if none or at least 2 factors are enabled. // Check if none or at least 2 factors are enabled.
var authFactorCount = 0;
if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount += 1; }
if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount += 1; }
if (userinfo.otpkeys == 1) { authFactorCount += 1; }
if (userinfo.otpsecret == 1) { authFactorCount += 1; }
if (userinfo.otphkeys != null) { authFactorCount += userinfo.otphkeys; }
if ((backupCodesWarningDone == false) && (authFactorCount == 1)) { if ((backupCodesWarningDone == false) && (authFactorCount == 1)) {
var n = { text: "Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.", title: "Two factor authentication" }; var n = { text: "Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.", title: "Two factor authentication" };
addNotification(n); addNotification(n);
@ -4838,7 +4844,7 @@
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; } if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
// Remind the user to add two factor authentication // Remind the user to add two factor authentication
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; } if ((features & 0x00040000) && (count2factoraAuths() == 0)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
if (event && (event.shiftKey == true)) { if (event && (event.shiftKey == true)) {
// Open the device in a different tab // Open the device in a different tab
@ -7987,7 +7993,7 @@
function account_manageOtp(action) { function account_manageOtp(action) {
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); } if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); }
if (xxdialogMode || ((features & 4096) == 0)) return false; if (xxdialogMode || ((features & 4096) == 0)) return false;
if ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); } if (count2factoraAuths() > 0) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); }
return false; return false;
} }