mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-09-11 08:55:32 +03:00
Improved charts, added no2factorauth switch
This commit is contained in:
parent
613ebe293a
commit
0b12b8c03a
4
db.js
4
db.js
@ -124,12 +124,13 @@ module.exports.CreateDB = function (parent) {
|
||||
// Check if we need to reset indexes
|
||||
var indexesByName = {}, indexCount = 0;
|
||||
for (var i in indexes) { indexesByName[indexes[i].name] = indexes[i]; indexCount++; }
|
||||
if ((indexCount != 2) || (indexesByName['ExpireTime1'] == null)) {
|
||||
if ((indexCount != 3) || (indexesByName['ExpireTime1'] == null)) {
|
||||
// Reset all indexes
|
||||
console.log('Resetting server stats indexes...');
|
||||
obj.serverstatsfile.dropIndexes(function (err) {
|
||||
// Create all indexes
|
||||
obj.serverstatsfile.createIndex({ "time": 1 }, { expireAfterSeconds: expireServerStatsSeconds, name: 'ExpireTime1' });
|
||||
obj.serverstatsfile.createIndex({ "expire": 1 }, { expireAfterSeconds: 0, name: 'ExpireTime2' }); // Auto-expire events
|
||||
});
|
||||
} else if (indexesByName['ExpireTime1'].expireAfterSeconds != expireServerStatsSeconds) {
|
||||
// Reset the timeout index
|
||||
@ -197,6 +198,7 @@ module.exports.CreateDB = function (parent) {
|
||||
obj.serverstatsfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-stats.db'), autoload: true });
|
||||
obj.serverstatsfile.persistence.setAutocompactionInterval(36000);
|
||||
obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
|
||||
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
|
||||
}
|
||||
|
||||
obj.SetupDatabase = function (func) {
|
||||
|
@ -60,6 +60,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary');
|
||||
obj.loginCookieEncryptionKey = null;
|
||||
obj.serverSelfWriteAllowed = true;
|
||||
obj.serverStatsCounter = Math.floor(Math.random() * 1000);
|
||||
obj.taskLimiter = obj.common.createTaskLimiterQueue(50, 20, 60); // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
|
||||
try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
|
||||
|
||||
@ -802,8 +803,19 @@ function CreateMeshCentralServer(config, args) {
|
||||
|
||||
// Start collecting server stats every 5 minutes
|
||||
setInterval(function () {
|
||||
obj.serverStatsCounter++;
|
||||
var hours = 720; // Start with all events lasting 30 days.
|
||||
if (((obj.serverStatsCounter) % 2) == 1) { hours = 3; } // Half of the event get removed after 3 hours.
|
||||
else if ((math.floor(obj.serverStatsCounter / 2) % 2) == 1) { hours = 8; } // Another half of the event get removed after 8 hours.
|
||||
else if ((math.floor(obj.serverStatsCounter / 4) % 2) == 1) { hours = 24; } // Another half of the event get removed after 24 hours.
|
||||
else if ((math.floor(obj.serverStatsCounter / 8) % 2) == 1) { hours = 48; } // Another half of the event get removed after 48 hours.
|
||||
else if ((math.floor(obj.serverStatsCounter / 16) % 2) == 1) { hours = 72; } // Another half of the event get removed after 72 hours.
|
||||
var expire = new Date();
|
||||
t.setTime(t.getTime() + (60 * 60 * 1000 * hours));
|
||||
|
||||
var data = {
|
||||
time: new Date(),
|
||||
expire: expire,
|
||||
mem: process.memoryUsage(),
|
||||
//cpu: process.cpuUsage(),
|
||||
conn: {
|
||||
@ -1616,7 +1628,13 @@ function getConfig(createSampleConfig) {
|
||||
function InstallModules(modules, func) {
|
||||
var missingModules = [];
|
||||
if (modules.length > 0) {
|
||||
for (var i in modules) { try { var xxmodule = require(modules[i]); } catch (e) { missingModules.push(modules[i]); } }
|
||||
for (var i in modules) {
|
||||
try {
|
||||
var xxmodule = require(modules[i]);
|
||||
} catch (e) {
|
||||
if (previouslyInstalledModules[modules[i]] !== true) { previouslyInstalledModules[modules[i]] = true; missingModules.push(modules[i]); }
|
||||
}
|
||||
}
|
||||
if (missingModules.length > 0) { InstallModule(missingModules.shift(), InstallModules, modules, func); } else { func(); }
|
||||
}
|
||||
}
|
||||
@ -1641,6 +1659,7 @@ process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop();
|
||||
|
||||
// Load the really basic modules
|
||||
var meshserver = null;
|
||||
var previouslyInstalledModules = { };
|
||||
function mainStart(args) {
|
||||
// Check the NodeJS is version 6 or better.
|
||||
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; }
|
||||
@ -1664,7 +1683,6 @@ function mainStart(args) {
|
||||
if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
|
||||
if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
|
||||
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
|
||||
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
|
||||
|
||||
// Get the current node version
|
||||
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
@ -1672,8 +1690,13 @@ function mainStart(args) {
|
||||
// If running NodeJS < 8, install "util.promisify"
|
||||
if (nodeVersion < 8) { modules.push('util.promisify'); }
|
||||
|
||||
// Setup 2nd factor authentication
|
||||
if (config.settings.no2factorauth !== true) {
|
||||
// Setup YubiKey OTP if configured
|
||||
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
|
||||
// if not all SSPI, WebAuthn/FIDO2 or U2F support depending on the NodeJS version. FIDO2 does not work below NodeJS 8.x
|
||||
if (allsspi == false) { modules.push('otplib'); if (nodeVersion >= 8) { modules.push('@davedoesdev/fido2-lib'); } else { modules.push('authdog'); } }
|
||||
}
|
||||
|
||||
// Install any missing modules and launch the server
|
||||
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
|
||||
|
@ -1896,6 +1896,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'otp-hkey-yubikey-add':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
// Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/
|
||||
var yubikeyotp = null;
|
||||
try { yubikeyotp = require('yubikeyotp'); } catch (ex) { }
|
||||
@ -1945,6 +1947,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'otp-hkey-setup-request':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
|
||||
@ -1971,6 +1975,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'otp-hkey-setup-response':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
|
||||
@ -1997,6 +2003,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'webauthn-startregister':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
// Check is 2-step login is supported
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
|
||||
@ -2019,6 +2027,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
case 'webauthn-endregister':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
|
||||
|
||||
// Figure out the origin
|
||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.3.1-a",
|
||||
"version": "0.3.1-h",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -16,7 +16,7 @@
|
||||
"jwk-to-pem": "2.0.1",
|
||||
"node-jose": "1.1.3",
|
||||
"pkijs": "2.1.58",
|
||||
"psl": "1.1.29"
|
||||
"psl": "1.1.31"
|
||||
}
|
||||
},
|
||||
"@peculiar/asn1-schema": {
|
||||
@ -50,9 +50,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.3.tgz",
|
||||
"integrity": "sha512-2lhc7S28vo8FwR3Jv3Ifyd77AxEsx+Nl9ajWiac6/eWuvZ84zPK4RE05pfqcn3acIzlZDpQj5F1rIKQZX3ptLQ=="
|
||||
"version": "10.14.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz",
|
||||
"integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg=="
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.5",
|
||||
@ -1638,9 +1638,9 @@
|
||||
}
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.1.29",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
|
||||
"integrity": "sha1-YPWA02AXC7cip5fMcEQR5tqFDGc="
|
||||
"version": "1.1.31",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
|
||||
"integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
@ -1652,7 +1652,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.4.tgz",
|
||||
"integrity": "sha512-lBDyLfPIWZjxHr6Nnl83/iaZgVLczDcpEqWdqRnghzBKXifRU/7D5T6JPYWUAm0sJdFeF9+sNTKto6dj/3P/Kg==",
|
||||
"requires": {
|
||||
"@types/node": "10.14.3",
|
||||
"@types/node": "10.14.4",
|
||||
"tslib": "1.9.3"
|
||||
}
|
||||
},
|
||||
@ -1920,7 +1920,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||
"requires": {
|
||||
"psl": "1.1.29",
|
||||
"psl": "1.1.31",
|
||||
"punycode": "1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.3.1-h",
|
||||
"version": "0.3.1-i",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
@ -30,6 +30,7 @@
|
||||
},
|
||||
"_TlsOffload": true,
|
||||
"_MpsTlsOffload": true,
|
||||
"_No2FactorAuth": true,
|
||||
"_WebRtConfig": {
|
||||
"iceServers": [
|
||||
{ "urls": "stun:stun.services.mozilla.com" },
|
||||
|
File diff suppressed because one or more lines are too long
@ -759,9 +759,11 @@
|
||||
<option value=168>Last week</option>
|
||||
<option value=5040>Last 30 days</option>
|
||||
</select>
|
||||
<img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>
|
||||
</div>
|
||||
<div>
|
||||
<input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
|
||||
<input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X
|
||||
</div>
|
||||
</div>
|
||||
<canvas id=serverMainStats style="height:calc(100vh - 250px);max-height:calc(100vh - 250px);width:100%"></canvas>
|
||||
@ -7264,7 +7266,7 @@
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [{ type: 'time', time: { tooltipFormat: 'll HH:mm' }, display: true, scaleLabel: { display: false, labelString: '' } }],
|
||||
yAxes: [{ display: true, scaleLabel: { display: true, labelString: '' } }]
|
||||
yAxes: [{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '' } }]
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -7295,7 +7297,11 @@
|
||||
} */
|
||||
updateServerTimelineHours();
|
||||
}
|
||||
function updateServerTimelineHours() { serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) }; window.serverMainStats.update(); }
|
||||
function updateServerTimelineHours() {
|
||||
serverTimelineConfig.options.scales.yAxes[0].type = (Q('p40log').checked ? 'logarithmic' : 'linear');
|
||||
serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) };
|
||||
window.serverMainStats.update();
|
||||
}
|
||||
function setupServerTimelineStats() { window.serverMainStats = new Chart(document.getElementById('serverMainStats').getContext('2d'), serverTimelineConfig); }
|
||||
|
||||
function updateServerTimelineStats() {
|
||||
@ -7362,6 +7368,16 @@
|
||||
window.serverMainStats.update();
|
||||
}
|
||||
|
||||
function p40downloadEvents() {
|
||||
var csv = "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss\r\n";
|
||||
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||||
if (serverTimelineStats[i].conn && serverTimelineStats[i].mem) {
|
||||
csv += new Date(serverTimelineStats[i].time) + ', ' + serverTimelineStats[i].conn.ca + ', ' + serverTimelineStats[i].conn.cu + ', ' + serverTimelineStats[i].conn.us + ', ' + serverTimelineStats[i].conn.rs + ', ' + (serverTimelineStats[i].conn.am ? serverTimelineStats[i].conn.am : '') + ', ' + serverTimelineStats[i].mem.external + ', ' + serverTimelineStats[i].mem.heapUsed + ', ' + serverTimelineStats[i].mem.heapTotal + ', ' + serverTimelineStats[i].mem.rss + '\r\n';
|
||||
}
|
||||
}
|
||||
saveAs(new Blob([csv], { type: "application/octet-stream" }), "ServerStats.csv");
|
||||
}
|
||||
|
||||
//
|
||||
// POPUP DIALOG
|
||||
//
|
||||
|
12
webserver.js
12
webserver.js
@ -328,12 +328,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Return true if this user has 2-step auth active
|
||||
function checkUserOneTimePasswordRequired(domain, user) {
|
||||
return ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0)));
|
||||
return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
|
||||
}
|
||||
|
||||
// Check the 2-step auth token
|
||||
function checkUserOneTimePassword(req, domain, user, token, hwtoken, func) {
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true));
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true) && (parent.config.settings.no2factorauth !== true));
|
||||
if (twoStepLoginSupported == false) { func(true); return; };
|
||||
|
||||
// Check hardware key
|
||||
@ -1157,19 +1157,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
|
||||
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
|
||||
if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA
|
||||
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 0x00000020; } // Allow site within iframe
|
||||
if (parent.config.settings.allowframing == true) { features += 0x00000020; } // Allow site within iframe
|
||||
if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
|
||||
if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now)
|
||||
if (obj.args.clickonce !== false) { features += 0x00000100; } // Enable ClickOnce (Default true)
|
||||
if (obj.args.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
|
||||
if (obj.args.lanonly == true || obj.args.mpsport == 0) { features += 0x00000400; } // No CIRA
|
||||
if ((obj.parent.serverSelfWriteAllowed == true) && (user != null) && (user.siteadmin == 0xFFFFFFFF)) { features += 0x00000800; } // Server can self-write (Allows self-update)
|
||||
if ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported
|
||||
if ((parent.config.settings.no2factorauth !== true) && (domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported
|
||||
if (domain.agentnoproxy === true) { features += 0x00002000; } // Indicates that agents should be installed without using a HTTP proxy
|
||||
if (domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
|
||||
if ((parent.config.settings.no2factorauth !== true) && domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
|
||||
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
||||
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
||||
if (obj.f2l != null) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||
if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||
|
||||
// Create a authentication cookie
|
||||
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
|
||||
|
Loading…
Reference in New Issue
Block a user