mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-22 21:31:35 +03:00
Fixed plugin version matching, 2-factor reuirement + skip, removed GreenLock completely.
This commit is contained in:
parent
5acfc5f0fc
commit
d483872aa6
316
letsEncrypt.js
316
letsEncrypt.js
@ -14,325 +14,12 @@
|
|||||||
/*jshint esversion: 6 */
|
/*jshint esversion: 6 */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// GreenLock Implementation
|
|
||||||
var globalLetsEncrypt = null;
|
|
||||||
module.exports.CreateLetsEncrypt = function (parent) {
|
|
||||||
try {
|
|
||||||
// Get the GreenLock version
|
|
||||||
var greenLockVersion = null;
|
|
||||||
try { greenLockVersion = require('greenlock/package.json').version; } catch (ex) { }
|
|
||||||
if (greenLockVersion == null) {
|
|
||||||
parent.debug('cert', "Initializing Let's Encrypt support");
|
|
||||||
} else {
|
|
||||||
parent.debug('cert', "Initializing Let's Encrypt support, using GreenLock v" + greenLockVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the current node version and support for generateKeyPair
|
|
||||||
if (require('crypto').generateKeyPair == null) { return null; }
|
|
||||||
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 10) { return null; }
|
|
||||||
|
|
||||||
// Try to delete the "./ursa-optional" or "./node_modules/ursa-optional" folder if present.
|
|
||||||
// This is an optional module that GreenLock uses that causes issues.
|
|
||||||
try {
|
|
||||||
const fs = require('fs');
|
|
||||||
if (fs.existsSync(parent.path.join(__dirname, 'ursa-optional'))) { fs.unlinkSync(obj.path.join(__dirname, 'ursa-optional')); }
|
|
||||||
if (fs.existsSync(parent.path.join(__dirname, 'node_modules', 'ursa-optional'))) { fs.unlinkSync(obj.path.join(__dirname, 'node_modules', 'ursa-optional')); }
|
|
||||||
} catch (ex) { }
|
|
||||||
|
|
||||||
// Get GreenLock setup and running.
|
|
||||||
const greenlock = require('greenlock');
|
|
||||||
var obj = {};
|
|
||||||
globalLetsEncrypt = obj;
|
|
||||||
obj.parent = parent;
|
|
||||||
obj.lib = 'greenlock';
|
|
||||||
obj.path = require('path');
|
|
||||||
obj.redirWebServerHooked = false;
|
|
||||||
obj.leDomains = null;
|
|
||||||
obj.leResults = null;
|
|
||||||
obj.leResultsStaging = null;
|
|
||||||
obj.performRestart = false; // Indicates we need to restart the server
|
|
||||||
obj.performMoveToProduction = false; // Indicates we just got a staging certificate and need to move to production
|
|
||||||
obj.runAsProduction = false; // This starts at false and moves to true if staging cert is ok.
|
|
||||||
|
|
||||||
// Setup the certificate storage paths
|
|
||||||
obj.configPath = obj.path.join(obj.parent.datapath, 'letsencrypt3');
|
|
||||||
try { obj.parent.fs.mkdirSync(obj.configPath); } catch (e) { }
|
|
||||||
obj.configPathStaging = obj.path.join(obj.parent.datapath, 'letsencrypt3-staging');
|
|
||||||
try { obj.parent.fs.mkdirSync(obj.configPathStaging); } catch (e) { }
|
|
||||||
|
|
||||||
// Setup Let's Encrypt default configuration
|
|
||||||
obj.leDefaults = { agreeToTerms: true, store: { module: 'greenlock-store-fs', basePath: obj.configPath } };
|
|
||||||
obj.leDefaultsStaging = { agreeToTerms: true, store: { module: 'greenlock-store-fs', basePath: obj.configPathStaging } };
|
|
||||||
|
|
||||||
// Get package and maintainer email
|
|
||||||
const pkg = require('./package.json');
|
|
||||||
var maintainerEmail = null;
|
|
||||||
if (typeof pkg.author == 'string') {
|
|
||||||
// Older NodeJS
|
|
||||||
maintainerEmail = pkg.author;
|
|
||||||
var i = maintainerEmail.indexOf('<');
|
|
||||||
if (i >= 0) { maintainerEmail = maintainerEmail.substring(i + 1); }
|
|
||||||
var i = maintainerEmail.indexOf('>');
|
|
||||||
if (i >= 0) { maintainerEmail = maintainerEmail.substring(0, i); }
|
|
||||||
} else if (typeof pkg.author == 'object') {
|
|
||||||
// Latest NodeJS
|
|
||||||
maintainerEmail = pkg.author.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we need to be in debug mode
|
|
||||||
var ledebug = false;
|
|
||||||
try { ledebug = ((obj.parent.args.debug != null) || (obj.parent.args.debug.indexOf('cert'))); } catch (ex) { }
|
|
||||||
|
|
||||||
// Create the main GreenLock code module for production.
|
|
||||||
var greenlockargs = {
|
|
||||||
parent: obj,
|
|
||||||
packageRoot: __dirname,
|
|
||||||
packageAgent: pkg.name + '/' + pkg.version,
|
|
||||||
manager: obj.path.join(__dirname, 'letsencrypt.js'),
|
|
||||||
maintainerEmail: maintainerEmail,
|
|
||||||
notify: function (ev, args) { if (typeof args == 'string') { parent.debug('cert', ev + ': ' + args); } else { parent.debug('cert', ev + ': ' + JSON.stringify(args)); } },
|
|
||||||
staging: false,
|
|
||||||
debug: ledebug
|
|
||||||
};
|
|
||||||
if (obj.parent.args.debug == null) { greenlockargs.log = function (debug) { }; } // If not in debug mode, ignore all console output from greenlock (makes things clean).
|
|
||||||
obj.le = greenlock.create(greenlockargs);
|
|
||||||
|
|
||||||
// Create the main GreenLock code module for staging.
|
|
||||||
var greenlockargsstaging = {
|
|
||||||
parent: obj,
|
|
||||||
packageRoot: __dirname,
|
|
||||||
packageAgent: pkg.name + '/' + pkg.version,
|
|
||||||
manager: obj.path.join(__dirname, 'letsencrypt.js'),
|
|
||||||
maintainerEmail: maintainerEmail,
|
|
||||||
notify: function (ev, args) { if (typeof args == 'string') { parent.debug('cert', 'Notify: ' + ev + ': ' + args); } else { parent.debug('cert', 'Notify: ' + ev + ': ' + JSON.stringify(args)); } },
|
|
||||||
staging: true,
|
|
||||||
debug: ledebug
|
|
||||||
};
|
|
||||||
if (obj.parent.args.debug == null) { greenlockargsstaging.log = function (debug) { }; } // If not in debug mode, ignore all console output from greenlock (makes things clean).
|
|
||||||
obj.leStaging = greenlock.create(greenlockargsstaging);
|
|
||||||
|
|
||||||
// Hook up GreenLock to the redirection server
|
|
||||||
if (obj.parent.config.settings.rediraliasport === 80) { obj.redirWebServerHooked = true; }
|
|
||||||
else if ((obj.parent.config.settings.rediraliasport == null) && (obj.parent.redirserver.port == 80)) { obj.redirWebServerHooked = true; }
|
|
||||||
|
|
||||||
// Respond to a challenge
|
|
||||||
obj.challenge = function (token, hostname, func) {
|
|
||||||
if (obj.runAsProduction === true) {
|
|
||||||
// Production
|
|
||||||
parent.debug('cert', "Challenge " + hostname + "/" + token);
|
|
||||||
obj.le.challenges.get({ type: 'http-01', servername: hostname, token: token })
|
|
||||||
.then(function (results) { func(results.keyAuthorization); })
|
|
||||||
.catch(function (e) { console.log('LE-ERROR', e); func(null); }); // unexpected error, not related to renewal
|
|
||||||
} else {
|
|
||||||
// Staging
|
|
||||||
parent.debug('cert', "Challenge " + hostname + "/" + token);
|
|
||||||
obj.leStaging.challenges.get({ type: 'http-01', servername: hostname, token: token })
|
|
||||||
.then(function (results) { func(results.keyAuthorization); })
|
|
||||||
.catch(function (e) { console.log('LE-ERROR', e); func(null); }); // unexpected error, not related to renewal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.getCertificate = function(certs, func) {
|
|
||||||
parent.debug('cert', "Getting certs from local store");
|
|
||||||
if (certs.CommonName.indexOf('.') == -1) { console.log("ERROR: Use --cert to setup the default server name before using Let's Encrypt."); func(certs); return; }
|
|
||||||
if (obj.parent.config.letsencrypt == null) { func(certs); return; }
|
|
||||||
if (obj.parent.config.letsencrypt.email == null) { console.log("ERROR: Let's Encrypt email address not specified."); func(certs); return; }
|
|
||||||
if ((obj.parent.redirserver == null) || ((typeof obj.parent.config.settings.rediraliasport === 'number') && (obj.parent.config.settings.rediraliasport !== 80)) || ((obj.parent.config.settings.rediraliasport == null) && (obj.parent.redirserver.port !== 80))) { console.log("ERROR: Redirection web server must be active on port 80 for Let's Encrypt to work."); func(certs); return; }
|
|
||||||
if (obj.redirWebServerHooked !== true) { console.log("ERROR: Redirection web server not setup for Let's Encrypt to work."); func(certs); return; }
|
|
||||||
if ((obj.parent.config.letsencrypt.rsakeysize != null) && (obj.parent.config.letsencrypt.rsakeysize !== 2048) && (obj.parent.config.letsencrypt.rsakeysize !== 3072)) { console.log("ERROR: Invalid Let's Encrypt certificate key size, must be 2048 or 3072."); func(certs); return; }
|
|
||||||
|
|
||||||
// Get the list of domains
|
|
||||||
obj.leDomains = [ certs.CommonName ];
|
|
||||||
if (obj.parent.config.letsencrypt.names != null) {
|
|
||||||
if (typeof obj.parent.config.letsencrypt.names == 'string') { obj.parent.config.letsencrypt.names = obj.parent.config.letsencrypt.names.split(','); }
|
|
||||||
obj.parent.config.letsencrypt.names.map(function (s) { return s.trim(); }); // Trim each name
|
|
||||||
if ((typeof obj.parent.config.letsencrypt.names != 'object') || (obj.parent.config.letsencrypt.names.length == null)) { console.log("ERROR: Let's Encrypt names must be an array in config.json."); func(certs); return; }
|
|
||||||
obj.leDomains = obj.parent.config.letsencrypt.names;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.parent.config.letsencrypt.production !== true) {
|
|
||||||
// We are in staging mode, just go ahead
|
|
||||||
obj.getCertificateEx(certs, func);
|
|
||||||
} else {
|
|
||||||
// We are really in production mode
|
|
||||||
if (obj.runAsProduction === true) {
|
|
||||||
// Staging cert check must have been done already, move to production
|
|
||||||
obj.getCertificateEx(certs, func);
|
|
||||||
} else {
|
|
||||||
// Perform staging certificate check
|
|
||||||
parent.debug('cert', "Checking staging certificate " + obj.leDomains[0] + "...");
|
|
||||||
obj.leStaging.get({ servername: obj.leDomains[0] })
|
|
||||||
.then(function (results) {
|
|
||||||
if (results != null) {
|
|
||||||
// We have a staging certificate, move to production for real
|
|
||||||
parent.debug('cert', "Staging certificate is present, moving to production...");
|
|
||||||
obj.runAsProduction = true;
|
|
||||||
obj.getCertificateEx(certs, func);
|
|
||||||
} else {
|
|
||||||
// No staging certificate
|
|
||||||
parent.debug('cert', "No staging certificate present");
|
|
||||||
func(certs);
|
|
||||||
setTimeout(obj.checkRenewCertificate, 10000); // Check the certificate in 10 seconds.
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (e) {
|
|
||||||
// No staging certificate
|
|
||||||
parent.debug('cert', "No staging certificate present");
|
|
||||||
func(certs);
|
|
||||||
setTimeout(obj.checkRenewCertificate, 10000); // Check the certificate in 10 seconds.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.getCertificateEx = function (certs, func) {
|
|
||||||
// Get the Let's Encrypt certificate from our own storage
|
|
||||||
const xle = (obj.runAsProduction === true)? obj.le : obj.leStaging;
|
|
||||||
xle.get({ servername: obj.leDomains[0] })
|
|
||||||
.then(function (results) {
|
|
||||||
// If we already have real certificates, use them
|
|
||||||
if (results) {
|
|
||||||
if (results.site.altnames.indexOf(certs.CommonName) >= 0) {
|
|
||||||
certs.web.cert = results.pems.cert;
|
|
||||||
certs.web.key = results.pems.privkey;
|
|
||||||
certs.web.ca = [results.pems.chain];
|
|
||||||
}
|
|
||||||
for (var i in obj.parent.config.domains) {
|
|
||||||
if ((obj.parent.config.domains[i].dns != null) && (obj.parent.certificateOperations.compareCertificateNames(results.site.altnames, obj.parent.config.domains[i].dns))) {
|
|
||||||
certs.dns[i].cert = results.pems.cert;
|
|
||||||
certs.dns[i].key = results.pems.privkey;
|
|
||||||
certs.dns[i].ca = [results.pems.chain];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent.debug('cert', "Got certs from local store (" + (obj.runAsProduction ? "Production" : "Staging") + ")");
|
|
||||||
func(certs);
|
|
||||||
|
|
||||||
// Check if the Let's Encrypt certificate needs to be renewed.
|
|
||||||
setTimeout(obj.checkRenewCertificate, 60000); // Check in 1 minute.
|
|
||||||
setInterval(obj.checkRenewCertificate, 86400000); // Check again in 24 hours and every 24 hours.
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
.catch(function (e) {
|
|
||||||
parent.debug('cert', "Unable to get certs from local store (" + (obj.runAsProduction ? "Production" : "Staging") + ")");
|
|
||||||
setTimeout(obj.checkRenewCertificate, 10000); // Check the certificate in 10 seconds.
|
|
||||||
func(certs);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we need to renew the certificate, call this every day.
|
|
||||||
obj.checkRenewCertificate = function () {
|
|
||||||
parent.debug('cert', "Checking certificate for " + obj.leDomains[0] + " (" + (obj.runAsProduction ? "Production" : "Staging") + ")");
|
|
||||||
|
|
||||||
// Setup renew options
|
|
||||||
obj.certCheckStart = Date.now();
|
|
||||||
const xle = (obj.runAsProduction === true) ? obj.le : obj.leStaging;
|
|
||||||
var renewOptions = { servername: obj.leDomains[0], altnames: obj.leDomains };
|
|
||||||
try {
|
|
||||||
xle.renew(renewOptions)
|
|
||||||
.then(function (results) {
|
|
||||||
if ((results == null) || (typeof results != 'object') || (results.length == 0) || (results[0].error != null)) {
|
|
||||||
parent.debug('cert', "Unable to get a certificate (" + (obj.runAsProduction ? "Production" : "Staging") + ", " + (Date.now() - obj.certCheckStart) + "ms): " + JSON.stringify(results));
|
|
||||||
} else {
|
|
||||||
parent.debug('cert', "Checks completed (" + (obj.runAsProduction ? "Production" : "Staging") + ", " + (Date.now() - obj.certCheckStart) + "ms): " + JSON.stringify(results));
|
|
||||||
if (obj.performRestart === true) { parent.debug('cert', "Certs changed, restarting..."); obj.parent.performServerCertUpdate(); } // Reset the server, TODO: Reset all peers
|
|
||||||
else if (obj.performMoveToProduction == true) {
|
|
||||||
parent.debug('cert', "Staging certificate received, moving to production...");
|
|
||||||
obj.runAsProduction = true;
|
|
||||||
obj.performMoveToProduction = false;
|
|
||||||
obj.performRestart = true;
|
|
||||||
setTimeout(obj.checkRenewCertificate, 10000); // Check the certificate in 10 seconds.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (ex) {
|
|
||||||
parent.debug('cert', "checkCertificate exception: (" + JSON.stringify(ex) + ")");
|
|
||||||
console.log(ex);
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
|
||||||
parent.debug('cert', "checkCertificate main exception: (" + JSON.stringify(ex) + ")");
|
|
||||||
console.log(ex);
|
|
||||||
return ex;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
} catch (ex) { console.log(ex); } // Unable to start Let's Encrypt
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// GreenLock v3 Manager
|
|
||||||
module.exports.create = function (options) {
|
|
||||||
//console.log('xxx-create', options);
|
|
||||||
var manager = { parent: globalLetsEncrypt };
|
|
||||||
manager.find = async function (options) {
|
|
||||||
try {
|
|
||||||
// GreenLock sometimes has the bad behavior of adding a wildcard cert request, remove it here if needed.
|
|
||||||
if ((options.wildname != null) && (options.wildname != '')) { options.wildname = ''; }
|
|
||||||
if (options.altnames) {
|
|
||||||
var altnames2 = [];
|
|
||||||
for (var i in options.altnames) { if (options.altnames[i].indexOf('*') == -1) { altnames2.push(options.altnames[i]); } }
|
|
||||||
options.altnames = altnames2;
|
|
||||||
}
|
|
||||||
if (options.servernames) {
|
|
||||||
var servernames2 = [];
|
|
||||||
for (var i in options.servernames) { if (options.servernames[i].indexOf('*') == -1) { servernames2.push(options.servernames[i]); } }
|
|
||||||
options.servernames = servernames2;
|
|
||||||
}
|
|
||||||
} catch (ex) { console.log(ex); }
|
|
||||||
return Promise.resolve([{ subject: options.servername, altnames: options.altnames }]);
|
|
||||||
};
|
|
||||||
|
|
||||||
manager.set = function (options) {
|
|
||||||
//console.log('xxx-set', options);
|
|
||||||
manager.parent.parent.debug('cert', "Certificate has been set: " + JSON.stringify(options));
|
|
||||||
if (manager.parent.parent.config.letsencrypt.production == manager.parent.runAsProduction) { manager.parent.performRestart = true; }
|
|
||||||
else if ((manager.parent.parent.config.letsencrypt.production === true) && (manager.parent.runAsProduction === false)) { manager.parent.performMoveToProduction = true; }
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
manager.remove = function (options) {
|
|
||||||
//console.log('xxx-remove', options);
|
|
||||||
manager.parent.parent.debug('cert', "Certificate has been removed: " + JSON.stringify(options));
|
|
||||||
if (manager.parent.parent.config.letsencrypt.production == manager.parent.runAsProduction) { manager.parent.performRestart = true; }
|
|
||||||
else if ((manager.parent.parent.config.letsencrypt.production === true) && (manager.parent.runAsProduction === false)) { manager.parent.performMoveToProduction = true; }
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// set the global config
|
|
||||||
manager.defaults = async function (options) {
|
|
||||||
//console.log('xxx-defaults', options);
|
|
||||||
var r;
|
|
||||||
if (manager.parent.runAsProduction === true) {
|
|
||||||
// Production
|
|
||||||
//console.log('LE-DEFAULTS-Production', options);
|
|
||||||
if (options != null) { for (var i in options) { if (manager.parent.leDefaults[i] == null) { manager.parent.leDefaults[i] = options[i]; } } }
|
|
||||||
r = manager.parent.leDefaults;
|
|
||||||
r.subscriberEmail = manager.parent.parent.config.letsencrypt.email;
|
|
||||||
r.sites = { mainsite: { subject: manager.parent.leDomains[0], altnames: manager.parent.leDomains } };
|
|
||||||
} else {
|
|
||||||
// Staging
|
|
||||||
//console.log('LE-DEFAULTS-Staging', options);
|
|
||||||
if (options != null) { for (var i in options) { if (manager.parent.leDefaultsStaging[i] == null) { manager.parent.leDefaultsStaging[i] = options[i]; } } }
|
|
||||||
r = manager.parent.leDefaultsStaging;
|
|
||||||
r.subscriberEmail = manager.parent.parent.config.letsencrypt.email;
|
|
||||||
r.sites = { mainsite: { subject: manager.parent.leDomains[0], altnames: manager.parent.leDomains } };
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
};
|
|
||||||
|
|
||||||
return manager;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ACME-Client Implementation
|
// ACME-Client Implementation
|
||||||
var globalLetsEncrypt = null;
|
var globalLetsEncrypt = null;
|
||||||
module.exports.CreateLetsEncrypt2 = function (parent) {
|
module.exports.CreateLetsEncrypt = function (parent) {
|
||||||
const acme = require('acme-client');
|
const acme = require('acme-client');
|
||||||
|
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj.lib = 'acme-client';
|
|
||||||
obj.fs = require('fs');
|
obj.fs = require('fs');
|
||||||
obj.path = require('path');
|
obj.path = require('path');
|
||||||
obj.parent = parent;
|
obj.parent = parent;
|
||||||
@ -530,7 +217,6 @@ module.exports.CreateLetsEncrypt2 = function (parent) {
|
|||||||
// Return the status of this module
|
// Return the status of this module
|
||||||
obj.getStats = function () {
|
obj.getStats = function () {
|
||||||
var r = {
|
var r = {
|
||||||
lib: 'acme-client',
|
|
||||||
configOk: obj.configOk,
|
configOk: obj.configOk,
|
||||||
leDomains: obj.leDomains,
|
leDomains: obj.leDomains,
|
||||||
challenges: obj.challenges,
|
challenges: obj.challenges,
|
||||||
|
@ -1041,7 +1041,7 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
|
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
|
||||||
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||||
if (nodeVersion < 7) {
|
if (nodeVersion < 7) {
|
||||||
addServerWarning("Plugin support requires Node v7.0 or higher.");
|
addServerWarning("Plugin support requires Node v7.x or higher.");
|
||||||
delete obj.config.settings.plugins;
|
delete obj.config.settings.plugins;
|
||||||
} else {
|
} else {
|
||||||
obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj);
|
obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj);
|
||||||
@ -1068,15 +1068,11 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
obj.certificateOperations.GetMeshServerCertificate(obj.args, obj.config, function (certs) {
|
obj.certificateOperations.GetMeshServerCertificate(obj.args, obj.config, function (certs) {
|
||||||
// Get the current node version
|
// Get the current node version
|
||||||
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||||
if ((obj.config.letsencrypt == null) || (obj.redirserver == null) || (nodeVersion < 8) || ((obj.config.letsencrypt.lib == 'greenlock') && (require('crypto').generateKeyPair == null))) {
|
if ((obj.config.letsencrypt == null) || (obj.redirserver == null) || (nodeVersion < 8)) {
|
||||||
obj.StartEx3(certs); // Just use the configured certificates
|
obj.StartEx3(certs); // Just use the configured certificates
|
||||||
} else if ((obj.config.letsencrypt != null) && (obj.config.letsencrypt.nochecks == true)) {
|
} else if ((obj.config.letsencrypt != null) && (obj.config.letsencrypt.nochecks == true)) {
|
||||||
// Use Let's Encrypt with no checking
|
// Use Let's Encrypt with no checking
|
||||||
if (obj.config.letsencrypt.lib == 'greenlock') {
|
obj.letsencrypt = require('./letsencrypt.js').CreateLetsEncrypt(obj);
|
||||||
obj.letsencrypt = require('./letsencrypt.js').CreateLetsEncrypt(obj);
|
|
||||||
} else {
|
|
||||||
obj.letsencrypt = require('./letsencrypt.js').CreateLetsEncrypt2(obj);
|
|
||||||
}
|
|
||||||
obj.letsencrypt.getCertificate(certs, obj.StartEx3); // Use Let's Encrypt with no checking, use at your own risk.
|
obj.letsencrypt.getCertificate(certs, obj.StartEx3); // Use Let's Encrypt with no checking, use at your own risk.
|
||||||
} else {
|
} else {
|
||||||
// Check Let's Encrypt settings
|
// Check Let's Encrypt settings
|
||||||
@ -1088,14 +1084,8 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
else if (obj.config.letsencrypt.email.trim() !== obj.config.letsencrypt.email) { leok = false; addServerWarning("Invalid Let's Encrypt email address."); }
|
else if (obj.config.letsencrypt.email.trim() !== obj.config.letsencrypt.email) { leok = false; addServerWarning("Invalid Let's Encrypt email address."); }
|
||||||
else {
|
else {
|
||||||
var le = require('./letsencrypt.js');
|
var le = require('./letsencrypt.js');
|
||||||
try {
|
try { obj.letsencrypt = le.CreateLetsEncrypt(obj); } catch (ex) { console.log(ex); }
|
||||||
if (obj.config.letsencrypt.lib == 'greenlock') {
|
if (obj.letsencrypt == null) { addServerWarning("Unable to setup Let's Encrypt module."); leok = false; }
|
||||||
obj.letsencrypt = le.CreateLetsEncrypt(obj);
|
|
||||||
} else {
|
|
||||||
obj.letsencrypt = le.CreateLetsEncrypt2(obj);
|
|
||||||
}
|
|
||||||
} catch (ex) { console.log(ex); }
|
|
||||||
if (obj.letsencrypt == null) { addServerWarning("Unable to setup GreenLock module."); leok = false; }
|
|
||||||
}
|
}
|
||||||
if (leok == true) {
|
if (leok == true) {
|
||||||
// Check that the email address domain MX resolves.
|
// Check that the email address domain MX resolves.
|
||||||
|
39
meshuser.js
39
meshuser.js
@ -703,26 +703,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
if (parent.parent.letsencrypt == null) {
|
if (parent.parent.letsencrypt == null) {
|
||||||
r = "Let's Encrypt not in use.";
|
r = "Let's Encrypt not in use.";
|
||||||
} else {
|
} else {
|
||||||
if (parent.parent.letsencrypt.lib == 'greenlock') {
|
r = JSON.stringify(parent.parent.letsencrypt.getStats(), null, 4);
|
||||||
var leinfo = {};
|
|
||||||
var greenLockVersion = null;
|
|
||||||
try { greenLockVersion = require('greenlock/package.json').version; } catch (ex) { }
|
|
||||||
if (greenLockVersion) { leinfo.greenLockVer = greenLockVersion; }
|
|
||||||
leinfo.redirWebServerHooked = parent.parent.letsencrypt.redirWebServerHooked;
|
|
||||||
leinfo.leDomains = parent.parent.letsencrypt.leDomains;
|
|
||||||
leinfo.leResults = parent.parent.letsencrypt.leResults;
|
|
||||||
leinfo.leResultsStaging = parent.parent.letsencrypt.leResultsStaging;
|
|
||||||
leinfo.performRestart = parent.parent.letsencrypt.performRestart;
|
|
||||||
leinfo.performMoveToProduction = parent.parent.letsencrypt.performMoveToProduction;
|
|
||||||
leinfo.runAsProduction = parent.parent.letsencrypt.runAsProduction;
|
|
||||||
leinfo.leDefaults = parent.parent.letsencrypt.leDefaults;
|
|
||||||
leinfo.leDefaultsStaging = parent.parent.letsencrypt.leDefaultsStaging;
|
|
||||||
r = JSON.stringify(leinfo, null, 4);
|
|
||||||
} else if (parent.parent.letsencrypt.lib == 'acme-client') {
|
|
||||||
r = JSON.stringify(parent.parent.letsencrypt.getStats(), null, 4);
|
|
||||||
} else {
|
|
||||||
r = 'Unknown module';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -730,14 +711,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
if (parent.parent.letsencrypt == null) {
|
if (parent.parent.letsencrypt == null) {
|
||||||
r = "Let's Encrypt not in use.";
|
r = "Let's Encrypt not in use.";
|
||||||
} else {
|
} else {
|
||||||
if (parent.parent.letsencrypt.lib == 'greenlock') {
|
r = ["CertOK", "Request:NoCert", "Request:Expire", "Request:MissingNames"][parent.parent.letsencrypt.checkRenewCertificate()];
|
||||||
var err = parent.parent.letsencrypt.checkRenewCertificate();
|
|
||||||
if (err == null) { r = "Called Let's Encrypt certificate check."; } else { r = err; }
|
|
||||||
} else if (parent.parent.letsencrypt.lib == 'acme-client') {
|
|
||||||
r = ["CertOK", "Request:NoCert", "Request:Expire", "Request:MissingNames"][parent.parent.letsencrypt.checkRenewCertificate()];
|
|
||||||
} else {
|
|
||||||
r = 'Unknown module';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -745,11 +719,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
if (parent.parent.letsencrypt == null) {
|
if (parent.parent.letsencrypt == null) {
|
||||||
r = "Let's Encrypt not in use.";
|
r = "Let's Encrypt not in use.";
|
||||||
} else {
|
} else {
|
||||||
if (parent.parent.letsencrypt.lib == 'acme-client') {
|
r = parent.parent.letsencrypt.events.join('\r\n');
|
||||||
r = parent.parent.letsencrypt.events.join('\r\n');
|
|
||||||
} else {
|
|
||||||
r = 'Not supported';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -845,6 +815,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
var info = process.memoryUsage();
|
var info = process.memoryUsage();
|
||||||
info.dbType = ['None', 'NeDB', 'MongoJS', 'MongoDB'][parent.db.databaseType];
|
info.dbType = ['None', 'NeDB', 'MongoJS', 'MongoDB'][parent.db.databaseType];
|
||||||
if (parent.db.databaseType == 3) { info.dbChangeStream = parent.db.changeStream; }
|
if (parent.db.databaseType == 3) { info.dbChangeStream = parent.db.changeStream; }
|
||||||
|
if (parent.parent.pluginHandler != null) { info.plugins = []; for (var i in parent.parent.pluginHandler.plugins) { info.plugins.push(i); } }
|
||||||
try { info.nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); } catch (ex) { }
|
try { info.nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); } catch (ex) { }
|
||||||
try { info.currentVer = parent.parent.currentVer; } catch (ex) { }
|
try { info.currentVer = parent.parent.currentVer; } catch (ex) { }
|
||||||
try { info.platform = process.platform; } catch (ex) { }
|
try { info.platform = process.platform; } catch (ex) { }
|
||||||
@ -3121,7 +3092,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
}
|
}
|
||||||
case 'otp-hkey-get':
|
case 'otp-hkey-get':
|
||||||
{
|
{
|
||||||
// Check is 2-step login is supported
|
// Check if 2-step login is supported
|
||||||
const twoStepLoginSupported = ((parent.parent.config.settings.no2factorauth !== true) && (domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.nousers !== true));
|
const twoStepLoginSupported = ((parent.parent.config.settings.no2factorauth !== true) && (domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.nousers !== true));
|
||||||
if (twoStepLoginSupported == false) break;
|
if (twoStepLoginSupported == false) break;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.5.0-b",
|
"version": "0.5.0-d",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
@ -274,6 +274,19 @@ module.exports.pluginHandler = function (parent) {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// MeshCentral doesn't adhere to semantic versioning (due to the -<alpha_char> at the end of the version)
|
||||||
|
// Convert 1.2.3-a to 1.2.3-3 where the letter is converted to a number.
|
||||||
|
function versionToNumber(ver) { var x = ver.split('-'); if (x.length != 2) return ver; x[1] = x[1].toLowerCase().charCodeAt(0) - 96; return x.join('.'); }
|
||||||
|
|
||||||
|
// Check if the current version of MeshCentral is at least the minimal required.
|
||||||
|
function checkMeshCentralVersion(current, minimal) {
|
||||||
|
if (minimal.startsWith('>=')) { minimal = minimal.substring(2); }
|
||||||
|
var c = versionToNumber(current).split('.'), m = versionToNumber(minimal).split('.');
|
||||||
|
if (c.length != m.length) return false;
|
||||||
|
for (var i = 0; i < c.length; i++) { var cx = parseInt(c[i]), cm = parseInt(m[i]); if (cx > cm) { return true; } if (cx < cm) { return false; } }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
obj.getPluginLatest = function () {
|
obj.getPluginLatest = function () {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
parent.db.getPlugins(function (err, plugins) {
|
parent.db.getPlugins(function (err, plugins) {
|
||||||
@ -294,16 +307,12 @@ module.exports.pluginHandler = function (parent) {
|
|||||||
});
|
});
|
||||||
if (curconf == null) reject("Some plugin configs could not be parsed");
|
if (curconf == null) reject("Some plugin configs could not be parsed");
|
||||||
var s = require('semver');
|
var s = require('semver');
|
||||||
// MeshCentral doesn't adhere to semantic versioning (due to the -<alpha_char> at the end of the version)
|
|
||||||
// Convert the letter to ASCII for a "true" version number comparison
|
|
||||||
var mcCurVer = parent.currentVer.replace(/-(.)$/, (m, p1) => { return ("000" + p1.charCodeAt(0)).substr(-3,3); });
|
|
||||||
var piCompatVer = newconf.meshCentralCompat.replace(/-(.)\b/g, (m, p1) => { return ("000" + p1.charCodeAt(0)).substr(-3,3); });
|
|
||||||
latestRet.push({
|
latestRet.push({
|
||||||
'id': curconf._id,
|
'id': curconf._id,
|
||||||
'installedVersion': curconf.version,
|
'installedVersion': curconf.version,
|
||||||
'version': newconf.version,
|
'version': newconf.version,
|
||||||
'hasUpdate': s.gt(newconf.version, curconf.version),
|
'hasUpdate': s.gt(newconf.version, curconf.version),
|
||||||
'meshCentralCompat': s.satisfies(mcCurVer, piCompatVer),
|
'meshCentralCompat': checkMeshCentralVersion(parent.currentVer, newconf.meshCentralCompat),
|
||||||
'changelogUrl': curconf.changelogUrl,
|
'changelogUrl': curconf.changelogUrl,
|
||||||
'status': curconf.status
|
'status': curconf.status
|
||||||
});
|
});
|
||||||
@ -377,7 +386,7 @@ module.exports.pluginHandler = function (parent) {
|
|||||||
response.pipe(file);
|
response.pipe(file);
|
||||||
file.on('finish', function () {
|
file.on('finish', function () {
|
||||||
file.close(function () {
|
file.close(function () {
|
||||||
var yauzl = require("yauzl");
|
var yauzl = require('yauzl');
|
||||||
if (!obj.fs.existsSync(obj.pluginPath)) {
|
if (!obj.fs.existsSync(obj.pluginPath)) {
|
||||||
obj.fs.mkdirSync(obj.pluginPath);
|
obj.fs.mkdirSync(obj.pluginPath);
|
||||||
}
|
}
|
||||||
@ -498,9 +507,10 @@ module.exports.pluginHandler = function (parent) {
|
|||||||
obj.removePlugin = function (id, func) {
|
obj.removePlugin = function (id, func) {
|
||||||
parent.db.getPlugin(id, function (err, docs) {
|
parent.db.getPlugin(id, function (err, docs) {
|
||||||
var plugin = docs[0];
|
var plugin = docs[0];
|
||||||
var rimraf = require('rimraf');
|
var rimraf = null;
|
||||||
|
try { rimraf = require('rimraf'); } catch (ex) { }
|
||||||
let pluginPath = obj.parent.path.join(obj.pluginPath, plugin.shortName);
|
let pluginPath = obj.parent.path.join(obj.pluginPath, plugin.shortName);
|
||||||
rimraf.sync(pluginPath);
|
if (rimraf) rimraf.sync(pluginPath);
|
||||||
parent.db.deletePlugin(id, func);
|
parent.db.deletePlugin(id, func);
|
||||||
delete obj.plugins[plugin.shortName];
|
delete obj.plugins[plugin.shortName];
|
||||||
});
|
});
|
||||||
|
11
webserver.js
11
webserver.js
@ -1564,7 +1564,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
||||||
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
||||||
if (parent.config.settings.no2factorauth !== true) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
if (parent.config.settings.no2factorauth !== true) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||||
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) { features += 0x00040000; } // Force 2-factor auth
|
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) {
|
||||||
|
// Check if we can skip 2nd factor auth because of the source IP address
|
||||||
|
var skip2factor = false;
|
||||||
|
if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
|
||||||
|
for (var i in domain.passwordrequirements.skip2factor) {
|
||||||
|
if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) { skip2factor = true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip2factor == false) { features += 0x00040000; } // Force 2-factor auth
|
||||||
|
}
|
||||||
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { features += 0x00080000; } // LDAP or SSPI in use, warn that users must login first before adding a user to a group.
|
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { features += 0x00080000; } // LDAP or SSPI in use, warn that users must login first before adding a user to a group.
|
||||||
if (domain.amtacmactivation) { features += 0x00100000; } // Intel AMT ACM activation/upgrade is possible
|
if (domain.amtacmactivation) { features += 0x00100000; } // Intel AMT ACM activation/upgrade is possible
|
||||||
if (domain.usernameisemail) { features += 0x00200000; } // Username is email address
|
if (domain.usernameisemail) { features += 0x00200000; } // Username is email address
|
||||||
|
Loading…
Reference in New Issue
Block a user