diff --git a/meshcentral.js b/meshcentral.js index 7957e086..d76831b3 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -26,8 +26,6 @@ function CreateMeshCentralServer() { obj.debugLevel = 0; obj.config = {}; // Configuration file obj.dbconfig = {}; // Persistance values, loaded from database - obj.datapath = obj.path.join(__dirname, '../.meshcentral-data'); - obj.filespath = obj.path.join(__dirname, '../.meshcentral-files'); obj.certificateOperations = require('./certoperations.js').CertificateOperations(); obj.defaultMeshCore = null; obj.defaultMeshCoreHash = null; @@ -38,6 +36,15 @@ function CreateMeshCentralServer() { obj.maintenanceTimer = null; obj.serverId = null; + // Setup the default configuration and files paths + if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) { + obj.datapath = obj.path.join(__dirname, '../../.meshcentral-data'); + obj.filespath = obj.path.join(__dirname, '../../.meshcentral-files'); + } else { + obj.datapath = obj.path.join(__dirname, '../.meshcentral-data'); + obj.filespath = obj.path.join(__dirname, '../.meshcentral-files'); + } + // Create data and files folders if needed try { obj.fs.mkdirSync(obj.datapath); } catch (e) { } try { obj.fs.mkdirSync(obj.filespath); } catch (e) { } @@ -57,8 +64,8 @@ function CreateMeshCentralServer() { try { require('./pass').hash('test', function () { }); } 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', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload']; - for (var arg in obj.args) { if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } + var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip']; + 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; } if ((obj.args.help == true) || (obj.args['?'] == true)) { @@ -85,19 +92,19 @@ function CreateMeshCentralServer() { if ((obj.service != null) && ((obj.args.install == true) || (obj.args.uninstall == true) || (obj.args.start == true) || (obj.args.stop == true) || (obj.args.restart == true))) { var env = [], xenv = ['user', 'port', 'mpsport', 'redirport', 'exactport', 'debug']; for (var i in xenv) { if (obj.args[xenv[i]] != null) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables. - var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: process.argv[1] + '.js', env: env, wait: 2, grow: .5 }); + var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: obj.path.join(__dirname, 'meshcentral.js'), env: env, wait: 2, grow: .5 }); svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); }); svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); process.exit(); }); svc.on('start', function () { console.log('MeshCentral service started.'); process.exit(); }); svc.on('stop', function () { console.log('MeshCentral service stopped.'); if (obj.args.stop) { process.exit(); } if (obj.args.restart) { console.log('Holding 5 seconds...'); setTimeout(function () { svc.start(); }, 5000); } }); svc.on('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); process.exit(); }); svc.on('invalidinstallation', function () { console.log('Invalid MeshCentral service installation.'); process.exit(); }); - try { - if (obj.args.install == true) { svc.install(); return; } - else if (obj.args.uninstall == true) { svc.uninstall(); return; } - else if (obj.args.start == true) { svc.start(); return; } - else if (obj.args.stop == true || obj.args.restart == true) { svc.stop(); return; } - } catch (e) { logException(e); } + + if (obj.args.install == true) { try { svc.install(); } catch (e) { logException(e); } } + if (obj.args.stop == true || obj.args.restart == true) { try { svc.stop(); } catch (e) { logException(e); } } + if (obj.args.start == true || obj.args.restart == true) { try { svc.start(); } catch (e) { logException(e); } } + if (obj.args.uninstall == true) { try { svc.uninstall(); } catch (e) { logException(e); } } + return; } // If "--launch" is in the arguments, launch now @@ -296,6 +303,9 @@ function CreateMeshCentralServer() { for (var serverid in obj.config.peers.servers) { obj.peerConnectivityByNode[serverid] = {}; } } + // If the server is set to "nousers", allow only loopback unless IP filter is set + if ((obj.args.nousers == 1) && (obj.args.userallowedip == null)) { obj.args.userallowedip = "::1,127.0.0.1"; } + if (obj.args.secret) { // This secret is used to encrypt HTTP session information, if specified, user it. obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.args.secret, obj.certificates); @@ -627,8 +637,8 @@ function CreateMeshCentralServer() { obj.defaultMeshCore = data; obj.defaultMeshCoreHash = obj.crypto.createHash('sha256').update(data).digest("binary"); } else { - obj.parent.defaultMeshCore = null; - obj.parent.defaultMeshCoreHash = null; + obj.defaultMeshCore = null; + obj.defaultMeshCoreHash = null; } if (func != null) { func(); } }); @@ -640,8 +650,8 @@ function CreateMeshCentralServer() { obj.defaultMeshCore = data; obj.defaultMeshCoreHash = obj.crypto.createHash('sha256').update(data).digest("binary"); } else { - obj.parent.defaultMeshCore = null; - obj.parent.defaultMeshCoreHash = null; + obj.defaultMeshCore = null; + obj.defaultMeshCoreHash = null; } if (func != null) { func(); } }); @@ -760,6 +770,7 @@ function CreateMeshCentralServer() { var called = false; try { obj.fs.open(filepath, 'r', function (err, fd) { + if (fd == null) { func(null); return; } obj.fs.fstat(fd, function (err, stats) { var bufferSize = stats.size, chunkSize = 512, buffer = new Buffer(bufferSize), bytesRead = 0; while (bytesRead < bufferSize) { diff --git a/mesherrors.txt b/mesherrors.txt index dc8b3a85..430ac93b 100644 --- a/mesherrors.txt +++ b/mesherrors.txt @@ -107,3 +107,47 @@ TypeError: command.join is not a function at Immediate._onImmediate (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\async\lib\async.js:498:34) +-------- 9/27/2017, 11:45:48 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\webserver.js:1 +(function (exports, require, module, __filename, __dirname) { * @description Meshcentral web server + ^ +SyntaxError: Unexpected token * + at exports.runInThisContext (vm.js:53:16) + at Module._compile (module.js:511:25) + at Object.Module._extensions..js (module.js:550:10) + at Module.load (module.js:456:32) + at tryModuleLoad (module.js:415:12) + at Function.Module._load (module.js:407:3) + at Module.require (module.js:466:17) + at require (internal/module.js:20:19) + at InternalFieldObject.ondone (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:311:45) + + +-------- 9/27/2017, 11:45:50 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\webserver.js:1 +(function (exports, require, module, __filename, __dirname) { * @description Meshcentral web server + ^ +SyntaxError: Unexpected token * + at exports.runInThisContext (vm.js:53:16) + at Module._compile (module.js:511:25) + at Object.Module._extensions..js (module.js:550:10) + at Module.load (module.js:456:32) + at tryModuleLoad (module.js:415:12) + at Function.Module._load (module.js:407:3) + at Module.require (module.js:466:17) + at require (internal/module.js:20:19) + at InternalFieldObject.ondone (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:311:45) + + +-------- 9/27/2017, 12:40:21 PM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:307 + if ((obj.args.nousers == 1) && (args.userallowedip == null)) { args.userallowedip = "::1,127.0.0.1"; } + ^ + +ReferenceError: args is not defined + at InternalFieldObject.ondone (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:307:57) + + diff --git a/package.json b/package.json index 1a109cec..dd1386f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.0.8-e", + "version": "0.0.8-g", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index 4c3b4df4..7dece0f2 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1742,7 +1742,13 @@ if (node.userloc) { loc = node.userloc; t = 4; } if ((loc == null) || (typeof loc != 'string')) return; loc = loc.split(','); - return [ parseFloat(loc[0]) + (stringToIntHash(node._id.substring(0, 20)) / 100000000000), parseFloat(loc[1]) + (stringToIntHash(node._id.substring(20)) / 100000000000), t ] + if (t == 1) { + // If this is IP location, randomize the position a little. + return [ parseFloat(loc[0]) + (stringToIntHash(node._id.substring(0, 20)) / 100000000000), parseFloat(loc[1]) + (stringToIntHash(node._id.substring(20)) / 100000000000), t ]; + } else { + // Return the real position + return [ parseFloat(loc[0]), parseFloat(loc[1]), t ]; + } } // Load the entire map diff --git a/webserver.js b/webserver.js index e1297f7b..2e661bb5 100644 --- a/webserver.js +++ b/webserver.js @@ -65,6 +65,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate obj.args = args; obj.users = {}; obj.meshes = {}; + obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users // Perform hash on web certificate and agent certificate obj.webCertificatHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha256.create(), encoding: 'binary' }); @@ -176,6 +177,22 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } */ + // Check if the source IP address is allowed, return false if not + function checkUserIpAddress(req, res) { + if (obj.userAllowedIp == null) { return true; } + try { + if (typeof obj.userAllowedIp == 'string') { if (obj.userAllowedIp == "") { obj.userAllowedIp = null; return true; } else { obj.userAllowedIp = obj.userAllowedIp.split(','); } } + var ip = null, type = 0; + if (req.connection) { ip = req.connection.remoteAddress; type = 1; } // HTTP(S) request + else if (req._socket) { ip = req._socket.remoteAddress; type = 2; } // WebSocket request + if (ip.startsWith('::ffff:')) { ip = ip.substring(7); } // Fix IPv4 IP's encoded in IPv6 form + if ((ip != null) && (obj.userAllowedIp.indexOf(ip) >= 0)) { return true; } + if (type == 1) { res.sendStatus(401); } + else if (type == 2) { try { req.close(); } catch (e) { } } + } catch (e) { console.log(e); } + return false; + } + // Return the current domain of the request function getDomain(req) { var x = req.url.split('/'); @@ -185,6 +202,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleLogoutRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); // Destroy the user's session to log them out will be re-created next request @@ -198,6 +216,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleLoginRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); obj.authenticate(req.body.username, req.body.password, domain, function (err, userid, passhint) { if (userid) { @@ -249,6 +268,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleCreateAccountRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if (domain.newAccounts == 0) { res.sendStatus(401); return; } if (!req.body.username || !req.body.email || !req.body.password1 || !req.body.password2 || (req.body.password1 != req.body.password2) || req.body.username == '~') { @@ -291,6 +311,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleDeleteAccountRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); // Check if the user is logged and we have all required parameters if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } @@ -313,6 +334,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle password changes function handlePasswordChangeRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); // Check if the user is logged and we have all required parameters if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } @@ -336,6 +358,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Indicates that any request to "/" should render "default" or "login" depending on login state function handleRootRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } if (!obj.args) { res.sendStatus(500); return; } var domain = getDomain(req); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); @@ -401,6 +424,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Renter the terms of service. function handleTermsRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); if (req.session && req.session.userid) { @@ -424,12 +448,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Returns the mesh server root certificate function handleRootCertRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + certificates.RootName + '.cer' }); res.send(new Buffer(getRootCertBase64(), 'base64')); } // Returns an mescript for Intel AMT configuration function handleMeScriptRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } if (req.query.type == 1) { var filename = 'cira_setup.mescript'; res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + filename }); @@ -500,6 +526,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle user public file downloads function handleDownloadUserFiles(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req), domainname = 'domain', spliturl = decodeURIComponent(req.path).split('/'), filename = ''; if ((spliturl.length < 3) || (obj.common.IsFilenameValid(spliturl[2]) == false) || (domain.userQuota == -1)) { res.sendStatus(404); return; } if (domain.id != '') { domainname = 'domain-' + domain.id; } @@ -522,6 +549,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Download a file from the server function handleDownloadFile(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if ((req.query.link == null) || (req.session == null) || (req.session.userid == null) || (domain == null) || (domain.userQuota == -1)) { res.sendStatus(404); return; } var user = obj.users[req.session.userid]; @@ -534,6 +562,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Upload a MeshCore.js file to the server function handleUploadMeshCoreFile(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; @@ -558,6 +587,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Upload a file to the server function handleUploadFile(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (domain.userQuota == -1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; @@ -619,6 +649,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a web socket relay request function handleRelayWebSocket(ws, req) { + if (checkUserIpAddress(ws, req) == false) { return; } var node, domain = getDomain(req); // Check if this is a logged in user var user, peering = true; @@ -881,6 +912,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle the web socket echo request, just echo back the data sent function handleEchoWebSocket(ws, req) { + if (checkUserIpAddress(ws, req) == false) { return; } + // When data is received from the web socket, echo it back ws.on('message', function (data) { var cmd = data.toString('utf8'); @@ -932,6 +965,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Indicates we want to handle websocket requests on "/control.ashx". function handleControlRequest(ws, req) { + if (checkUserIpAddress(ws, req) == false) { return; } var domain = getDomain(req); try { // Check if the user is logged in @@ -1803,6 +1837,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a server backup request function handleBackupRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (obj.parent.args.noserverbackup == 1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; @@ -1835,6 +1870,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a server restore request function handleRestoreRequest(req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (obj.parent.args.noserverbackup == 1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; @@ -1850,6 +1886,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a request to download a mesh agent obj.handleMeshAgentRequest = function (req, res) { + if (checkUserIpAddress(req, res) == false) { return; } if (req.query.id != null) { // Send a specific mesh agent back var argentInfo = obj.parent.meshAgentBinaries[req.query.id]; @@ -1879,6 +1916,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a request to download a mesh settings obj.handleMeshSettingsRequest = function (req, res) { + if (checkUserIpAddress(req, res) == false) { return; } var domain = getDomain(req); //if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }