Added bad login IP blocking cooloff support.

Ylian Saint-Hilaire committed Nov 19, 2019 at 13:33 UTC 07d4099892266026b6991f2b8a6902978e20448a
5 files changed +29 -9
meshcentral.js
+1 -2
@@ -2039,8 +2039,7 @@ function mainStart() {
2039 var modules = ['ws', 'cbor', 'nedb', 'https', 'yauzl', 'xmldom', 'ipcheck', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'cookie-session', 'express-handlebars'];
2040 if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
2041 if (ldap == true) { modules.push('ldapauth-fork'); }
2042 - //if (config.letsencrypt != null) { modules.push('greenlock@2.8.8'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
2043 - if (config.letsencrypt != null) { if ((nodeVersion < 10) || (require('crypto').generateKeyPair == null)) { console.log("WARNING: Let's Encrypt support requires Node v10.12.0 or higher."); } else { modules.push('greenlock'); } } // Add Greenlock Module
2042 + if (config.letsencrypt != null) { if ((nodeVersion < 10) || (require('crypto').generateKeyPair == null)) { if (!args.launch) { console.log("WARNING: Let's Encrypt support requires Node v10.12.0 or higher."); } } else { modules.push('greenlock'); } } // Add Greenlock Module
2043 if (config.settings.mqtt != null) { modules.push('aedes'); } // Add MQTT Modules
2044 if (config.settings.mongodb != null) { modules.push('mongodb'); } // Add MongoDB, official driver.
2045 if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module.
meshuser.js
+13 -2
@@ -693,10 +693,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
693 break;
694 }
695 case 'badlogins': {
696 - r = "Max is " + parent.parent.config.settings.maxinvalidlogin.count + " bad login(s) in " + parent.parent.config.settings.maxinvalidlogin.time + " minute(s).\r\n";
696 + if (typeof parent.parent.config.settings.maxinvalidlogin.coolofftime == 'number') {
697 + r = "Max is " + parent.parent.config.settings.maxinvalidlogin.count + " bad login(s) in " + parent.parent.config.settings.maxinvalidlogin.time + " minute(s), " + parent.parent.config.settings.maxinvalidlogin.coolofftime + " minute(s) cooloff.\r\n";
698 + } else {
699 + r = "Max is " + parent.parent.config.settings.maxinvalidlogin.count + " bad login(s) in " + parent.parent.config.settings.maxinvalidlogin.time + " minute(s).\r\n";
700 + }
701 var badLoginCount = 0;
702 parent.cleanBadLoginTable();
699 - for (var i in parent.badLoginTable) { badLoginCount++; r += (i + ' - ' + parent.badLoginTable[i].length + " entries\r\n"); }
703 + for (var i in parent.badLoginTable) {
704 + badLoginCount++;
705 + if (typeof parent.badLoginTable[i] == 'number') {
706 + r += "Cooloff for " + Math.floor((parent.badLoginTable[i] - Date.now()) / 60000) + " minute(s)\r\n";
707 + } else {
708 + r += (i + ' - ' + parent.badLoginTable[i].length + " entries\r\n");
709 + }
710 + }
711 if (badLoginCount == 0) { r += 'No bad logins.'; }
712 break;
713 }
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.4.4-m",
3 + "version": "0.4.4-o",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
sample-config.json
+1 -1
@@ -60,7 +60,7 @@
60 "meshcommander": "https://www.meshcommander.com/"
61 },
62 "__MaxInvalidLogin": "Time in minutes, max amount of bad logins from a source IP in the time before logins are rejected.",
63 - "MaxInvalidLogin": { "time": 10, "count": 10 }
63 + "MaxInvalidLogin": { "time": 10, "count": 10, "coolofftime": 10 }
64 },
65 "_domains": {
66 "": {
webserver.js
+13 -3
@@ -4038,26 +4038,36 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4038 if (parent.config.settings.maxinvalidlogin == null) { parent.config.settings.maxinvalidlogin = { time: 10, count: 10 }; }
4039 if (typeof parent.config.settings.maxinvalidlogin.time != 'number') { parent.config.settings.maxinvalidlogin.time = 10; }
4040 if (typeof parent.config.settings.maxinvalidlogin.count != 'number') { parent.config.settings.maxinvalidlogin.count = 10; }
4041 + if ((typeof parent.config.settings.maxinvalidlogin.coolofftime != 'number') || (parent.config.settings.maxinvalidlogin.coolofftime < 1)) { parent.config.settings.maxinvalidlogin.coolofftime = null; }
4042 obj.setbadLogin = function (ip) { // Set an IP address that just did a bad login request
4043 if (typeof ip == 'object') { ip = cleanRemoteAddr(ip.ip); }
4044 if (++obj.badLoginTableLastClean > 100) { obj.cleanBadLoginTable(); }
4045 + if (typeof obj.badLoginTable[ip] == 'number') { if (obj.badLoginTable[ip] < Date.now()) { delete obj.badLoginTable[ip]; } else { return; } } // Check cooloff period
4046 if (obj.badLoginTable[ip] == null) { obj.badLoginTable[ip] = [Date.now()]; } else { obj.badLoginTable[ip].push(Date.now()); }
4047 + if ((obj.badLoginTable[ip].length >= parent.config.settings.maxinvalidlogin.count) && (parent.config.settings.maxinvalidlogin.coolofftime != null)) {
4048 + obj.badLoginTable[ip] = Date.now() + (parent.config.settings.maxinvalidlogin.coolofftime * 60000); // Move to cooloff period
4049 + }
4050 }
4051 obj.checkAllowLogin = function (ip) { // Check if an IP address is allowed to login
4052 if (typeof ip == 'object') { ip = cleanRemoteAddr(ip.ip); }
4053 var cutoffTime = Date.now() - (parent.config.settings.maxinvalidlogin.time * 60000); // Time in minutes
4054 var ipTable = obj.badLoginTable[ip];
4055 if (ipTable == null) return true;
4056 + if (typeof ipTable == 'number') { if (obj.badLoginTable[ip] < Date.now()) { delete obj.badLoginTable[ip]; } else { return false; } } // Check cooloff period
4057 while ((ipTable.length > 0) && (ipTable[0] < cutoffTime)) { ipTable.shift(); }
4058 if (ipTable.length == 0) { delete obj.badLoginTable[ip]; return true; }
4059 return (ipTable.length < parent.config.settings.maxinvalidlogin.count); // No more than x bad logins in x minutes
4060 }
4061 obj.cleanBadLoginTable = function () { // Clean up the IP address login blockage table, we do this occasionaly.
4062 var cutoffTime = Date.now() - (parent.config.settings.maxinvalidlogin.time * 60000); // Time in minutes
4057 - for (var i in ipTable) {
4063 + for (var ip in obj.badLoginTable) {
4064 var ipTable = obj.badLoginTable[ip];
4059 - while ((ipTable.length > 0) && (ipTable[0] < cutoffTime)) { ipTable.shift(); }
4060 - if (ipTable.length == 0) { delete obj.badLoginTable[ip]; }
4065 + if (typeof ipTable == 'number') {
4066 + if (obj.badLoginTable[ip] < Date.now()) { delete obj.badLoginTable[ip]; } // Check cooloff period
4067 + } else {
4068 + while ((ipTable.length > 0) && (ipTable[0] < cutoffTime)) { ipTable.shift(); }
4069 + if (ipTable.length == 0) { delete obj.badLoginTable[ip]; }
4070 + }
4071 }
4072 obj.badLoginTableLastClean = 0;
4073 }