Implemented optional 2nd factor skip for some IP addresses

Ylian Saint-Hilaire committed Nov 28, 2019 at 20:57 UTC 664eb63c636a32ddccf9efcc693eb787fccf059f
3 files changed +18 -6
meshcentral.js
+7
@@ -728,6 +728,13 @@ function CreateMeshCentralServer(config, args) {
728 if (typeof obj.config.domains[i].userblockedip == 'string') { if (obj.config.domains[i].userblockedip == '') { obj.config.domains[i].userblockedip = null; } else { obj.config.domains[i].userblockedip = obj.config.domains[i].userallowedip.split(','); } }
729 if (typeof obj.config.domains[i].agentallowedip == 'string') { if (obj.config.domains[i].agentallowedip == '') { obj.config.domains[i].agentallowedip = null; } else { obj.config.domains[i].agentallowedip = obj.config.domains[i].agentallowedip.split(','); } }
730 if (typeof obj.config.domains[i].agentblockedip == 'string') { if (obj.config.domains[i].agentblockedip == '') { obj.config.domains[i].agentblockedip = null; } else { obj.config.domains[i].agentblockedip = obj.config.domains[i].agentblockedip.split(','); } }
731 + if ((obj.config.domains[i].passwordrequirements != null) && (typeof obj.config.domains[i].passwordrequirements == 'object')) {
732 + if (typeof obj.config.domains[i].passwordrequirements.skip2factor == 'string') {
733 + obj.config.domains[i].passwordrequirements.skip2factor = obj.config.domains[i].passwordrequirements.skip2factor.split(',');
734 + } else {
735 + delete obj.config.domains[i].passwordrequirements.skip2factor;
736 + }
737 + }
738 if ((obj.config.domains[i].auth == 'ldap') && (typeof obj.config.domains[i].ldapoptions != 'object')) {
739 if (i == '') { console.log("ERROR: Default domain is LDAP, but is missing LDAPOptions."); } else { console.log("ERROR: Domain '" + i + "' is LDAP, but is missing LDAPOptions."); }
740 process.exit();
sample-config.json
+1 -1
@@ -76,7 +76,7 @@
76 "_NewAccountsRights": [ "nonewgroups", "notools" ],
77 "Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
78 "_CertUrl": "https://192.168.2.106:443/",
79 - "_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90, "force2factor": true },
79 + "_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90, "force2factor": true, "skip2factor": "127.0.0.1,192.168.2.0/24" },
80 "_AgentNoProxy": true,
81 "_GeoLocation": true,
82 "_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
webserver.js
+10 -5
@@ -511,7 +511,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
511 }
512
513 // Return true if this user has 2-step auth active
514 - function checkUserOneTimePasswordRequired(domain, user) {
514 + function checkUserOneTimePasswordRequired(domain, user, req) {
515 + // Check if we can skip 2nd factor auth because of the source IP address
516 + if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
517 + for (var i in domain.passwordrequirements.skip2factor) { if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) return false; }
518 + }
519 + // Check if a 2nd factor is present
520 return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
521 }
522
@@ -657,7 +662,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
662 var user = obj.users[userid];
663
664 // Check if this user has 2-step login active
660 - if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user)) {
665 + if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user, req)) {
666 checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
667 if (result == false) {
668 var randomWaitTime = 0;
@@ -1011,7 +1016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1016 var responseSent = false;
1017 for (var i in docs) {
1018 var user = docs[i];
1014 - if (checkUserOneTimePasswordRequired(domain, user) == true) {
1019 + if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
1020 // Second factor setup, request it now.
1021 checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
1022 if (result == false) {
@@ -3503,7 +3508,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3508 var user = obj.users[userid];
3509 if ((err == null) && (user)) {
3510 // Check if a 2nd factor is needed
3506 - if (checkUserOneTimePasswordRequired(domain, user) == true) {
3511 + if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3512 if (typeof req.query.token != 'string') {
3513 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3514 } else {
@@ -3558,7 +3563,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3563 var user = obj.users[userid];
3564 if ((err == null) && (user)) {
3565 // Check if a 2nd factor is needed
3561 - if (checkUserOneTimePasswordRequired(domain, user) == true) {
3566 + if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3567 if (s.length != 3) {
3568 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3569 } else {