mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-09-11 08:55:32 +03:00
Improved support for reverse-proxy certificate loading.
This commit is contained in:
parent
41393d12a8
commit
1f06f91610
@ -196,7 +196,6 @@ module.exports.CertificateOperations = function (parent) {
|
|||||||
|
|
||||||
// Return the certificate of the remote HTTPS server
|
// Return the certificate of the remote HTTPS server
|
||||||
obj.loadCertificate = function (url, hostname, tag, func) {
|
obj.loadCertificate = function (url, hostname, tag, func) {
|
||||||
console.log('loadCertificate', url, hostname);
|
|
||||||
const u = require('url').parse(url);
|
const u = require('url').parse(url);
|
||||||
if (u.protocol == 'https:') {
|
if (u.protocol == 'https:') {
|
||||||
// Read the certificate from HTTPS
|
// Read the certificate from HTTPS
|
||||||
@ -218,7 +217,7 @@ module.exports.CertificateOperations = function (parent) {
|
|||||||
func(url, data, hostname, tag);
|
func(url, data, hostname, tag);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else { func(url, null, tag); }
|
} else { func(url, null, hostname, tag); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if a configuration file exists
|
// Check if a configuration file exists
|
||||||
|
@ -35,6 +35,9 @@ module.exports.makeFilename = function (v) { return v.split('\\').join('').split
|
|||||||
// Move an element from one position in an array to a new position
|
// Move an element from one position in an array to a new position
|
||||||
module.exports.ArrayElementMove = function(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
|
module.exports.ArrayElementMove = function(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
|
||||||
|
|
||||||
|
// Format a string with arguments, "replaces {0} and {1}..."
|
||||||
|
module.exports.format = function (format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
||||||
|
|
||||||
// Print object for HTML
|
// Print object for HTML
|
||||||
module.exports.ObjectToStringEx = function (x, c) {
|
module.exports.ObjectToStringEx = function (x, c) {
|
||||||
var r = "", i;
|
var r = "", i;
|
||||||
|
@ -53,6 +53,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||||||
parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1);
|
parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove this agent from the list of agents with bad web certificates
|
||||||
|
if (obj.badWebCert) { delete parent.wsagentsWithBadWebCerts[obj.badWebCert]; }
|
||||||
|
|
||||||
// Get the current mesh
|
// Get the current mesh
|
||||||
const mesh = parent.meshes[obj.dbMeshKey];
|
const mesh = parent.meshes[obj.dbMeshKey];
|
||||||
|
|
||||||
@ -381,6 +384,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||||||
} else {
|
} else {
|
||||||
// Check that the server hash matches our own web certificate hash (SHA384)
|
// Check that the server hash matches our own web certificate hash (SHA384)
|
||||||
if ((getWebCertHash(domain) != msg.substring(2, 50)) && (getWebCertFullHash(domain) != msg.substring(2, 50))) {
|
if ((getWebCertHash(domain) != msg.substring(2, 50)) && (getWebCertFullHash(domain) != msg.substring(2, 50))) {
|
||||||
|
if (parent.parent.supportsProxyCertificatesRequest !== false) {
|
||||||
|
obj.badWebCert = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64');
|
||||||
|
parent.wsagentsWithBadWebCerts[obj.badWebCert] = obj; // Add this agent to the list of of agents with bad web certificates.
|
||||||
|
parent.parent.updateProxyCertificates();
|
||||||
|
}
|
||||||
parent.agentStats.agentBadWebCertHashCount++;
|
parent.agentStats.agentBadWebCertHashCount++;
|
||||||
console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').');
|
console.log('Agent bad web cert hash (Agent:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (Buffer.from(getWebCertHash(domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').');
|
||||||
console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.');
|
console.log('Agent reported web cert hash:' + (Buffer.from(msg.substring(2, 50), 'binary').toString('hex')) + '.');
|
||||||
|
176
meshcentral.js
176
meshcentral.js
@ -396,34 +396,34 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
|
|
||||||
// Show a list of all configuration files in the database
|
// Show a list of all configuration files in the database
|
||||||
if (obj.args.dblistconfigfiles) {
|
if (obj.args.dblistconfigfiles) {
|
||||||
obj.db.GetAllType('cfile', function (err, docs) { if (err == null) { if (docs.length == 0) { console.log('No files found.'); } else { for (var i in docs) { console.log(docs[i]._id.split('/')[1] + ', ' + Buffer.from(docs[i].data, 'base64').length + ' bytes.'); } } } else { console.log('Unable to read from database.'); } process.exit(); }); return;
|
obj.db.GetAllType('cfile', function (err, docs) { if (err == null) { if (docs.length == 0) { console.log("No files found."); } else { for (var i in docs) { console.log(docs[i]._id.split('/')[1] + ', ' + Buffer.from(docs[i].data, 'base64').length + ' bytes.'); } } } else { console.log('Unable to read from database.'); } process.exit(); }); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the content of a configuration file in the database
|
// Display the content of a configuration file in the database
|
||||||
if (obj.args.dbshowconfigfile) {
|
if (obj.args.dbshowconfigfile) {
|
||||||
if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; }
|
if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; }
|
||||||
obj.db.getConfigFile(obj.args.dbshowconfigfile, function (err, docs) {
|
obj.db.getConfigFile(obj.args.dbshowconfigfile, function (err, docs) {
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
if (docs.length == 0) { console.log('File not found.'); } else {
|
if (docs.length == 0) { console.log("File not found."); } else {
|
||||||
var data = obj.db.decryptData(obj.args.configkey, docs[0].data);
|
var data = obj.db.decryptData(obj.args.configkey, docs[0].data);
|
||||||
if (data == null) { console.log('Invalid config key.'); } else { console.log(data); }
|
if (data == null) { console.log("Invalid config key."); } else { console.log(data); }
|
||||||
}
|
}
|
||||||
} else { console.log('Unable to read from database.'); }
|
} else { console.log("Unable to read from database."); }
|
||||||
process.exit();
|
process.exit();
|
||||||
}); return;
|
}); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all configuration files from database
|
// Delete all configuration files from database
|
||||||
if (obj.args.dbdeleteconfigfiles) {
|
if (obj.args.dbdeleteconfigfiles) {
|
||||||
console.log('Deleting all configuration files from the database...'); obj.db.RemoveAllOfType('cfile', function () { console.log('Done.'); process.exit(); });
|
console.log("Deleting all configuration files from the database..."); obj.db.RemoveAllOfType('cfile', function () { console.log('Done.'); process.exit(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push all relevent files from meshcentral-data into the database
|
// Push all relevent files from meshcentral-data into the database
|
||||||
if (obj.args.dbpushconfigfiles) {
|
if (obj.args.dbpushconfigfiles) {
|
||||||
if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; }
|
if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; }
|
||||||
if ((obj.args.dbpushconfigfiles !== true) && (typeof obj.args.dbpushconfigfiles != 'string')) {
|
if ((obj.args.dbpushconfigfiles !== true) && (typeof obj.args.dbpushconfigfiles != 'string')) {
|
||||||
console.log('Usage: --dbpulldatafiles (path) This will import files from folder into the database');
|
console.log("Usage: --dbpulldatafiles (path) This will import files from folder into the database");
|
||||||
console.log(' --dbpulldatafiles This will import files from meshcentral-data into the db.');
|
console.log(" --dbpulldatafiles This will import files from meshcentral-data into the db.");
|
||||||
process.exit();
|
process.exit();
|
||||||
} else {
|
} else {
|
||||||
if ((obj.args.dbpushconfigfiles == '*') || (obj.args.dbpushconfigfiles === true)) { obj.args.dbpushconfigfiles = obj.datapath; }
|
if ((obj.args.dbpushconfigfiles == '*') || (obj.args.dbpushconfigfiles === true)) { obj.args.dbpushconfigfiles = obj.datapath; }
|
||||||
@ -454,20 +454,20 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
|
|
||||||
// Pull all database files into meshcentral-data
|
// Pull all database files into meshcentral-data
|
||||||
if (obj.args.dbpullconfigfiles) {
|
if (obj.args.dbpullconfigfiles) {
|
||||||
if (typeof obj.args.configkey != 'string') { console.log('Error, --configkey is required.'); process.exit(); return; }
|
if (typeof obj.args.configkey != 'string') { console.log("Error, --configkey is required."); process.exit(); return; }
|
||||||
if (typeof obj.args.dbpullconfigfiles != 'string') {
|
if (typeof obj.args.dbpullconfigfiles != 'string') {
|
||||||
console.log('Usage: --dbpulldatafiles (path)');
|
console.log("Usage: --dbpulldatafiles (path)");
|
||||||
process.exit();
|
process.exit();
|
||||||
} else {
|
} else {
|
||||||
obj.db.GetAllType('cfile', function (err, docs) {
|
obj.db.GetAllType('cfile', function (err, docs) {
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
if (docs.length == 0) {
|
if (docs.length == 0) {
|
||||||
console.log('File not found.');
|
console.log("File not found.");
|
||||||
} else {
|
} else {
|
||||||
for (var i in docs) {
|
for (var i in docs) {
|
||||||
const file = docs[i]._id.split('/')[1], binary = obj.db.decryptData(obj.args.configkey, docs[i].data);
|
const file = docs[i]._id.split('/')[1], binary = obj.db.decryptData(obj.args.configkey, docs[i].data);
|
||||||
if (binary == null) {
|
if (binary == null) {
|
||||||
console.log('Invalid config key.');
|
console.log("Invalid config key.");
|
||||||
} else {
|
} else {
|
||||||
var fullFileName = obj.path.join(obj.args.dbpullconfigfiles, file);
|
var fullFileName = obj.path.join(obj.args.dbpullconfigfiles, file);
|
||||||
try { obj.fs.writeFileSync(fullFileName, binary); } catch (ex) { console.log('Unable to write to ' + fullFileName); process.exit(); return; }
|
try { obj.fs.writeFileSync(fullFileName, binary); } catch (ex) { console.log('Unable to write to ' + fullFileName); process.exit(); return; }
|
||||||
@ -476,7 +476,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('Unable to read from database.');
|
console.log("Unable to read from database.");
|
||||||
}
|
}
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
@ -603,10 +603,10 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
var key = null;
|
var key = null;
|
||||||
if (typeof obj.args.configkey == 'string') { key = obj.args.configkey; }
|
if (typeof obj.args.configkey == 'string') { key = obj.args.configkey; }
|
||||||
else if (typeof obj.args.loadconfigfromdb == 'string') { key = obj.args.loadconfigfromdb; }
|
else if (typeof obj.args.loadconfigfromdb == 'string') { key = obj.args.loadconfigfromdb; }
|
||||||
if (key == null) { console.log('Error, --configkey is required.'); process.exit(); return; }
|
if (key == null) { console.log("Error, --configkey is required."); process.exit(); return; }
|
||||||
obj.db.getAllConfigFiles(key, function (configFiles) {
|
obj.db.getAllConfigFiles(key, function (configFiles) {
|
||||||
if (configFiles == null) { console.log('Error, no configuration files found or invalid configkey.'); process.exit(); return; }
|
if (configFiles == null) { console.log("Error, no configuration files found or invalid configkey."); process.exit(); return; }
|
||||||
if (!configFiles['config.json']) { console.log('Error, could not file config.json from database.'); process.exit(); return; }
|
if (!configFiles['config.json']) { console.log("Error, could not file config.json from database."); process.exit(); return; }
|
||||||
obj.configurationFiles = configFiles;
|
obj.configurationFiles = configFiles;
|
||||||
|
|
||||||
// Parse the new configuration file
|
// Parse the new configuration file
|
||||||
@ -744,9 +744,9 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
var adminname = obj.args.admin.split('/');
|
var adminname = obj.args.admin.split('/');
|
||||||
if (adminname.length == 1) { adminname = 'user//' + adminname[0]; }
|
if (adminname.length == 1) { adminname = 'user//' + adminname[0]; }
|
||||||
else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; }
|
else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; }
|
||||||
else { console.log('Invalid administrator name.'); process.exit(); return; }
|
else { console.log("Invalid administrator name."); process.exit(); return; }
|
||||||
obj.db.Get(adminname, function (err, user) {
|
obj.db.Get(adminname, function (err, user) {
|
||||||
if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; }
|
if (user.length != 1) { console.log("Invalid user name."); process.exit(); return; }
|
||||||
user[0].siteadmin = 4294967295; // 0xFFFFFFFF
|
user[0].siteadmin = 4294967295; // 0xFFFFFFFF
|
||||||
obj.db.Set(user[0], function () {
|
obj.db.Set(user[0], function () {
|
||||||
if (user[0].domain == '') { console.log('User ' + user[0].name + ' set to site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' set to site administrator.'); }
|
if (user[0].domain == '') { console.log('User ' + user[0].name + ' set to site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' set to site administrator.'); }
|
||||||
@ -762,9 +762,9 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
var adminname = obj.args.unadmin.split('/');
|
var adminname = obj.args.unadmin.split('/');
|
||||||
if (adminname.length == 1) { adminname = 'user//' + adminname[0]; }
|
if (adminname.length == 1) { adminname = 'user//' + adminname[0]; }
|
||||||
else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; }
|
else if (adminname.length == 2) { adminname = 'user/' + adminname[0] + '/' + adminname[1]; }
|
||||||
else { console.log('Invalid administrator name.'); process.exit(); return; }
|
else { console.log("Invalid administrator name."); process.exit(); return; }
|
||||||
obj.db.Get(adminname, function (err, user) {
|
obj.db.Get(adminname, function (err, user) {
|
||||||
if (user.length != 1) { console.log('Invalid user name.'); process.exit(); return; }
|
if (user.length != 1) { console.log("Invalid user name."); process.exit(); return; }
|
||||||
if (user[0].siteadmin) { delete user[0].siteadmin; }
|
if (user[0].siteadmin) { delete user[0].siteadmin; }
|
||||||
obj.db.Set(user[0], function () {
|
obj.db.Set(user[0], function () {
|
||||||
if (user[0].domain == '') { console.log('User ' + user[0].name + ' is not a site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' is not a site administrator.'); }
|
if (user[0].domain == '') { console.log('User ' + user[0].name + ' is not a site administrator.'); } else { console.log('User ' + user[0].name + ' of domain ' + user[0].domain + ' is not a site administrator.'); }
|
||||||
@ -793,15 +793,15 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
while (obj.dbconfig.amtWsEventSecret == null) { process.nextTick(); }
|
while (obj.dbconfig.amtWsEventSecret == null) { process.nextTick(); }
|
||||||
var username = buf.toString('hex');
|
var username = buf.toString('hex');
|
||||||
var nodeid = obj.args.getwspass;
|
var nodeid = obj.args.getwspass;
|
||||||
var pass = obj.crypto.createHash('sha384').update(username.toLowerCase() + ":" + nodeid + ":" + obj.dbconfig.amtWsEventSecret).digest("base64").substring(0, 12).split("/").join("x").split("\\").join("x");
|
var pass = obj.crypto.createHash('sha384').update(username.toLowerCase() + ':' + nodeid + ':' + obj.dbconfig.amtWsEventSecret).digest('base64').substring(0, 12).split('/').join('x').split('\\').join('x');
|
||||||
console.log('--- Intel(r) AMT WSMAN eventing credentials ---');
|
console.log("--- Intel(r) AMT WSMAN eventing credentials ---");
|
||||||
console.log('Username: ' + username);
|
console.log("Username: " + username);
|
||||||
console.log('Password: ' + pass);
|
console.log("Password: " + pass);
|
||||||
console.log('Argument: ' + nodeid);
|
console.log("Argument: " + nodeid);
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log('Invalid NodeID.');
|
console.log("Invalid NodeID.");
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -809,7 +809,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
|
|
||||||
// Start plugin manager if configuration allows this.
|
// Start plugin manager if configuration allows this.
|
||||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
|
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
|
||||||
obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj);
|
obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the default meshcore and meshcmd
|
// Load the default meshcore and meshcmd
|
||||||
@ -838,7 +838,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
if (obj.letsencrypt != null) {
|
if (obj.letsencrypt != null) {
|
||||||
obj.letsencrypt.getCertificate(certs, obj.StartEx3); // Use Let's Encrypt certificate
|
obj.letsencrypt.getCertificate(certs, obj.StartEx3); // Use Let's Encrypt certificate
|
||||||
} else {
|
} else {
|
||||||
console.log('ERROR: Unable to setup GreenLock module.');
|
console.log("ERROR: Unable to setup GreenLock module.");
|
||||||
obj.StartEx3(certs); // Let's Encrypt did not load, just use the configured certificates
|
obj.StartEx3(certs); // Let's Encrypt did not load, just use the configured certificates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -847,50 +847,23 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
|
|
||||||
// Start the server with the given certificates, but check if we have web certificates to load
|
// Start the server with the given certificates, but check if we have web certificates to load
|
||||||
obj.StartEx3 = function (certs) {
|
obj.StartEx3 = function (certs) {
|
||||||
var i, webCertLoadCount = 0;
|
|
||||||
obj.certificates = certs;
|
obj.certificates = certs;
|
||||||
obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators
|
obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators
|
||||||
|
|
||||||
// Load any domain web certificates
|
// Load any domain web certificates
|
||||||
for (i in obj.config.domains) {
|
for (var i in obj.config.domains) {
|
||||||
// Load any Intel AMT ACM activation certificates
|
// Load any Intel AMT ACM activation certificates
|
||||||
obj.certificateOperations.loadIntelAmtAcmCerts(obj.config.domains[i].amtacmactivation);
|
obj.certificateOperations.loadIntelAmtAcmCerts(obj.config.domains[i].amtacmactivation);
|
||||||
|
|
||||||
if (obj.config.domains[i].certurl != null) {
|
if (typeof obj.config.domains[i].certurl == 'string') {
|
||||||
// Fix the URL and add 'https://' if needed
|
obj.supportsProxyCertificatesRequest = true; // If a certurl is set, enable proxy cert requests
|
||||||
|
// Then, fix the URL and add 'https://' if needed
|
||||||
if (obj.config.domains[i].certurl.indexOf('://') < 0) { obj.config.domains[i].certurl = 'https://' + obj.config.domains[i].certurl; }
|
if (obj.config.domains[i].certurl.indexOf('://') < 0) { obj.config.domains[i].certurl = 'https://' + obj.config.domains[i].certurl; }
|
||||||
|
|
||||||
// Load web certs
|
|
||||||
webCertLoadCount++;
|
|
||||||
var dnsname = obj.config.domains[i].dns;
|
|
||||||
if ((dnsname == null) && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; }
|
|
||||||
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) {
|
|
||||||
if (cert != null) {
|
|
||||||
// Hash the entire cert
|
|
||||||
var hash = obj.crypto.createHash('sha384').update(Buffer.from(cert, 'binary')).digest('hex');
|
|
||||||
if (xdomain.certhash != hash) { xdomain.certkeyhash = hash; xdomain.certhash = hash; }
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
|
|
||||||
var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert));
|
|
||||||
xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
|
|
||||||
//console.log('V1: ' + xdomain.certkeyhash);
|
|
||||||
} catch (ex) { }
|
|
||||||
|
|
||||||
console.log('Loaded web certificate from \"' + url + '\", host: \"' + xhostname + '\"');
|
|
||||||
console.log(' SHA384 cert hash: ' + xdomain.certhash);
|
|
||||||
if (xdomain.certhash != xdomain.certkeyhash) { console.log(' SHA384 key hash: ' + xdomain.certkeyhash); }
|
|
||||||
} else {
|
|
||||||
console.log('Failed to load web certificate at: \"' + url + '\", host: \"' + xhostname + '\"');
|
|
||||||
}
|
|
||||||
webCertLoadCount--;
|
|
||||||
if (webCertLoadCount == 0) { obj.StartEx4(); } // Done loading all certificates
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No certificate to load, start the server
|
if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(); }
|
||||||
if (webCertLoadCount == 0) { obj.StartEx4(); }
|
obj.StartEx4(); // Keep going
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the server with the given certificates
|
// Start the server with the given certificates
|
||||||
@ -903,7 +876,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
// Write server version and run mode
|
// Write server version and run mode
|
||||||
var productionMode = (process.env.NODE_ENV && (process.env.NODE_ENV == 'production'));
|
var productionMode = (process.env.NODE_ENV && (process.env.NODE_ENV == 'production'));
|
||||||
var runmode = (obj.args.lanonly ? 2 : (obj.args.wanonly ? 1 : 0));
|
var runmode = (obj.args.lanonly ? 2 : (obj.args.wanonly ? 1 : 0));
|
||||||
console.log('MeshCentral v' + obj.currentVer + ', ' + (['Hybrid (LAN + WAN) mode', 'WAN mode', 'LAN mode'][runmode]) + (productionMode ? ', Production mode.' : '.'));
|
console.log("MeshCentral v" + obj.currentVer + ', ' + (["Hybrid (LAN + WAN) mode", "WAN mode", "LAN mode"][runmode]) + (productionMode ? ", Production mode." : '.'));
|
||||||
|
|
||||||
// Check that no sub-domains have the same DNS as the parent
|
// Check that no sub-domains have the same DNS as the parent
|
||||||
for (i in obj.config.domains) {
|
for (i in obj.config.domains) {
|
||||||
@ -1029,9 +1002,9 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
|
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
|
||||||
}, 300000);
|
}, 300000);
|
||||||
|
|
||||||
obj.debug('main', 'Server started');
|
obj.debug('main', "Server started");
|
||||||
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
|
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
|
||||||
obj.updateServerState('state', 'running');
|
obj.updateServerState('state', "running");
|
||||||
|
|
||||||
// Setup auto-backup defaults
|
// Setup auto-backup defaults
|
||||||
if (obj.config.settings.autobackup == null) { obj.config.settings.autobackup = { backupintervalhours: 24, keeplastdaysbackup: 10 }; }
|
if (obj.config.settings.autobackup == null) { obj.config.settings.autobackup = { backupintervalhours: 24, keeplastdaysbackup: 10 }; }
|
||||||
@ -1045,6 +1018,61 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Refresh any certificate hashs from the reverse proxy
|
||||||
|
obj.pendingProxyCertificatesRequests = 0;
|
||||||
|
obj.lastProxyCertificatesRequest = null;
|
||||||
|
obj.supportsProxyCertificatesRequest = false;
|
||||||
|
obj.updateProxyCertificates = function () {
|
||||||
|
var i;
|
||||||
|
if ((obj.pendingProxyCertificatesRequests > 0) || (obj.supportsProxyCertificatesRequest == false)) { return; }
|
||||||
|
if ((obj.lastProxyCertificatesRequest != null) && ((Date.now() - obj.lastProxyCertificatesRequest) < 120000)) { return; } // Don't allow this call more than every 2 minutes.
|
||||||
|
obj.lastProxyCertificatesRequest = Date.now();
|
||||||
|
|
||||||
|
// Load any domain web certificates
|
||||||
|
for (i in obj.config.domains) {
|
||||||
|
if (obj.config.domains[i].certurl != null) {
|
||||||
|
// Load web certs
|
||||||
|
obj.pendingProxyCertificatesRequests++;
|
||||||
|
var dnsname = obj.config.domains[i].dns;
|
||||||
|
if ((dnsname == null) && (obj.config.settings.cert != null)) { dnsname = obj.config.settings.cert; }
|
||||||
|
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, dnsname, obj.config.domains[i], function (url, cert, xhostname, xdomain) {
|
||||||
|
obj.pendingProxyCertificatesRequests--;
|
||||||
|
if (cert != null) {
|
||||||
|
// Hash the entire cert
|
||||||
|
var hash = obj.crypto.createHash('sha384').update(Buffer.from(cert, 'binary')).digest('hex');
|
||||||
|
if (xdomain.certhash != hash) { // The certificate has changed.
|
||||||
|
xdomain.certkeyhash = hash;
|
||||||
|
xdomain.certhash = hash;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
|
||||||
|
var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert));
|
||||||
|
xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
|
||||||
|
//console.log('V1: ' + xdomain.certkeyhash);
|
||||||
|
} catch (ex) {
|
||||||
|
delete xdomain.certkeyhash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.webserver) {
|
||||||
|
obj.webserver.webCertificateHashs[xdomain.id] = obj.webserver.webCertificateFullHashs[xdomain.id] = Buffer.from(hash, 'hex').toString('binary');
|
||||||
|
if (xdomain.certkeyhash != null) { obj.webserver.webCertificateHashs[xdomain.id] = Buffer.from(xdomain.certkeyhash, 'hex').toString('binary'); }
|
||||||
|
|
||||||
|
// Disconnect all agents with bad web certificates
|
||||||
|
for (var i in obj.webserver.wsagentsWithBadWebCerts) { obj.webserver.wsagentsWithBadWebCerts[i].close(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(obj.common.format("Loaded web certificate from \"{0}\", host: \"{1}\"", url, xhostname));
|
||||||
|
console.log(obj.common.format(" SHA384 cert hash: {0}", xdomain.certhash));
|
||||||
|
if ((xdomain.certkeyhash != null) && (xdomain.certhash != xdomain.certkeyhash)) { console.log(obj.common.format(" SHA384 key hash: {0}", xdomain.certkeyhash)); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(obj.common.format("Failed to load web certificate at: \"{0}\", host: \"{1}\"", url, xhostname));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Perform maintenance operations (called every hour)
|
// Perform maintenance operations (called every hour)
|
||||||
obj.maintenanceActions = function () {
|
obj.maintenanceActions = function () {
|
||||||
// Check for self-update that targets a specific version
|
// Check for self-update that targets a specific version
|
||||||
@ -1068,19 +1096,19 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
if (!obj.db) return;
|
if (!obj.db) return;
|
||||||
|
|
||||||
// Dispatch an event saying the server is now stopping
|
// Dispatch an event saying the server is now stopping
|
||||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' });
|
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: "Server stopped" });
|
||||||
|
|
||||||
// Set all nodes to power state of unknown (0)
|
// Set all nodes to power state of unknown (0)
|
||||||
obj.db.storePowerEvent({ time: new Date(), nodeid: '*', power: 0, s: 2 }, obj.multiServer, function () { // s:2 indicates that the server is shutting down.
|
obj.db.storePowerEvent({ time: new Date(), nodeid: '*', power: 0, s: 2 }, obj.multiServer, function () { // s:2 indicates that the server is shutting down.
|
||||||
if (restoreFile) {
|
if (restoreFile) {
|
||||||
obj.debug('main', 'Server stopped, updating settings: ' + restoreFile);
|
obj.debug('main', obj.common.format("Server stopped, updating settings: {0}", restoreFile));
|
||||||
console.log('Updating settings folder...');
|
console.log("Updating settings folder...");
|
||||||
|
|
||||||
var yauzl = require("yauzl");
|
var yauzl = require('yauzl');
|
||||||
yauzl.open(restoreFile, { lazyEntries: true }, function (err, zipfile) {
|
yauzl.open(restoreFile, { lazyEntries: true }, function (err, zipfile) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
zipfile.readEntry();
|
zipfile.readEntry();
|
||||||
zipfile.on("entry", function (entry) {
|
zipfile.on('entry', function (entry) {
|
||||||
if (/\/$/.test(entry.fileName)) {
|
if (/\/$/.test(entry.fileName)) {
|
||||||
// Directory file names end with '/'.
|
// Directory file names end with '/'.
|
||||||
// Note that entires for directories themselves are optional.
|
// Note that entires for directories themselves are optional.
|
||||||
@ -1090,22 +1118,22 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
// file entry
|
// file entry
|
||||||
zipfile.openReadStream(entry, function (err, readStream) {
|
zipfile.openReadStream(entry, function (err, readStream) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
readStream.on("end", function () { zipfile.readEntry(); });
|
readStream.on('end', function () { zipfile.readEntry(); });
|
||||||
// console.log('Extracting:', obj.getConfigFilePath(entry.fileName));
|
// console.log('Extracting:', obj.getConfigFilePath(entry.fileName));
|
||||||
readStream.pipe(obj.fs.createWriteStream(obj.getConfigFilePath(entry.fileName)));
|
readStream.pipe(obj.fs.createWriteStream(obj.getConfigFilePath(entry.fileName)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
zipfile.on("end", function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); });
|
zipfile.on('end', function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
obj.debug('main', 'Server stopped');
|
obj.debug('main', "Server stopped");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the server state
|
// Update the server state
|
||||||
obj.updateServerState('state', 'stopped');
|
obj.updateServerState('state', "stopped");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Event Dispatch
|
// Event Dispatch
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.4.3-o",
|
"version": "0.4.3-p",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
23
webserver.js
23
webserver.js
@ -156,20 +156,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main lists
|
// Main lists
|
||||||
obj.wsagents = {}; // NodeId --> Agent
|
obj.wsagents = {}; // NodeId --> Agent
|
||||||
|
obj.wsagentsWithBadWebCerts = {}; // NodeId --> Agent
|
||||||
obj.wsagentsDisconnections = {};
|
obj.wsagentsDisconnections = {};
|
||||||
obj.wsagentsDisconnectionsTimer = null;
|
obj.wsagentsDisconnectionsTimer = null;
|
||||||
obj.duplicateAgentsLog = {};
|
obj.duplicateAgentsLog = {};
|
||||||
obj.wssessions = {}; // UserId --> Array Of Sessions
|
obj.wssessions = {}; // UserId --> Array Of Sessions
|
||||||
obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd)
|
obj.wssessions2 = {}; // "UserId + SessionRnd" --> Session (Note that the SessionId is the UserId + / + SessionRnd)
|
||||||
obj.wsPeerSessions = {}; // ServerId --> Array Of "UserId + SessionRnd"
|
obj.wsPeerSessions = {}; // ServerId --> Array Of "UserId + SessionRnd"
|
||||||
obj.wsPeerSessions2 = {}; // "UserId + SessionRnd" --> ServerId
|
obj.wsPeerSessions2 = {}; // "UserId + SessionRnd" --> ServerId
|
||||||
obj.wsPeerSessions3 = {}; // ServerId --> UserId --> [ SessionId ]
|
obj.wsPeerSessions3 = {}; // ServerId --> UserId --> [ SessionId ]
|
||||||
obj.sessionsCount = {}; // Merged session counters, used when doing server peering. UserId --> SessionCount
|
obj.sessionsCount = {}; // Merged session counters, used when doing server peering. UserId --> SessionCount
|
||||||
obj.wsrelays = {}; // Id -> Relay
|
obj.wsrelays = {}; // Id -> Relay
|
||||||
obj.wsPeerRelays = {}; // Id -> { ServerId, Time }
|
obj.wsPeerRelays = {}; // Id -> { ServerId, Time }
|
||||||
var tlsSessionStore = {}; // Store TLS session information for quick resume.
|
var tlsSessionStore = {}; // Store TLS session information for quick resume.
|
||||||
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
|
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
|
||||||
|
|
||||||
// Setup randoms
|
// Setup randoms
|
||||||
obj.crypto.randomBytes(48, function (err, buf) { obj.httpAuthRandom = buf; });
|
obj.crypto.randomBytes(48, function (err, buf) { obj.httpAuthRandom = buf; });
|
||||||
|
Loading…
Reference in New Issue
Block a user