MeshRouter now supports new icon, fixed server crash when running on single CPU, Removed Let's Encrypt wildcard altname being added by GreenLock.

This commit is contained in:
Ylian Saint-Hilaire 2020-03-02 12:36:52 -08:00
parent fa929b3467
commit 607cdf888f
8 changed files with 55 additions and 24 deletions

Binary file not shown.

View File

@ -854,7 +854,7 @@ module.exports.CertificateOperations = function (parent) {
obj.acceleratorPerformOperation = function (operation, data, tag, func) {
if (acceleratorTotalCount <= 1) {
// No accelerators available
program.processMessage({ action: operation, data: data, tag: tag, func: func });
require(program).processMessage({ action: operation, data: data, tag: tag, func: func });
} else {
var acc = obj.getAccelerator();
if (acc == null) {

View File

@ -261,13 +261,28 @@ module.exports.CreateLetsEncrypt = function (parent) {
// GreenLock v3 Manager
module.exports.create = function (options) {
//console.log('xxx-create', options);
var manager = { parent: globalLetsEncrypt };
manager.find = async function (options) {
//console.log('LE-FIND', 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; }
@ -275,6 +290,7 @@ module.exports.create = function (options) {
};
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; }
@ -283,6 +299,7 @@ module.exports.create = function (options) {
// set the global config
manager.defaults = async function (options) {
//console.log('xxx-defaults', options);
var r;
if (manager.parent.runAsProduction === true) {
// Production

View File

@ -1079,6 +1079,7 @@ function CreateMeshCentralServer(config, args) {
var leok = true;
if (typeof obj.config.letsencrypt.email != 'string') { leok = false; addServerWarning("Missing Let's Encrypt email address."); }
else if (typeof obj.config.letsencrypt.names != 'string') { leok = false; addServerWarning("Invalid Let's Encrypt host names."); }
else if (obj.config.letsencrypt.names.indexOf('*') >= 0) { leok = false; addServerWarning("Invalid Let's Encrypt names, can't contain a *."); }
else if (obj.config.letsencrypt.email.split('@').length != 2) { 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 {

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.4.9-o",
"version": "0.4.9-q",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -760,7 +760,7 @@ function AmtStackCreateService(wsmanStack) {
// ###BEGIN###{Certificates}
// Forge MD5
function hex_md5(str) { return forge.md.md5.create().update(str).digest().toHex(); }
function hex_md5(str) { if (str == null) { str = ''; } return forge.md.md5.create().update(str).digest().toHex(); }
// ###END###{Certificates}
@ -774,6 +774,7 @@ for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296)
// Perform MD5 on raw string and return hex
function hex_md5(str) {
if (str == null) { str = ''; }
var b, c, d, j,
x = [],
str2 = unescape(encodeURI(str)),

View File

@ -80,12 +80,12 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
// Websocket relay specific private method (Content Length Encoding)
obj.sendRequest = function (postdata, url, action) {
url = url ? url : "/wsman";
action = action ? action : "POST";
var h = action + " " + url + " HTTP/1.1\r\n";
url = url ? url : '/wsman';
action = action ? action : 'POST';
var h = action + ' ' + url + ' HTTP/1.1\r\n';
if (obj.challengeParams != null) {
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams["realm"] + ':' + obj.pass) + ':' + obj.challengeParams["nonce"] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams["qop"] + ':' + hex_md5(action + ':' + url));
h += 'Authorization: ' + obj.renderDigest({ "username": obj.user, "realm": obj.challengeParams["realm"], "nonce": obj.challengeParams["nonce"], "uri": url, "qop": obj.challengeParams["qop"], "response": response, "nc": obj.noncecounter++, "cnonce": obj.cnonce }) + '\r\n';
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams['qop'] + ':' + hex_md5(action + ':' + url + ((obj.challengeParams['qop'] == 'auth-int') ? (':' + hex_md5(postdata)) : '')));
h += 'Authorization: ' + obj.renderDigest({ 'username': obj.user, 'realm': obj.challengeParams['realm'], 'nonce': obj.challengeParams['nonce'], 'uri': url, 'qop': obj.challengeParams['qop'], 'response': response, 'nc': obj.noncecounter++, 'cnonce': obj.cnonce }) + '\r\n';
}
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
@ -93,12 +93,11 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
//obj.Debug("SEND: " + h); // Display send packet
}
// Websocket relay specific private method
obj.parseDigest = function (header) {
var t = header.substring(7).split(',');
for (i in t) t[i] = t[i].trim();
return t.reduce(function (obj, s) { var parts = s.split('='); obj[parts[0]] = parts[1].replace(/"/g, ''); return obj; }, {})
}
// Parse the HTTP digest header and return a list of key & values.
obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) }
// Split a string on quotes but do not do it when in quotes
function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st }
// Websocket relay specific private method
obj.renderDigest = function (params) {
@ -117,7 +116,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
obj.socketState = 1;
console.log(obj.tlsv1only);
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=1&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + "&tlsv1only=" + obj.tlsv1only + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=1" indicates to the relay that this is a WSMAN session
obj.socket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/webrelay.ashx?p=1&host=' + obj.host + '&port=' + obj.port + '&tls=' + obj.tls + '&tlsv1only=' + obj.tlsv1only + ((user == '*') ? '&serverauth=1' : '') + ((typeof pass === 'undefined') ? ('&serverauth=1&user=' + user) : '')); // The "p=1" indicates to the relay that this is a WSMAN session
obj.socket.onopen = _OnSocketConnected;
obj.socket.onmessage = _OnMessage;
obj.socket.onclose = _OnSocketClosed;
@ -154,7 +153,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
fileReader.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
var binary = '', bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
_OnSocketData(binary);
}
@ -169,7 +168,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
var binary = '', bytes = new Uint8Array(data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
@ -180,10 +179,10 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
obj.socketAccumulator += data;
while (true) {
if (obj.socketParseState == 0) {
var headersize = obj.socketAccumulator.indexOf("\r\n\r\n");
var headersize = obj.socketAccumulator.indexOf('\r\n\r\n');
if (headersize < 0) return;
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n");
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n');
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
obj.socketParseState = 1;
obj.socketData = '';
@ -197,12 +196,12 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
}
if (obj.socketParseState == 1) {
var csize = -1;
if ((obj.socketXHeader["connection"] != undefined) && (obj.socketXHeader["connection"].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
if ((obj.socketXHeader['connection'] != undefined) && (obj.socketXHeader['connection'].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
// The body ends with a close, in this case, we will only process the header
csize = 0;
} else if (obj.socketXHeader["content-length"] != undefined) {
} else if (obj.socketXHeader['content-length'] != undefined) {
// The body length is specified by the content-length
csize = parseInt(obj.socketXHeader["content-length"]);
csize = parseInt(obj.socketXHeader['content-length']);
if (obj.socketAccumulator.length < csize) return;
var data = obj.socketAccumulator.substring(0, csize);
obj.socketAccumulator = obj.socketAccumulator.substring(csize);
@ -239,6 +238,11 @@ var CreateWsmanComm = function (host, port, user, pass, tls) {
if (isNaN(s)) s = 602;
if (s == 401 && ++(obj.authcounter) < 3) {
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
if (obj.challengeParams['qop'] != null) {
var qopList = obj.challengeParams['qop'].split(',');
for (var i in qopList) { qopList[i] = qopList[i].trim(); }
if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; }
}
} else {
var r = obj.pendingAjaxCall.shift();
// if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.

View File

@ -75,7 +75,15 @@ module.exports.CreateRedirServer = function (parent, db, args, func) {
parent.letsencrypt.challenge(req.url.slice(leChallengePrefix.length), getCleanHostname(req), function (response) { if (response == null) { res.sendStatus(404); } else { res.send(response); } });
} else {
// Everything else
res.set({ 'strict-transport-security': "max-age=60000; includeSubDomains", "Referrer-Policy": "no-referrer", "x-frame-options": "SAMEORIGIN", "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "Content-Security-Policy": "default-src http: ws: \"self\" \"unsafe-inline\"" });
var selfurl = ((args.notls !== true) ? (' wss://' + req.headers.host) : (' ws://' + req.headers.host));
res.set({
'strict-transport-security': 'max-age=60000; includeSubDomains',
'Referrer-Policy': 'no-referrer',
'x-frame-options': 'SAMEORIGIN',
'X-XSS-Protection': '1; mode=block',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': "default-src 'none'; style-src 'self' 'unsafe-inline';"
});
return next();
}
});