UI customization improvements.

This commit is contained in:
Ylian Saint-Hilaire 2020-11-05 02:27:39 -08:00
parent 8c590b9b30
commit 05847525bb
11 changed files with 72 additions and 39 deletions

View File

@ -131,7 +131,7 @@ function CreateMeshCentralServer(config, args) {
try { require('./pass').hash('test', function () { }, 0); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
// Check for invalid arguments
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'rediraliasport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'showitem', 'listuserids', 'showusergroups', 'shownodes', 'showallmeshes', 'showmeshes', 'showevents', 'showsmbios', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'xinstall', 'xuninstall', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbexportmin', 'dbimport', 'dbmerge', 'dbfix', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'logintoken', 'logintokenkey', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'vaultpushconfigfiles', 'vaultpullconfigfiles', 'vaultdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath', 'serverid', 'recordencryptionrecode', 'vault', 'token', 'unsealkey', 'name', 'log', 'dbstats', 'translate', 'createaccount', 'resetaccount', 'pass', 'adminaccount', 'removeaccount', 'domain', 'email'];
var validArguments = ['_', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'rediraliasport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'showitem', 'listuserids', 'showusergroups', 'shownodes', 'showallmeshes', 'showmeshes', 'showevents', 'showsmbios', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'xinstall', 'xuninstall', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbexportmin', 'dbimport', 'dbmerge', 'dbfix', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'logintoken', 'logintokenkey', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'vaultpushconfigfiles', 'vaultpullconfigfiles', 'vaultdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath', 'serverid', 'recordencryptionrecode', 'vault', 'token', 'unsealkey', 'name', 'log', 'dbstats', 'translate', 'createaccount', 'resetaccount', 'pass', 'adminaccount', 'removeaccount', 'domain', 'email'];
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
@ -1103,12 +1103,12 @@ function CreateMeshCentralServer(config, args) {
if ((obj.args.ciralocalfqdn != null) && ((obj.args.lanonly == true) || (obj.args.wanonly == true))) { addServerWarning("CIRA local FQDN's ignored when server in LAN-only or WAN-only mode."); }
if ((obj.args.ciralocalfqdn != null) && (obj.args.ciralocalfqdn.split(',').length > 4)) { addServerWarning("Can't have more than 4 CIRA local FQDN's. Ignoring value."); obj.args.ciralocalfqdn = null; }
if (obj.args.ignoreagenthashcheck === true) { addServerWarning("Agent hash checking is being skipped, this is unsafe."); }
if (obj.args.port == null || typeof obj.args.port != 'number') { if (obj.args.notls == null) { obj.args.port = 443; } else { obj.args.port = 80; } }
if (obj.args.port == null || typeof obj.args.port != 'number') { obj.args.port = 443; }
if (obj.args.aliasport != null && (typeof obj.args.aliasport != 'number')) obj.args.aliasport = null;
if (obj.args.mpsport == null || typeof obj.args.mpsport != 'number') obj.args.mpsport = 4433;
if (obj.args.mpsaliasport != null && (typeof obj.args.mpsaliasport != 'number')) obj.args.mpsaliasport = null;
if (obj.args.rediraliasport != null && (typeof obj.args.rediraliasport != 'number')) obj.args.rediraliasport = null;
if (obj.args.notls == null && obj.args.redirport == null) obj.args.redirport = 80;
if (obj.args.redirport == null) obj.args.redirport = 80;
if (obj.args.minifycore === 0) obj.args.minifycore = false;
if (typeof args.agentidletimeout != 'number') { args.agentidletimeout = 150000; } else { args.agentidletimeout *= 1000 } // Default agent idle timeout is 2m, 30sec.
@ -2107,7 +2107,7 @@ function CreateMeshCentralServer(config, args) {
obj.meshToolsBinaries[this.toolname].hashx = this.hashx;
obj.meshToolsBinaries[this.toolname].path = this.agentpath;
obj.meshToolsBinaries[this.toolname].dlname = this.dlname;
obj.meshToolsBinaries[this.toolname].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?meshaction=' + this.dlname;
obj.meshToolsBinaries[this.toolname].url = ('https://' + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?meshaction=' + this.dlname;
var stats = null;
try { stats = obj.fs.statSync(this.agentpath); } catch (e) { }
if (stats != null) { obj.meshToolsBinaries[this.toolname].size = stats.size; }
@ -2148,7 +2148,7 @@ function CreateMeshCentralServer(config, args) {
obj.meshAgentInstallScripts[this.info.id].hash = this.hash.digest('hex');
obj.meshAgentInstallScripts[this.info.id].path = this.agentpath;
obj.meshAgentInstallScripts[this.info.id].data = this.xdata;
obj.meshAgentInstallScripts[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?script=' + this.info.id;
obj.meshAgentInstallScripts[this.info.id].url = ('https://' + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?script=' + this.info.id;
var stats = null;
try { stats = obj.fs.statSync(this.agentpath); } catch (e) { }
if (stats != null) { obj.meshAgentInstallScripts[this.info.id].size = stats.size; }
@ -2213,7 +2213,7 @@ function CreateMeshCentralServer(config, args) {
archcount++;
obj.meshAgentBinaries[archid] = Object.assign({}, obj.meshAgentsArchitectureNumbers[archid]);
obj.meshAgentBinaries[archid].path = agentpath;
obj.meshAgentBinaries[archid].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?id=' + archid;
obj.meshAgentBinaries[archid].url = ('http://' + obj.certificates.CommonName + ':' + ((typeof obj.args.aliasport == 'number') ? obj.args.aliasport : obj.args.port) + '/meshagents?id=' + archid;
obj.meshAgentBinaries[archid].size = stats.size;
// If this is a windows binary, pull binary information

View File

@ -272,6 +272,15 @@ if (args['_'].length == 0) {
console.log(" --hours [hours] - Validity period in hours or 0 for infinit.");
break;
}
case 'showevents': {
console.log("Show the server's event stream for this user account. Example usage:\r\n");
console.log(" MeshCtrl ShowEvents");
console.log(" MeshCtrl ShowEvents --filter nodeconnect");
console.log(" MeshCtrl ShowEvents --filter uicustomevent,changenode");
console.log("\r\nOptional arguments:\r\n");
console.log(" --filter [actions] - Show only specified actions.");
break;
}
case 'serverinfo': {
console.log("Get information on the MeshCentral server, Example usages:\r\n");
console.log(" MeshCtrl ServerInfo --loginuser myaccountname --loginpass mypassword");
@ -625,10 +634,12 @@ if (args['_'].length == 0) {
break;
}
case 'broadcast': {
console.log("Display a message to all logged in users, Example usages:\r\n");
console.log("Display a message to one or all logged in users, Example usages:\r\n");
console.log(" MeshCtrl Broadcast --msg \"This is a test\"");
console.log("\r\nRequired arguments:\r\n");
console.log(" --msg [message] - Message to display.");
console.log("\r\nOptional arguments:\r\n");
console.log(" --user [userid] - Send the message to the speficied user.");
break;
}
case 'deviceinfo': {
@ -1216,6 +1227,7 @@ function serverConnect() {
}
case 'broadcast': {
var op = { action: 'userbroadcast', msg: args.msg, responseid: 'meshctrl' };
if (args.user) { op.userid = args.user; }
ws.send(JSON.stringify(op));
break;
}
@ -1269,7 +1281,21 @@ function serverConnect() {
var data = null;
try { data = JSON.parse(rawdata); } catch (ex) { }
if (data == null) { console.log('Unable to parse data: ' + rawdata); }
if (settings.cmd == 'showevents') { console.log(JSON.stringify(data, null, 2)); return; }
if (settings.cmd == 'showevents') {
if (args.filter == null) {
// Display all events
console.log(JSON.stringify(data, null, 2));
} else {
// Display select events
var filters = args.filter.split(',');
if (typeof data.event == 'object') {
if (filters.indexOf(data.event.action) >= 0) { console.log(JSON.stringify(data, null, 2) + '\r\n'); }
} else {
if (filters.indexOf(data.action) >= 0) { console.log(JSON.stringify(data, null, 2) + '\r\n'); }
}
}
return;
}
switch (data.action) {
case 'serverinfo': { // SERVERINFO
settings.currentDomain = data.serverinfo.domain;

View File

@ -984,7 +984,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
/*
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
// TODO: Figure out when this needs to be done.
if ((user == null) && (!parent.args.notls)) {
if (user == null) {
// Check the identifier, if running without TLS, skip this.
var ids = obj.id.split(':');
if (ids.length != 3) { ws.close(); delete obj.id; return null; } // Invalid ID, drop this.

View File

@ -128,10 +128,10 @@ module.exports.CreateMeshMail = function (parent) {
var httpsport = (typeof obj.parent.args.aliasport == 'number') ? obj.parent.args.aliasport : obj.parent.args.port;
if (domain.dns == null) {
// Default domain or subdomain of the default.
options.serverurl = 'http' + ((obj.parent.args.notls == null) ? 's' : '') + '://' + obj.parent.certificates.CommonName + ':' + httpsport + domain.url;
options.serverurl = 'https://' + obj.parent.certificates.CommonName + ':' + httpsport + domain.url;
} else {
// Domain with a DNS name.
options.serverurl = 'http' + ((obj.parent.args.notls == null) ? 's' : '') + '://' + domain.dns + ':' + httpsport + domain.url;
options.serverurl = 'https://' + domain.dns + ':' + httpsport + domain.url;
}
if (options.serverurl.endsWith('/')) { options.serverurl = options.serverurl.substring(0, options.serverurl.length - 1); } // Remove the ending / if present
for (var i in options) {

View File

@ -175,15 +175,15 @@ module.exports.CreateMeshScanner = function (parent) {
var port = (parent.args.aliasport)?parent.args.aliasport:parent.args.port;
// Build the IPv4 response
var url = (parent.args.notls ? 'ws' : 'wss') + '://%s:' + port + '/agent.ashx';
var url = 'wss://%s:' + port + '/agent.ashx';
obj.multicastPacket4 = Buffer.from("MeshCentral2|" + obj.agentCertificateHashHex + '|' + url, 'ascii');
if (parent.certificates.CommonName.indexOf('.') != -1) { url = (parent.args.notls ? 'ws' : 'wss') + '://' + parent.certificates.CommonName + ':' + port + '/agent.ashx'; }
if (parent.certificates.CommonName.indexOf('.') != -1) { url = 'wss://' + parent.certificates.CommonName + ':' + port + '/agent.ashx'; }
obj.multicastPacket4x = Buffer.from("MeshCentral2|" + obj.agentCertificateHashHex + '|' + url + '|' + name + '|' + info, 'ascii');
// Build the IPv6 response
url = (parent.args.notls ? 'ws' : 'wss') + '://[%s]:' + port + '/agent.ashx';
url = 'wss://[%s]:' + port + '/agent.ashx';
obj.multicastPacket6 = Buffer.from("MeshCentral2|" + obj.agentCertificateHashHex + '|' + url, 'ascii');
if (parent.certificates.CommonName.indexOf('.') != -1) { url = (parent.args.notls ? 'ws' : 'wss') + '://' + parent.certificates.CommonName + ':' + port + '/agent.ashx'; }
if (parent.certificates.CommonName.indexOf('.') != -1) { url = 'wss://' + parent.certificates.CommonName + ':' + port + '/agent.ashx'; }
obj.multicastPacket6x = Buffer.from("MeshCentral2|" + obj.agentCertificateHashHex + '|' + url + '|' + name + '|' + info, 'ascii');
setupServers();

View File

@ -457,7 +457,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
if (matchingDomains.length > 0) { serverinfo.amtAcmFqdn = matchingDomains; }
}
if (args.notls == true) { serverinfo.https = false; } else { serverinfo.https = true; serverinfo.redirport = args.redirport; }
serverinfo.https = true;
serverinfo.redirport = args.redirport;
if (typeof domain.userconsentflags == 'number') { serverinfo.consent = domain.userconsentflags; }
if ((typeof domain.usersessionidletimeout == 'number') && (domain.usersessionidletimeout > 0)) { serverinfo.timeout = (domain.usersessionidletimeout * 60 * 1000); }
if (user.siteadmin === SITERIGHT_ADMIN) {
@ -1743,6 +1744,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try {
if (parent.wssessions2[i].domainid == domain.id) {
var sessionUser = parent.users[parent.wssessions2[i].userid];
if ((command.userid != null) && (command.userid != sessionUser._id) && (command.userid != sessionUser._id.split('/')[2])) { continue; }
if ((command.target == null) || ((sessionUser.links) != null && (sessionUser.links[command.target] != null))) {
if ((user.groups == null) || (user.groups.length == 0)) {
// We are part of no user groups, send to everyone.
@ -2662,7 +2664,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += "/";
var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name);
var url = "https://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.nodeid) + "/" + encodeURIComponent(user._id) + "&title=" + encodeURIComponent(user.name);
// Create the notification message
routeCommandToNode({ 'action': 'openUrl', 'nodeid': command.nodeid, 'userid': user._id, 'username': user.name, 'url': url });
@ -4570,7 +4572,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += '/';
var url = 'http' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'agentinvite?c=' + inviteCookie;
var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + 'agentinvite?c=' + inviteCookie;
if (serverName.split('.') == 1) { url = '/' + xdomain + 'agentinvite?c=' + inviteCookie; }
ws.send(JSON.stringify({ action: 'createInviteLink', meshid: command.meshid, url: url, expire: command.expire, cookie: inviteCookie, responseid: command.responseid, tag: command.tag }));
@ -4725,7 +4727,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += '/';
var url = 'http' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'desktop?c=' + inviteCookie;
var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + 'desktop?c=' + inviteCookie;
if (serverName.split('.') == 1) { url = '/' + xdomain + 'desktop?c=' + inviteCookie; }
command.url = url;
ws.send(JSON.stringify(command));
@ -4819,7 +4821,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += '/';
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
r.wsUrl = 'ws' + (args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'mqtt.ashx';
r.wsUrl = 'wss://' + serverName + ':' + httpsPort + '/' + xdomain + 'mqtt.ashx';
r.wsTrustedCert = parent.isTrustedCert(domain);
try { ws.send(JSON.stringify(r)); } catch (ex) { }

View File

@ -64,7 +64,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
var options = { rejectUnauthorized: false };
if (domain.dns != null) { options.servername = domain.dns; }
var protocol = 'wss';
if (args.notls || args.tlsoffload) { protocol = 'ws'; }
if (args.tlsoffload) { protocol = 'ws'; }
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + 'meshrelay.ashx?auth=' + obj.infos.ip;

View File

@ -75,7 +75,7 @@ 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
var selfurl = ((args.notls !== true) ? (' wss://' + req.headers.host) : (' ws://' + req.headers.host));
var selfurl = (' wss://' + req.headers.host);
res.set({
'strict-transport-security': 'max-age=60000; includeSubDomains',
'Referrer-Policy': 'no-referrer',

View File

@ -3331,7 +3331,13 @@
for (var pid in processes) { p.push({ p: parseInt(pid), c: processes[pid].cmd, d: processes[pid].cmd.toLowerCase(), u: processes[pid].user }); }
if (deskTools.sort == 0) { p.sort(sortProcessPid); } else if (deskTools.sort == 1) { p.sort(sortProcessName); }
var x = '';
for (var i in p) { if (p[i].p != 0) { x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + p[i].p + '</div><a style=float:right;padding-right:5px;cursor:pointer onclick=stopProcess(' + p[i].p + ',"' + p[i].c + '")><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? p[i].u : '') + '</div><div>' + p[i].c + '</div></div>'; } }
for (var i in p) {
if (p[i].p != 0) {
var c = p[i].c;
if (c.length > 30) { c = '<span title="' + EscapeHtml(c) + '">' + EscapeHtml(c.substring(0, 30)) + '...</span>' } else { c = EscapeHtml(c); }
x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + EscapeHtml(p[i].p) + '</div><a style=float:right;padding-right:5px;cursor:pointer onclick=stopProcess(' + EscapeHtml(p[i].p) + ',"' + EscapeHtml(p[i].c) + '")><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? EscapeHtml(p[i].u) : '') + '</div><div>' + c + '</div></div>';
}
}
QH('DeskToolsProcesses', x);
}
}

View File

@ -1573,8 +1573,7 @@
if (typeof dialog.elements == 'object') {
for (var i in dialog.elements) {
var elem = dialog.elements[i];
if (elem.type == 'text') { t.values[i] = Q('cui:' + i).value; }
if (elem.type == 'droplist') { t.values[i] = Q('cui:' + i).value; }
if ((elem.type == 'droplist') || (elem.type == 'textarea') || (elem.type == 'text')) { t.values[i] = Q('cui:' + i).value; }
}
}
meshserver.send(t);
@ -5790,7 +5789,9 @@
// Custom UI
if ((customui != null) && (customui.devicebuttons != null)) {
for (var i in customui.devicebuttons) {
x += '<input id="cui:' + i + '" type="button" value="' + customui.devicebuttons[i].name + '" onclick=customUIAction(event,"devicebuttons") />';
if ((customui.devicebuttons[i].showif == null) || ((mesh != null) && (customui.devicebuttons[i].showif == mesh._id)) || (customui.devicebuttons[i].showif == currentNode._id)) {
x += '<input id="cui:' + i + '" type="button" value="' + customui.devicebuttons[i].name + '" onclick=customUIAction(event,"devicebuttons") />';
}
}
}

View File

@ -1729,7 +1729,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (typeof installflags != 'number') { installflags = 0; }
parent.debug('web', 'handleAgentInviteRequest using cookie.');
var meshcookie = parent.encodeCookie({ m: mesh._id.split('/')[2] }, parent.invitationLinkEncryptionKey);
render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags }, req, domain));
render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags }, req, domain));
} else if (req.query.m != null) {
// The MeshId is specified in the query string, use that
var mesh = obj.meshes['mesh/' + domain.id + '/' + req.query.m.toLowerCase()];
@ -1739,7 +1739,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (typeof installflags != 'number') { installflags = 0; }
parent.debug('web', 'handleAgentInviteRequest using meshid.');
var meshcookie = parent.encodeCookie({ m: mesh._id.split('/')[2] }, parent.invitationLinkEncryptionKey);
render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags }, req, domain));
render(req, res, getRenderPage('agentinvite', req, domain), getRenderArgs({ meshid: meshcookie, serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: 1, servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name).replace(/'/g, '%27'), installflags: installflags }, req, domain));
}
}
@ -2527,7 +2527,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Return true if it looks like we are using a real TLS certificate.
obj.isTrustedCert = function (domain) {
if (obj.args.notls == true) return false; // We are not using TLS, so not trusted cert.
if ((domain != null) && (typeof domain.trustedcert == 'boolean')) return domain.trustedcert; // If the status of the cert specified, use that.
if (typeof obj.args.trustedcert == 'boolean') return obj.args.trustedcert; // If the status of the cert specified, use that.
if (obj.args.tlsoffload != null) return true; // We are using TLS offload, a real cert is likely used.
@ -2540,7 +2539,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Get the link to the root certificate if needed
function getRootCertLink() {
// Check if the HTTPS certificate is issued from MeshCentralRoot, if so, add download link to root certificate.
if ((obj.args.notls == null) && (obj.args.tlsoffload == null) && (obj.parent.config.letsencrypt == null) && (obj.tlsSniCredentials == null) && (obj.certificates.WebIssuer.indexOf('MeshCentralRoot-') == 0) && (obj.certificates.CommonName.indexOf('.') != -1)) { return '<a href=/MeshServerRootCert.cer title="Download the root certificate for this server">Root Certificate</a>'; }
if ((obj.args.tlsoffload == null) && (obj.parent.config.letsencrypt == null) && (obj.tlsSniCredentials == null) && (obj.certificates.WebIssuer.indexOf('MeshCentralRoot-') == 0) && (obj.certificates.CommonName.indexOf('.') != -1)) { return '<a href=/MeshServerRootCert.cer title="Download the root certificate for this server">Root Certificate</a>'; }
return '';
}
@ -4106,7 +4105,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += '/';
var meshsettings = 'MeshName=' + mesh.name + '\r\nMeshType=' + mesh.mtype + '\r\nMeshID=0x' + meshidhex + '\r\nServerID=' + serveridhex + '\r\n';
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=ws' + (obj.args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=wss://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
meshsettings += 'MeshServer=local\r\n';
if ((obj.args.localdiscovery != null) && (typeof obj.args.localdiscovery.key == 'string') && (obj.args.localdiscovery.key.length > 0)) { meshsettings += 'DiscoveryKey=' + obj.args.localdiscovery.key + '\r\n'; }
}
@ -4227,7 +4226,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (user != null) { meshaction.username = user.name; }
if (req.query.key != null) { meshaction.loginKey = req.query.key; }
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
if (obj.args.lanonly != true) { meshaction.serverUrl = 'wss://' + obj.getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
setContentDispositionHeader(res, 'application/octet-stream', 'meshaction.txt', null, 'meshaction.txt');
res.send(JSON.stringify(meshaction, null, ' '));
@ -4243,7 +4242,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (user != null) { meshaction.username = user.name; }
if (req.query.key != null) { meshaction.loginKey = req.query.key; }
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
if (obj.args.lanonly != true) { meshaction.serverUrl = 'wss://' + obj.getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
setContentDispositionHeader(res, 'application/octet-stream', 'meshaction.txt', null, 'meshaction.txt');
res.send(JSON.stringify(meshaction, null, ' '));
} else if (req.query.meshaction == 'winrouter') {
@ -4407,7 +4406,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.agentport != null) { httpsPort = obj.args.agentport; } // If an agent only port is enabled, use that.
if (obj.args.agentaliasport != null) { httpsPort = obj.args.agentaliasport; } // If an agent alias port is specified, use that.
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=ws' + (obj.args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=wss://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
meshsettings += 'MeshServer=local\r\n';
if ((obj.args.localdiscovery != null) && (typeof obj.args.localdiscovery.key == 'string') && (obj.args.localdiscovery.key.length > 0)) { meshsettings += 'DiscoveryKey=' + obj.args.localdiscovery.key + '\r\n'; }
}
@ -4499,7 +4498,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.agentport != null) { httpsPort = obj.args.agentport; } // If an agent only port is enabled, use that.
if (obj.args.agentaliasport != null) { httpsPort = obj.args.agentaliasport; } // If an agent alias port is specified, use that.
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=ws' + (obj.args.notls ? '' : 's') + '://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
if (obj.args.lanonly != true) { meshsettings += 'MeshServer=wss://' + serverName + ':' + httpsPort + '/' + xdomain + 'agent.ashx\r\n'; } else {
meshsettings += 'MeshServer=local\r\n';
if ((obj.args.localdiscovery != null) && (typeof obj.args.localdiscovery.key == 'string') && (obj.args.localdiscovery.key.length > 0)) { meshsettings += 'DiscoveryKey=' + obj.args.localdiscovery.key + '\r\n'; }
}
@ -4599,7 +4598,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Starts the HTTPS server, this should be called after the user/mesh tables are loaded
function serverStart() {
// Start the server, only after users and meshes are loaded from the database.
if (obj.args.notls || obj.args.tlsoffload) {
if (obj.args.tlsoffload) {
// Setup the HTTP server without TLS
obj.expressWs = require('express-ws')(obj.app, null, { wsOptions: { perMessageDeflate: (args.wscompression === true) } });
} else {
@ -4619,7 +4618,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Start a second agent-only server if needed
if (obj.args.agentport) {
var agentPortTls = true;
if ((obj.args.notls == 1) || (obj.args.notls == true)) { agentPortTls = false; }
if (obj.args.tlsoffload != null) { agentPortTls = false; }
if (typeof obj.args.agentporttls == 'boolean') { agentPortTls = obj.args.agentporttls; }
if (obj.certificates.webdefault == null) { agentPortTls = false; }
@ -4651,7 +4649,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
name: 'xid', // Recommended security practice to not use the default cookie name
httpOnly: true,
keys: [obj.args.sessionkey], // If multiple instances of this server are behind a load-balancer, this secret must be the same for all instances
secure: ((obj.args.notls != true) && (obj.args.tlsoffload == null)) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
secure: (obj.args.tlsoffload == null) // Use this cookie only over TLS (Check this: https://expressjs.com/en/guide/behind-proxies.html)
}
if (obj.args.sessionsamesite != null) { sessionOptions.sameSite = obj.args.sessionsamesite; } else { sessionOptions.sameSite = 'strict'; }
if (obj.args.sessiontime != null) { sessionOptions.maxAge = (obj.args.sessiontime * 60 * 1000); }
@ -4696,7 +4694,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else {
// Use default security headers
var geourl = (domain.geolocation ? ' *.openstreetmap.org' : '');
var selfurl = ((args.notls !== true) ? (' wss://' + req.headers.host) : (' ws://' + req.headers.host));
var selfurl = (' wss://' + req.headers.host);
var headers = {
'Referrer-Policy': 'no-referrer',
'X-XSS-Protection': '1; mode=block',