2017-08-28 12:27:45 -04:00
/ * *
2018-01-04 15:15:21 -05:00
* @ description MeshCentral main module
2017-08-28 12:27:45 -04:00
* @ author Ylian Saint - Hilaire
2019-01-03 19:22:15 -05:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 15:15:21 -05:00
* @ license Apache - 2.0
2017-08-28 12:27:45 -04:00
* @ version v0 . 0.1
* /
2018-08-30 15:05:23 -04:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 15:24:15 -04:00
2018-11-29 20:59:29 -05:00
// If running NodeJS less than version 8, try to polyfill promisify
try { if ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) < 8 ) { require ( 'util.promisify' ) . shim ( ) ; } } catch ( ex ) { }
2018-01-09 23:13:41 -05:00
// If app metrics is available
if ( process . argv [ 2 ] == '--launch' ) { try { require ( 'appmetrics-dash' ) . monitor ( { url : '/' , title : 'MeshCentral' , port : 88 , host : '127.0.0.1' } ) ; } catch ( e ) { } }
2018-03-14 18:16:55 -04:00
function CreateMeshCentralServer ( config , args ) {
2017-08-28 12:27:45 -04:00
var obj = { } ;
2018-08-30 15:05:23 -04:00
obj . db = null ;
obj . webserver = null ;
obj . redirserver = null ;
obj . mpsserver = null ;
2019-08-29 17:38:13 -04:00
obj . apfserver = null ;
2018-08-30 15:05:23 -04:00
obj . swarmserver = null ;
obj . mailserver = null ;
obj . amtEventHandler = null ;
obj . amtScanner = null ;
obj . meshScanner = null ;
obj . letsencrypt = null ;
2017-08-28 12:27:45 -04:00
obj . eventsDispatch = { } ;
obj . fs = require ( 'fs' ) ;
obj . path = require ( 'path' ) ;
obj . crypto = require ( 'crypto' ) ;
2018-01-19 21:04:54 -05:00
obj . exeHandler = require ( './exeHandler.js' ) ;
2017-08-28 12:27:45 -04:00
obj . platform = require ( 'os' ) . platform ( ) ;
2018-03-14 18:16:55 -04:00
obj . args = args ;
2017-08-28 12:27:45 -04:00
obj . common = require ( './common.js' ) ;
2019-02-02 17:54:36 -05:00
obj . configurationFiles = null ;
2017-08-28 12:27:45 -04:00
obj . certificates = null ;
obj . connectivityByNode = { } ; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
2017-09-13 14:25:57 -04:00
obj . peerConnectivityByNode = { } ; // This object keeps a list of all connected CIRA and agents of peers, by serverid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
2019-08-22 18:31:39 -04:00
obj . debugSources = [ ] ;
2019-08-23 14:51:48 -04:00
obj . debugRemoteSources = null ;
2018-01-31 19:10:15 -05:00
obj . config = config ; // Configuration file
2017-08-28 12:27:45 -04:00
obj . dbconfig = { } ; // Persistance values, loaded from database
2018-01-09 23:13:41 -05:00
obj . certificateOperations = null ;
2017-12-12 19:04:54 -05:00
obj . defaultMeshCmd = null ;
2018-12-29 18:24:33 -05:00
obj . defaultMeshCores = { } ;
obj . defaultMeshCoresHash = { } ;
2017-10-15 02:22:19 -04:00
obj . meshAgentBinaries = { } ; // Mesh Agent Binaries, Architecture type --> { hash:(sha384 hash), size:(binary size), path:(binary path) }
obj . meshAgentInstallScripts = { } ; // Mesh Install Scripts, Script ID -- { hash:(sha384 hash), size:(binary size), path:(binary path) }
2017-08-28 12:27:45 -04:00
obj . multiServer = null ;
2017-09-06 21:10:24 -04:00
obj . maintenanceTimer = null ;
2017-09-13 14:25:57 -04:00
obj . serverId = null ;
2017-12-12 19:04:54 -05:00
obj . currentVer = null ;
2019-01-02 21:03:34 -05:00
obj . serverKey = Buffer . from ( obj . crypto . randomBytes ( 48 ) , 'binary' ) ;
2017-12-13 17:52:57 -05:00
obj . loginCookieEncryptionKey = null ;
2019-06-07 20:11:56 -04:00
obj . invitationLinkEncryptionKey = null ;
2018-09-26 17:58:55 -04:00
obj . serverSelfWriteAllowed = true ;
2019-03-26 17:11:51 -04:00
obj . serverStatsCounter = Math . floor ( Math . random ( ) * 1000 ) ;
2019-03-09 17:28:08 -05:00
obj . taskLimiter = obj . common . createTaskLimiterQueue ( 50 , 20 , 60 ) ; // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
2018-01-25 19:12:53 -05:00
try { obj . currentVer = JSON . parse ( obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'package.json' ) , 'utf8' ) ) . version ; } catch ( e ) { } // Fetch server version
2017-09-15 14:45:06 -04:00
2017-09-27 15:43:20 -04:00
// 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\\' ) ) ) {
2019-04-05 12:21:21 -04:00
obj . parentpath = obj . path . join ( _ _dirname , '../..' ) ;
2017-10-15 02:22:19 -04:00
obj . datapath = obj . path . join ( _ _dirname , '../../meshcentral-data' ) ;
obj . filespath = obj . path . join ( _ _dirname , '../../meshcentral-files' ) ;
2019-05-17 15:40:15 -04:00
obj . backuppath = obj . path . join ( _ _dirname , '../../meshcentral-backup' ) ;
2019-08-05 14:30:07 -04:00
obj . recordpath = obj . path . join ( _ _dirname , '../../meshcentral-recordings' ) ;
2019-08-25 14:08:32 -04:00
obj . webPublicPath = obj . path . join ( _ _dirname , 'public' ) ;
obj . webViewsPath = obj . path . join ( _ _dirname , 'views' ) ;
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web/views' ) ) ) { obj . webViewsOverridePath = obj . path . join ( _ _dirname , '../../meshcentral-web/views' ) ; }
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web/public' ) ) ) { obj . webPublicOverridePath = obj . path . join ( _ _dirname , '../../meshcentral-web/public' ) ; }
2017-09-27 15:43:20 -04:00
} else {
2019-04-05 12:21:21 -04:00
obj . parentpath = _ _dirname ;
2017-10-15 02:22:19 -04:00
obj . datapath = obj . path . join ( _ _dirname , '../meshcentral-data' ) ;
obj . filespath = obj . path . join ( _ _dirname , '../meshcentral-files' ) ;
2019-05-17 15:40:15 -04:00
obj . backuppath = obj . path . join ( _ _dirname , '../meshcentral-backups' ) ;
2019-08-05 14:30:07 -04:00
obj . recordpath = obj . path . join ( _ _dirname , '../meshcentral-recordings' ) ;
2019-08-25 14:08:32 -04:00
obj . webPublicPath = obj . path . join ( _ _dirname , 'public' ) ;
obj . webViewsPath = obj . path . join ( _ _dirname , 'views' ) ;
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web/views' ) ) ) { obj . webViewsOverridePath = obj . path . join ( _ _dirname , '../meshcentral-web/views' ) ; }
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web/public' ) ) ) { obj . webPublicOverridePath = obj . path . join ( _ _dirname , '../meshcentral-web/public' ) ; }
2017-09-27 15:43:20 -04:00
}
2019-08-12 17:58:06 -04:00
// Look to see if data and/or file path is specified
if ( obj . args . datapath ) { obj . datapath = obj . args . datapath ; }
if ( obj . args . filespath ) { obj . filespath = obj . args . filespath ; }
2017-08-28 12:27:45 -04:00
// Create data and files folders if needed
try { obj . fs . mkdirSync ( obj . datapath ) ; } catch ( e ) { }
try { obj . fs . mkdirSync ( obj . filespath ) ; } catch ( e ) { }
// Windows Specific Code, setup service and event log
obj . service = null ;
obj . servicelog = null ;
if ( obj . platform == 'win32' ) {
var nodewindows = require ( 'node-windows' ) ;
obj . service = nodewindows . Service ;
var eventlogger = nodewindows . EventLogger ;
obj . servicelog = new eventlogger ( 'MeshCentral' ) ;
}
// Start the Meshcentral server
obj . Start = function ( ) {
2018-08-30 15:05:23 -04:00
var i ;
2019-05-14 17:39:26 -04:00
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.
2018-07-23 20:34:24 -04:00
2017-08-28 12:27:45 -04:00
// Check for invalid arguments
2019-08-23 14:51:48 -04:00
var validArguments = [ '_' , 'notls' , 'user' , 'port' , 'aliasport' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'cert' , 'mpscert' , 'deletedomain' , 'deletedefaultdomain' , 'showall' , 'showusers' , 'shownodes' , 'showmeshes' , 'showevents' , 'showpower' , 'clearpower' , 'showiplocations' , 'help' , 'exactports' , 'install' , 'uninstall' , 'start' , 'stop' , 'restart' , 'debug' , 'filespath' , 'datapath' , 'noagentupdate' , 'launch' , 'noserverbackup' , 'mongodb' , 'mongodbcol' , 'wanonly' , 'lanonly' , 'nousers' , 'mpspass' , 'ciralocalfqdn' , 'dbexport' , 'dbexportmin' , 'dbimport' , 'dbmerge' , 'dbencryptkey' , 'selfupdate' , 'tlsoffload' , 'userallowedip' , 'userblockedip' , 'swarmallowedip' , 'agentallowedip' , 'agentblockedip' , 'fastcert' , 'swarmport' , 'logintoken' , 'logintokenkey' , 'logintokengen' , 'logintokengen' , 'mailtokengen' , 'admin' , 'unadmin' , 'sessionkey' , 'sessiontime' , 'minify' , 'minifycore' , 'dblistconfigfiles' , 'dbshowconfigfile' , 'dbpushconfigfiles' , 'dbpullconfigfiles' , 'dbdeleteconfigfiles' , 'configkey' , 'loadconfigfromdb' , 'npmpath' , 'memorytracking' , 'serverid' ] ;
2017-09-27 15:43:20 -04:00
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 ; } }
2017-08-28 12:27:45 -04:00
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 ; }
2018-08-30 15:05:23 -04:00
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.
2017-08-28 12:27:45 -04:00
if ( ( obj . args . help == true ) || ( obj . args [ '?' ] == true ) ) {
2019-01-24 20:01:50 -05:00
console . log ( 'MeshCentral v' + obj . currentVer + ', a open source remote computer management web portal.' ) ;
console . log ( 'Details at: https://www.meshcommander.com/meshcentral2\r\n' ) ;
2017-08-28 12:27:45 -04:00
if ( obj . platform == 'win32' ) {
console . log ( 'Run as a Windows Service' ) ;
console . log ( ' --install/uninstall Install Meshcentral as a background service.' ) ;
console . log ( ' --start/stop/restart Control Meshcentral background service.' ) ;
console . log ( 'Run standalone, console application' ) ;
}
console . log ( ' --notls Use HTTP instead of HTTPS for the main web server.' ) ;
console . log ( ' --user [username] Always login as [username] if account exists.' ) ;
console . log ( ' --port [number] Web server port number.' ) ;
console . log ( ' --mpsport [number] Intel AMT server port number.' ) ;
console . log ( ' --redirport [number] Creates an additional HTTP server to redirect users to the HTTPS server.' ) ;
console . log ( ' --exactports Server must run with correct ports or exit.' ) ;
console . log ( ' --noagentupdate Server will not update mesh agent native binaries.' ) ;
2018-07-13 22:18:43 -04:00
console . log ( ' --fastcert Generate weaker RSA2048 certificates.' ) ;
2018-05-29 19:57:08 -04:00
console . log ( ' --cert [name], (country), (org) Create a web server certificate with [name] server name.' ) ;
2017-08-28 12:27:45 -04:00
console . log ( ' country and organization can optionaly be set.' ) ;
return ;
}
2018-08-30 15:05:23 -04:00
2017-08-28 12:27:45 -04:00
// Check if we need to install, start, stop, remove ourself as a background service
if ( ( obj . service != null ) && ( ( obj . args . install == true ) || ( obj . args . uninstall == true ) || ( obj . args . start == true ) || ( obj . args . stop == true ) || ( obj . args . restart == true ) ) ) {
2018-03-08 20:58:22 -05:00
var env = [ ] , xenv = [ 'user' , 'port' , 'aliasport' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'exactport' , 'debug' ] ;
2018-08-30 15:05:23 -04:00
for ( 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 : obj . path . join ( _ _dirname , 'winservice.js' ) , env : env , wait : 2 , grow : 0.5 } ) ;
2017-08-28 12:27:45 -04:00
svc . on ( 'install' , function ( ) { console . log ( 'MeshCentral service installed.' ) ; svc . start ( ) ; } ) ;
svc . on ( 'uninstall' , function ( ) { console . log ( 'MeshCentral service uninstalled.' ) ; process . exit ( ) ; } ) ;
2019-07-18 16:11:08 -04:00
svc . on ( 'start' , function ( ) { console . log ( 'MeshCentral service started.' ) ; process . exit ( ) ; } ) ;
2017-08-28 12:27:45 -04:00
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 ( ) ; } ) ;
2018-08-30 15:05:23 -04:00
2017-09-27 15:43:20 -04:00
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 ;
2017-08-28 12:27:45 -04:00
}
// If "--launch" is in the arguments, launch now
2019-07-17 18:57:42 -04:00
if ( obj . args . launch ) {
2017-08-28 12:27:45 -04:00
obj . StartEx ( ) ;
} else {
// if "--launch" is not specified, launch the server as a child process.
2019-07-17 20:34:34 -04:00
var startArgs = [ ] ;
2018-08-30 15:05:23 -04:00
for ( i in process . argv ) {
2019-07-17 20:34:34 -04:00
if ( i > 0 ) {
var arg = process . argv [ i ] ;
2019-07-23 17:09:44 -04:00
if ( ( arg . length > 0 ) && ( ( arg . indexOf ( ' ' ) >= 0 ) || ( arg . indexOf ( '&' ) >= 0 ) ) ) { startArgs . push ( arg ) ; } else { startArgs . push ( arg ) ; }
2017-08-28 12:27:45 -04:00
}
}
2019-07-17 20:34:34 -04:00
startArgs . push ( '--launch' , process . pid ) ;
obj . launchChildServer ( startArgs ) ;
2017-08-28 12:27:45 -04:00
}
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Launch MeshCentral as a child server and monitor it.
2019-07-17 20:34:34 -04:00
obj . launchChildServer = function ( startArgs ) {
2017-08-28 12:27:45 -04:00
var child _process = require ( 'child_process' ) ;
2019-07-17 20:34:34 -04:00
childProcess = child _process . execFile ( process . argv [ 0 ] , startArgs , { maxBuffer : Infinity , cwd : obj . parentpath } , function ( error , stdout , stderr ) {
2019-07-17 18:57:42 -04:00
if ( childProcess . xrestart == 1 ) {
2019-07-18 13:42:22 -04:00
setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 500 ) ; // This is an expected restart.
2019-07-17 18:57:42 -04:00
} else if ( childProcess . xrestart == 2 ) {
2017-09-04 15:20:18 -04:00
console . log ( 'Expected exit...' ) ;
process . exit ( ) ; // User CTRL-C exit.
2019-07-17 18:57:42 -04:00
} else if ( childProcess . xrestart == 3 ) {
2017-09-06 21:10:24 -04:00
// Server self-update exit
2019-07-18 16:30:49 -04:00
var version = '' ;
if ( typeof obj . args . selfupdate == 'string' ) { version = '@' + obj . args . selfupdate ; }
2019-07-18 16:11:08 -04:00
var child _process = require ( 'child_process' ) ;
2019-07-18 16:30:49 -04:00
var npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
var npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
var xxprocess = child _process . exec ( npmpath + ' install meshcentral' + version + npmproxy , { maxBuffer : Infinity , cwd : obj . parentpath } , function ( error , stdout , stderr ) { } ) ;
2019-01-31 13:41:07 -05:00
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
2019-07-18 16:30:49 -04:00
xxprocess . on ( 'close' , function ( code ) { console . log ( 'Update completed...' ) ; setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 1000 ) ; } ) ;
2017-08-28 12:27:45 -04:00
} else {
2017-09-01 20:34:02 -04:00
if ( error != null ) {
2017-09-04 15:20:18 -04:00
// This is an un-expected restart
2017-09-13 14:25:57 -04:00
console . log ( error ) ;
2017-10-03 21:31:20 -04:00
console . log ( 'ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...' ) ;
2019-07-18 13:42:22 -04:00
setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 5000 ) ;
2018-08-30 15:05:23 -04:00
}
2017-08-28 12:27:45 -04:00
}
} ) ;
2019-07-17 18:57:42 -04:00
childProcess . stdout . on ( 'data' , function ( data ) { if ( data [ data . length - 1 ] == '\n' ) { data = data . substring ( 0 , data . length - 1 ) ; } if ( data . indexOf ( 'Updating settings folder...' ) >= 0 ) { childProcess . xrestart = 1 ; } else if ( data . indexOf ( 'Updating server certificates...' ) >= 0 ) { childProcess . xrestart = 1 ; } else if ( data . indexOf ( 'Server Ctrl-C exit...' ) >= 0 ) { childProcess . xrestart = 2 ; } else if ( data . indexOf ( 'Starting self upgrade...' ) >= 0 ) { childProcess . xrestart = 3 ; } else if ( data . indexOf ( 'Server restart...' ) >= 0 ) { childProcess . xrestart = 1 ; } console . log ( data ) ; } ) ;
childProcess . stderr . on ( 'data' , function ( data ) {
2018-01-15 00:01:06 -05:00
if ( data . startsWith ( 'le.challenges[tls-sni-01].loopback' ) ) { return ; } // Ignore this error output from GreenLock
2018-12-02 02:41:57 -05:00
if ( data [ data . length - 1 ] == '\n' ) { data = data . substring ( 0 , data . length - 1 ) ; }
2019-07-08 18:59:44 -04:00
try {
var errlogpath = null ;
if ( typeof obj . args . mesherrorlogpath == 'string' ) { errlogpath = obj . path . join ( obj . args . mesherrorlogpath , 'mesherrors.txt' ) ; } else { errlogpath = obj . getConfigFilePath ( 'mesherrors.txt' ) ; }
obj . fs . appendFileSync ( obj . getConfigFilePath ( 'mesherrors.txt' ) , '-------- ' + new Date ( ) . toLocaleString ( ) + ' ---- ' + obj . currentVer + ' --------\r\n\r\n' + data + '\r\n\r\n\r\n' ) ;
} catch ( ex ) { console . log ( 'ERROR: Unable to write to mesherrors.txt.' ) ; }
2018-01-15 00:01:06 -05:00
} ) ;
2019-07-17 18:57:42 -04:00
childProcess . on ( 'close' , function ( code ) { if ( ( code != 0 ) && ( code != 123 ) ) { /* console.log("Exited with code " + code); */ } } ) ;
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Get current and latest MeshCentral server versions using NPM
obj . getLatestServerVersion = function ( callback ) {
2017-09-15 14:45:06 -04:00
if ( callback == null ) return ;
2019-03-07 22:56:24 -05:00
try {
if ( typeof obj . args . selfupdate == 'string' ) { callback ( obj . currentVer , obj . args . selfupdate ) ; return ; } // If we are targetting a specific version, return that one as current.
var child _process = require ( 'child_process' ) ;
var npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
2019-07-17 18:57:42 -04:00
var npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
var xxprocess = child _process . exec ( npmpath + npmproxy + ' view meshcentral dist-tags.latest' , { maxBuffer : 512000 , cwd : obj . parentpath } , function ( error , stdout , stderr ) { } ) ;
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { } ) ;
xxprocess . on ( 'close' , function ( code ) {
2019-03-07 22:56:24 -05:00
var latestVer = null ;
2019-07-17 18:57:42 -04:00
if ( code == 0 ) { try { latestVer = xxprocess . data . split ( ' ' ) . join ( '' ) . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; } catch ( e ) { } }
2019-03-07 22:56:24 -05:00
callback ( obj . currentVer , latestVer ) ;
} ) ;
2019-05-01 18:02:03 -04:00
} catch ( ex ) { callback ( obj . currentVer , null , ex ) ; } // If the system is running out of memory, an exception here can easily happen.
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
2017-09-06 21:10:24 -04:00
// Initiate server self-update
2019-07-18 16:11:08 -04:00
obj . performServerUpdate = function ( ) {
if ( obj . serverSelfWriteAllowed == true ) { console . log ( 'Starting self upgrade...' ) ; process . exit ( 200 ) ; return true ; }
return false ;
} ;
2017-09-06 21:10:24 -04:00
2018-01-15 00:01:06 -05:00
// Initiate server self-update
2018-08-30 15:05:23 -04:00
obj . performServerCertUpdate = function ( ) { console . log ( 'Updating server certificates...' ) ; process . exit ( 200 ) ; } ;
2018-01-15 00:01:06 -05:00
2019-02-02 17:54:36 -05:00
// Look for easy command line instructions and do them here.
2017-08-28 12:27:45 -04:00
obj . StartEx = function ( ) {
2018-08-30 15:05:23 -04:00
var i ;
2018-03-14 18:16:55 -04:00
//var wincmd = require('node-windows');
//wincmd.list(function (svc) { console.log(svc); }, true);
2018-08-30 15:05:23 -04:00
2019-06-12 13:23:26 -04:00
// Check top level configuration for any unreconized values
if ( config ) { for ( var i in config ) { if ( ( typeof i == 'string' ) && ( i . length > 0 ) && ( i [ 0 ] != '_' ) && ( [ 'settings' , 'domains' , 'configfiles' , 'smtp' , 'letsencrypt' , 'peers' ] . indexOf ( i ) == - 1 ) ) { console . log ( 'WARNING: unrecognized configuration option \"' + i + '\".' ) ; } } }
2019-01-07 16:18:20 -05:00
if ( typeof obj . args . userallowedip == 'string' ) { if ( obj . args . userallowedip == '' ) { obj . args . userallowedip = null ; } else { obj . args . userallowedip = obj . args . userallowedip . split ( ',' ) ; } }
2019-01-30 16:43:42 -05:00
if ( typeof obj . args . userblockedip == 'string' ) { if ( obj . args . userblockedip == '' ) { obj . args . userblockedip = null ; } else { obj . args . userblockedip = obj . args . userblockedip . split ( ',' ) ; } }
if ( typeof obj . args . agentallowedip == 'string' ) { if ( obj . args . agentallowedip == '' ) { obj . args . agentallowedip = null ; } else { obj . args . agentallowedip = obj . args . agentallowedip . split ( ',' ) ; } }
if ( typeof obj . args . agentblockedip == 'string' ) { if ( obj . args . agentblockedip == '' ) { obj . args . agentblockedip = null ; } else { obj . args . agentblockedip = obj . args . agentblockedip . split ( ',' ) ; } }
2019-01-11 13:02:54 -05:00
if ( typeof obj . args . swarmallowedip == 'string' ) { if ( obj . args . swarmallowedip == '' ) { obj . args . swarmallowedip = null ; } else { obj . args . swarmallowedip = obj . args . swarmallowedip . split ( ',' ) ; } }
2019-08-22 18:31:39 -04:00
// Local console tracing
if ( typeof obj . args . debug == 'string' ) { obj . debugSources = obj . args . debug . toLowerCase ( ) . split ( ',' ) ; }
else if ( typeof obj . args . debug == 'object' ) { obj . debugSources = obj . args . debug ; }
else if ( obj . args . debug === true ) { obj . debugSources = '*' ; }
2019-05-08 21:14:30 -04:00
require ( './db.js' ) . CreateDB ( obj ,
function ( db ) {
obj . db = db ;
obj . db . SetupDatabase ( function ( dbversion ) {
// See if any database operations needs to be completed
if ( obj . args . deletedomain ) { obj . db . DeleteDomain ( obj . args . deletedomain , function ( ) { console . log ( 'Deleted domain ' + obj . args . deletedomain + '.' ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . deletedefaultdomain ) { obj . db . DeleteDomain ( '' , function ( ) { console . log ( 'Deleted default domain.' ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showall ) { obj . db . GetAll ( function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showusers ) { obj . db . GetAllType ( 'user' , function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . shownodes ) { obj . db . GetAllType ( 'node' , function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showmeshes ) { obj . db . GetAllType ( 'mesh' , function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showevents ) { obj . db . GetAllEvents ( function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showpower ) { obj . db . getAllPower ( function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . clearpower ) { obj . db . removeAllPowerEvents ( function ( ) { process . exit ( ) ; } ) ; return ; }
if ( obj . args . showiplocations ) { obj . db . GetAllType ( 'iploc' , function ( err , docs ) { console . log ( docs ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . logintoken ) { obj . getLoginToken ( obj . args . logintoken , function ( r ) { console . log ( r ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . logintokenkey ) { obj . showLoginTokenKey ( function ( r ) { console . log ( r ) ; process . exit ( ) ; } ) ; return ; }
// Show a list of all configuration files in the database
if ( obj . args . dblistconfigfiles ) {
obj . db . GetAllType ( 'cfile' , function ( err , docs ) { if ( err == null ) { if ( docs . length == 0 ) { console . log ( 'No files found.' ) ; } else { for ( var i in docs ) { console . log ( docs [ i ] . _id . split ( '/' ) [ 1 ] + ', ' + Buffer . from ( docs [ i ] . data , 'base64' ) . length + ' bytes.' ) ; } } } else { console . log ( 'Unable to read from database.' ) ; } process . exit ( ) ; } ) ; return ;
}
2019-02-02 17:54:36 -05:00
2019-05-08 21:14:30 -04:00
// Display the content of a configuration file in the database
if ( obj . args . dbshowconfigfile ) {
if ( typeof obj . args . configkey != 'string' ) { console . log ( 'Error, --configkey is required.' ) ; process . exit ( ) ; return ; }
obj . db . getConfigFile ( obj . args . dbshowconfigfile , function ( err , docs ) {
if ( err == null ) {
if ( docs . length == 0 ) { console . log ( 'File not found.' ) ; } else {
var data = obj . db . decryptData ( obj . args . configkey , docs [ 0 ] . data ) ;
if ( data == null ) { console . log ( 'Invalid config key.' ) ; } else { console . log ( data ) ; }
}
} else { console . log ( 'Unable to read from database.' ) ; }
process . exit ( ) ;
} ) ; return ;
}
2019-02-02 17:54:36 -05:00
2019-05-08 21:14:30 -04:00
// Delete all configuration files from database
if ( obj . args . dbdeleteconfigfiles ) {
console . log ( 'Deleting all configuration files from the database...' ) ; obj . db . RemoveAllOfType ( 'cfile' , function ( ) { console . log ( 'Done.' ) ; process . exit ( ) ; } ) ;
}
2019-02-01 18:16:46 -05:00
2019-05-08 21:14:30 -04:00
// Push all relevent files from meshcentral-data into the database
if ( obj . args . dbpushconfigfiles ) {
if ( typeof obj . args . configkey != 'string' ) { console . log ( 'Error, --configkey is required.' ) ; process . exit ( ) ; return ; }
if ( ( obj . args . dbpushconfigfiles !== true ) && ( typeof obj . args . dbpushconfigfiles != 'string' ) ) {
console . log ( 'Usage: --dbpulldatafiles (path) This will import files from folder into the database' ) ;
console . log ( ' --dbpulldatafiles This will import files from meshcentral-data into the db.' ) ;
process . exit ( ) ;
} else {
if ( ( obj . args . dbpushconfigfiles == '*' ) || ( obj . args . dbpushconfigfiles === true ) ) { obj . args . dbpushconfigfiles = obj . datapath ; }
2019-02-26 17:39:45 -05:00
obj . fs . readdir ( obj . args . dbpushconfigfiles , function ( err , files ) {
2019-05-08 21:14:30 -04:00
if ( err != null ) { console . log ( 'ERROR: Unable to read from folder ' + obj . args . dbpushconfigfiles ) ; process . exit ( ) ; return ; }
var configFound = false ;
for ( var i in files ) { if ( files [ i ] == 'config.json' ) { configFound = true ; } }
if ( configFound == false ) { console . log ( 'ERROR: No config.json in folder ' + obj . args . dbpushconfigfiles ) ; process . exit ( ) ; return ; }
obj . db . RemoveAllOfType ( 'cfile' , function ( ) {
obj . fs . readdir ( obj . args . dbpushconfigfiles , function ( err , files ) {
var lockCount = 1
for ( var i in files ) {
const file = files [ i ] ;
if ( ( file == 'config.json' ) || file . endsWith ( '.key' ) || file . endsWith ( '.crt' ) || ( file == 'terms.txt' ) || file . endsWith ( '.jpg' ) || file . endsWith ( '.png' ) ) {
const path = obj . path . join ( obj . args . dbpushconfigfiles , files [ i ] ) , binary = Buffer . from ( obj . fs . readFileSync ( path , { encoding : 'binary' } ) , 'binary' ) ;
console . log ( 'Pushing ' + file + ', ' + binary . length + ' bytes.' ) ;
lockCount ++ ;
obj . db . setConfigFile ( file , obj . db . encryptData ( obj . args . configkey , binary ) , function ( ) { if ( ( -- lockCount ) == 0 ) { console . log ( 'Done.' ) ; process . exit ( ) ; } } ) ;
}
}
if ( -- lockCount == 0 ) { process . exit ( ) ; }
} ) ;
} ) ;
2019-02-26 17:39:45 -05:00
} ) ;
2019-05-08 21:14:30 -04:00
}
return ;
}
2019-02-01 18:16:46 -05:00
2019-05-08 21:14:30 -04:00
// Pull all database files into meshcentral-data
if ( obj . args . dbpullconfigfiles ) {
if ( typeof obj . args . configkey != 'string' ) { console . log ( 'Error, --configkey is required.' ) ; process . exit ( ) ; return ; }
if ( typeof obj . args . dbpullconfigfiles != 'string' ) {
console . log ( 'Usage: --dbpulldatafiles (path)' ) ;
process . exit ( ) ;
} else {
obj . db . GetAllType ( 'cfile' , function ( err , docs ) {
if ( err == null ) {
if ( docs . length == 0 ) {
console . log ( 'File not found.' ) ;
2019-02-02 17:54:36 -05:00
} else {
2019-05-08 21:14:30 -04:00
for ( var i in docs ) {
const file = docs [ i ] . _id . split ( '/' ) [ 1 ] , binary = obj . db . decryptData ( obj . args . configkey , docs [ i ] . data ) ;
if ( binary == null ) {
console . log ( 'Invalid config key.' ) ;
} else {
var fullFileName = obj . path . join ( obj . args . dbpullconfigfiles , file ) ;
try { obj . fs . writeFileSync ( fullFileName , binary ) ; } catch ( ex ) { console . log ( 'Unable to write to ' + fullFileName ) ; process . exit ( ) ; return ; }
console . log ( 'Pulling ' + file + ', ' + binary . length + ' bytes.' ) ;
}
}
2019-02-02 17:54:36 -05:00
}
2019-05-08 21:14:30 -04:00
} else {
console . log ( 'Unable to read from database.' ) ;
2019-02-01 18:16:46 -05:00
}
2019-05-08 21:14:30 -04:00
process . exit ( ) ;
} ) ;
2019-02-01 18:16:46 -05:00
}
2019-05-08 21:14:30 -04:00
return ;
}
2019-02-01 18:16:46 -05:00
2019-05-08 21:14:30 -04:00
if ( obj . args . dbexport ) {
// Export the entire database to a JSON file
if ( obj . args . dbexport == true ) { obj . args . dbexport = obj . getConfigFilePath ( 'meshcentral.db.json' ) ; }
obj . db . GetAll ( function ( err , docs ) {
obj . fs . writeFileSync ( obj . args . dbexport , JSON . stringify ( docs ) ) ;
console . log ( 'Exported ' + docs . length + ' objects(s) to ' + obj . args . dbexport + '.' ) ; process . exit ( ) ;
2019-02-25 14:13:13 -05:00
} ) ;
2019-05-08 21:14:30 -04:00
return ;
}
if ( obj . args . dbexportmin ) {
// Export a minimal database to a JSON file. Export only users, meshes and nodes.
// This is a useful command to look at the database.
if ( obj . args . dbexportmin == true ) { obj . args . dbexportmin = obj . getConfigFilePath ( 'meshcentral.db.json' ) ; }
obj . db . GetAllType ( { $in : [ 'user' , 'node' , 'mesh' ] } , function ( err , docs ) {
obj . fs . writeFileSync ( obj . args . dbexportmin , JSON . stringify ( docs ) ) ;
console . log ( 'Exported ' + docs . length + ' objects(s) to ' + obj . args . dbexportmin + '.' ) ; process . exit ( ) ;
} ) ;
return ;
}
if ( obj . args . dbimport ) {
// Import the entire database from a JSON file
if ( obj . args . dbimport == true ) { obj . args . dbimport = obj . getConfigFilePath ( 'meshcentral.db.json' ) ; }
var json = null , json2 = "" , badCharCount = 0 ;
try { json = obj . fs . readFileSync ( obj . args . dbimport , { encoding : 'utf8' } ) ; } catch ( e ) { console . log ( 'Invalid JSON file: ' + obj . args . dbimport + ': ' + e ) ; process . exit ( ) ; }
for ( i = 0 ; i < json . length ; i ++ ) { if ( json . charCodeAt ( i ) >= 32 ) { json2 += json [ i ] ; } else { var tt = json . charCodeAt ( i ) ; if ( tt != 10 && tt != 13 ) { badCharCount ++ ; } } } // Remove all bad chars
if ( badCharCount > 0 ) { console . log ( badCharCount + ' invalid character(s) where removed.' ) ; }
try { json = JSON . parse ( json2 ) ; } catch ( e ) { console . log ( 'Invalid JSON format: ' + obj . args . dbimport + ': ' + e ) ; process . exit ( ) ; }
if ( ( json == null ) || ( typeof json . length != 'number' ) || ( json . length < 1 ) ) { console . log ( 'Invalid JSON format: ' + obj . args . dbimport + '.' ) ; }
for ( i in json ) { if ( ( json [ i ] . type == "mesh" ) && ( json [ i ] . links != null ) ) { for ( var j in json [ i ] . links ) { var esc = obj . common . escapeFieldName ( j ) ; if ( esc !== j ) { json [ i ] . links [ esc ] = json [ i ] . links [ j ] ; delete json [ i ] . links [ j ] ; } } } } // Escape MongoDB invalid field chars
//for (i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname
setTimeout ( function ( ) { // If the Mongo database is being created for the first time, there is a race condition here. This will get around it.
obj . db . RemoveAll ( function ( ) {
obj . db . InsertMany ( json , function ( err ) {
if ( err != null ) { console . log ( err ) ; } else { console . log ( 'Imported ' + json . length + ' objects(s) from ' + obj . args . dbimport + '.' ) ; } process . exit ( ) ;
} ) ;
} ) ;
} , 100 ) ;
return ;
}
/ *
if ( obj . args . dbimport ) {
// Import the entire database from a very large JSON file
obj . db . RemoveAll ( function ( ) {
if ( obj . args . dbimport == true ) { obj . args . dbimport = obj . getConfigFilePath ( 'meshcentral.db.json' ) ; }
var json = null , json2 = "" , badCharCount = 0 ;
const StreamArray = require ( 'stream-json/streamers/StreamArray' ) ;
const jsonStream = StreamArray . withParser ( ) ;
jsonStream . on ( 'data' , function ( data ) { obj . db . Set ( data . value ) ; } ) ;
jsonStream . on ( 'end' , ( ) => { console . log ( 'Done.' ) ; process . exit ( ) ; } ) ;
obj . fs . createReadStream ( obj . args . dbimport ) . pipe ( jsonStream . input ) ;
} ) ;
return ;
}
* /
if ( obj . args . dbmerge ) {
// Import the entire database from a JSON file
if ( obj . args . dbmerge == true ) { obj . args . dbmerge = obj . getConfigFilePath ( 'meshcentral.db.json' ) ; }
var json = null , json2 = "" , badCharCount = 0 ;
try { json = obj . fs . readFileSync ( obj . args . dbmerge , { encoding : 'utf8' } ) ; } catch ( e ) { console . log ( 'Invalid JSON file: ' + obj . args . dbmerge + ': ' + e ) ; process . exit ( ) ; }
for ( i = 0 ; i < json . length ; i ++ ) { if ( json . charCodeAt ( i ) >= 32 ) { json2 += json [ i ] ; } else { var tt = json . charCodeAt ( i ) ; if ( tt != 10 && tt != 13 ) { badCharCount ++ ; } } } // Remove all bad chars
if ( badCharCount > 0 ) { console . log ( badCharCount + ' invalid character(s) where removed.' ) ; }
try { json = JSON . parse ( json2 ) ; } catch ( e ) { console . log ( 'Invalid JSON format: ' + obj . args . dbmerge + ': ' + e ) ; process . exit ( ) ; }
if ( ( json == null ) || ( typeof json . length != 'number' ) || ( json . length < 1 ) ) { console . log ( 'Invalid JSON format: ' + obj . args . dbimport + '.' ) ; }
// Get all users from current database
obj . db . GetAllType ( 'user' , function ( err , docs ) {
var users = { } , usersCount = 0 ;
for ( var i in docs ) { users [ docs [ i ] . _id ] = docs [ i ] ; usersCount ++ ; }
// Fetch all meshes from the database
obj . db . GetAllType ( 'mesh' , function ( err , docs ) {
obj . common . unEscapeAllLinksFieldName ( docs ) ;
var meshes = { } , meshesCount = 0 ;
for ( var i in docs ) { meshes [ docs [ i ] . _id ] = docs [ i ] ; meshesCount ++ ; }
console . log ( 'Loaded ' + usersCount + ' users and ' + meshesCount + ' meshes.' ) ;
// Look at each object in the import file
var objectToAdd = [ ] ;
for ( var i in json ) {
var newobj = json [ i ] ;
if ( newobj . type == 'user' ) {
// Check if the user already exists
var existingUser = users [ newobj . _id ] ;
if ( existingUser ) {
// Merge the links
if ( typeof newobj . links == 'object' ) {
for ( var j in newobj . links ) {
if ( ( existingUser . links == null ) || ( existingUser . links [ j ] == null ) ) {
if ( existingUser . links == null ) { existingUser . links = { } ; }
existingUser . links [ j ] = newobj . links [ j ] ;
}
}
2019-02-15 18:11:52 -05:00
}
2019-05-08 21:14:30 -04:00
if ( existingUser . name == 'admin' ) { existingUser . links = { } ; }
objectToAdd . push ( existingUser ) ; // Add this user
} else {
objectToAdd . push ( newobj ) ; // Add this user
2019-02-15 17:50:23 -05:00
}
2019-05-08 21:14:30 -04:00
} else if ( newobj . type == 'mesh' ) {
// Add this object after escaping
objectToAdd . push ( obj . common . escapeLinksFieldName ( newobj ) ) ;
} // Don't add nodes.
2019-02-15 17:50:23 -05:00
}
2019-05-08 21:14:30 -04:00
console . log ( 'Importing ' + objectToAdd . length + ' object(s)...' ) ;
var pendingCalls = 1 ;
for ( var i in objectToAdd ) {
pendingCalls ++ ;
obj . db . Set ( objectToAdd [ i ] , function ( err ) { if ( err != null ) { console . log ( err ) ; } else { if ( -- pendingCalls == 0 ) { process . exit ( ) ; } } } ) ;
}
if ( -- pendingCalls == 0 ) { process . exit ( ) ; }
} ) ;
} ) ;
2018-07-16 20:49:55 -04:00
return ;
2019-02-02 17:54:36 -05:00
}
2018-07-16 20:49:55 -04:00
2019-05-08 21:14:30 -04:00
// Load configuration for database if needed
if ( obj . args . loadconfigfromdb ) {
var key = null ;
if ( typeof obj . args . configkey == 'string' ) { key = obj . args . configkey ; }
else if ( typeof obj . args . loadconfigfromdb == 'string' ) { key = obj . args . loadconfigfromdb ; }
if ( key == null ) { console . log ( 'Error, --configkey is required.' ) ; process . exit ( ) ; return ; }
obj . db . getAllConfigFiles ( key , function ( configFiles ) {
if ( configFiles == null ) { console . log ( 'Error, no configuration files found or invalid configkey.' ) ; process . exit ( ) ; return ; }
if ( ! configFiles [ 'config.json' ] ) { console . log ( 'Error, could not file config.json from database.' ) ; process . exit ( ) ; return ; }
obj . configurationFiles = configFiles ;
// Parse the new configuration file
var config2 = null ;
try { config2 = JSON . parse ( configFiles [ 'config.json' ] ) ; } catch ( ex ) { console . log ( 'Error, unable to parse config.json from database.' ) ; process . exit ( ) ; return ; }
// Set the command line arguments to the config file if they are not present
if ( ! config2 . settings ) { config2 . settings = { } ; }
for ( i in args ) { config2 . settings [ i ] = args [ i ] ; }
// Lower case all keys in the config file
try {
require ( './common.js' ) . objKeysToLower ( config2 , [ "ldapoptions" ] ) ;
} catch ( ex ) {
console . log ( 'CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.' ) ;
process . exit ( ) ;
return ;
}
// Grad some of the values from the original config.json file if present.
config2 [ 'mongodb' ] = config [ 'mongodb' ] ;
config2 [ 'mongodbcol' ] = config [ 'mongodbcol' ] ;
config2 [ 'dbencryptkey' ] = config [ 'dbencryptkey' ] ;
2019-02-02 17:54:36 -05:00
2019-05-08 21:14:30 -04:00
// We got a new config.json from the database, let's use it.
config = obj . config = config2 ;
obj . StartEx1b ( ) ;
} ) ;
} else {
config = obj . config = getConfig ( true ) ;
obj . StartEx1b ( ) ;
}
2018-07-16 20:49:55 -04:00
} ) ;
}
2019-05-08 21:14:30 -04:00
) ;
2019-02-02 17:54:36 -05:00
} ;
2018-07-16 20:49:55 -04:00
2019-07-17 18:57:42 -04:00
// Time to start the server of real.
2019-02-02 17:54:36 -05:00
obj . StartEx1b = function ( ) {
var i ;
2017-08-28 12:27:45 -04:00
2019-07-18 16:11:08 -04:00
// Check if self update is allowed. If running as a Windows service, self-update is not possible.
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'daemon' ) ) ) { obj . serverSelfWriteAllowed = false ; }
2019-02-02 17:54:36 -05:00
// If we are targetting a specific version, update now.
2019-07-18 16:11:08 -04:00
if ( ( obj . serverSelfWriteAllowed == true ) && ( typeof obj . args . selfupdate == 'string' ) ) {
2019-02-02 17:54:36 -05:00
obj . args . selfupdate = obj . args . selfupdate . toLowerCase ( ) ;
if ( obj . currentVer !== obj . args . selfupdate ) { obj . performServerUpdate ( ) ; return ; } // We are targetting a specific version, run self update now.
}
2017-08-28 12:27:45 -04:00
2019-02-02 17:54:36 -05:00
// Write the server state
obj . updateServerState ( 'state' , 'starting' ) ;
2019-07-17 18:57:42 -04:00
if ( process . pid ) { obj . updateServerState ( 'server-pid' , process . pid ) ; }
2019-07-17 20:34:34 -04:00
if ( process . ppid ) { obj . updateServerState ( 'server-parent-pid' , process . ppid ) ; }
2019-02-02 17:54:36 -05:00
2019-03-07 22:56:24 -05:00
// Start memory tracking if requested
if ( typeof obj . args . memorytracking == 'number' ) {
var info = process . memoryUsage ( ) , txt = [ ] ;
info . time = Date . now ( ) ;
for ( var i in info ) { txt . push ( i ) ; }
2019-03-14 15:28:58 -04:00
obj . fs . appendFile ( obj . getConfigFilePath ( 'memorytracking.csv' ) , txt . join ( ',' ) + '\r\n' , function ( err ) { } ) ;
2019-03-07 22:56:24 -05:00
setInterval ( function ( ) {
var info = process . memoryUsage ( ) , txt = [ ] ;
info . time = Date . now ( ) ;
for ( var i in info ) { txt . push ( info [ i ] ) ; }
2019-03-14 15:28:58 -04:00
obj . fs . appendFile ( obj . getConfigFilePath ( 'memorytracking.csv' ) , txt . join ( ',' ) + '\r\n' , function ( err ) { } ) ;
2019-03-07 22:56:24 -05:00
} , ( obj . args . memorytracking * 1000 ) ) ;
}
2019-02-02 17:54:36 -05:00
// Read environment variables. For a subset of arguments, we allow them to be read from environment variables.
var xenv = [ 'user' , 'port' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'exactport' , 'debug' ] ;
for ( i in xenv ) { if ( ( obj . args [ xenv [ i ] ] == null ) && ( process . env [ 'mesh' + xenv [ i ] ] ) ) { obj . args [ xenv [ i ] ] = obj . common . toNumber ( process . env [ 'mesh' + xenv [ i ] ] ) ; } }
// Validate the domains, this is used for multi-hosting
if ( obj . config . domains == null ) { obj . config . domains = { } ; }
if ( obj . config . domains [ '' ] == null ) { obj . config . domains [ '' ] = { } ; }
if ( obj . config . domains [ '' ] . dns != null ) { console . log ( "ERROR: Default domain can't have a DNS name." ) ; return ; }
var xdomains = { } ; for ( i in obj . config . domains ) { if ( obj . config . domains [ i ] . title == null ) { obj . config . domains [ i ] . title = 'MeshCentral' ; } if ( obj . config . domains [ i ] . title2 == null ) { obj . config . domains [ i ] . title2 = '2.0 Beta 2' ; } xdomains [ i . toLowerCase ( ) ] = obj . config . domains [ i ] ; } obj . config . domains = xdomains ;
var bannedDomains = [ 'public' , 'private' , 'images' , 'scripts' , 'styles' , 'views' ] ; // List of banned domains
2019-04-11 16:41:51 -04:00
for ( i in obj . config . domains ) { for ( var j in bannedDomains ) { if ( i == bannedDomains [ j ] ) { console . log ( "ERROR: Domain '" + i + "' is not allowed domain name in config.json." ) ; return ; } } }
2019-02-02 17:54:36 -05:00
for ( i in obj . config . domains ) {
2019-04-11 16:41:51 -04:00
if ( typeof config . domains [ i ] . auth == 'string' ) { config . domains [ i ] . auth = config . domains [ i ] . auth . toLowerCase ( ) ; }
2019-02-11 17:41:15 -05:00
if ( obj . config . domains [ i ] . limits == null ) { obj . config . domains [ i ] . limits = { } ; }
2019-02-02 17:54:36 -05:00
if ( obj . config . domains [ i ] . dns == null ) { obj . config . domains [ i ] . url = ( i == '' ) ? '/' : ( '/' + i + '/' ) ; } else { obj . config . domains [ i ] . url = '/' ; }
obj . config . domains [ i ] . id = i ;
if ( typeof obj . config . domains [ i ] . userallowedip == 'string' ) { if ( obj . config . domains [ i ] . userallowedip == '' ) { obj . config . domains [ i ] . userallowedip = null ; } else { obj . config . domains [ i ] . userallowedip = obj . config . domains [ i ] . userallowedip . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . userblockedip == 'string' ) { if ( obj . config . domains [ i ] . userblockedip == '' ) { obj . config . domains [ i ] . userblockedip = null ; } else { obj . config . domains [ i ] . userblockedip = obj . config . domains [ i ] . userallowedip . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . agentallowedip == 'string' ) { if ( obj . config . domains [ i ] . agentallowedip == '' ) { obj . config . domains [ i ] . agentallowedip = null ; } else { obj . config . domains [ i ] . agentallowedip = obj . config . domains [ i ] . agentallowedip . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . agentblockedip == 'string' ) { if ( obj . config . domains [ i ] . agentblockedip == '' ) { obj . config . domains [ i ] . agentblockedip = null ; } else { obj . config . domains [ i ] . agentblockedip = obj . config . domains [ i ] . agentblockedip . split ( ',' ) ; } }
2019-04-11 16:41:51 -04:00
if ( ( obj . config . domains [ i ] . auth == 'ldap' ) && ( typeof obj . config . domains [ i ] . ldapoptions != 'object' ) ) {
if ( i == '' ) { console . log ( "ERROR: Default domain is LDAP, but is missing LDAPOptions." ) ; } else { console . log ( "ERROR: Domain '" + i + "' is LDAP, but is missing LDAPOptions." ) ; }
process . exit ( ) ;
return ;
}
2019-04-11 17:52:23 -04:00
if ( ( obj . config . domains [ i ] . auth == 'ldap' ) || ( obj . config . domains [ i ] . auth == 'sspi' ) ) { obj . config . domains [ i ] . newaccounts = 0 ; } // No new accounts allowed in SSPI/LDAP authentication modes.
2019-05-12 22:14:24 -04:00
// Convert newAccountsRights from a array of strings to flags number.
if ( obj . config . domains [ i ] . newaccountsrights && Array . isArray ( obj . config . domains [ i ] . newaccountsrights ) ) {
var newAccRights = 0 ;
for ( var j in obj . config . domains [ i ] . newaccountsrights ) {
var r = obj . config . domains [ i ] . newaccountsrights [ j ] . toLowerCase ( ) ;
2019-06-27 01:13:56 -04:00
if ( r == 'fulladmin' ) { newAccRights = 4294967295 ; } // 0xFFFFFFFF
2019-05-12 22:14:24 -04:00
if ( r == 'serverbackup' ) { newAccRights |= 1 ; }
if ( r == 'manageusers' ) { newAccRights |= 2 ; }
if ( r == 'serverrestore' ) { newAccRights |= 4 ; }
if ( r == 'fileaccess' ) { newAccRights |= 8 ; }
if ( r == 'serverupdate' ) { newAccRights |= 16 ; }
if ( r == 'locked' ) { newAccRights |= 32 ; }
if ( r == 'nonewgroups' ) { newAccRights |= 64 ; }
if ( r == 'notools' ) { newAccRights |= 128 ; }
}
obj . config . domains [ i ] . newaccountsrights = newAccRights ;
}
if ( obj . config . domains [ i ] . newaccountsrights && ( typeof ( obj . config . domains [ i ] . newaccountsrights ) != 'number' ) ) { delete obj . config . domains [ i ] . newaccountsrights ; }
2019-02-02 17:54:36 -05:00
}
// Log passed arguments into Windows Service Log
//if (obj.servicelog != null) { var s = ''; for (i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); }
// Look at passed in arguments
if ( ( obj . args . user != null ) && ( typeof obj . args . user != 'string' ) ) { delete obj . args . user ; }
if ( ( obj . args . ciralocalfqdn != null ) && ( ( obj . args . lanonly == true ) || ( obj . args . wanonly == true ) ) ) { console . log ( "WARNING: 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 ) ) { console . log ( "WARNING: Can't have more than 4 CIRA local FQDN's. Ignoring value." ) ; obj . args . ciralocalfqdn = null ; }
if ( obj . args . ignoreagenthashcheck === true ) { console . log ( "WARNING: 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 . 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 . notls == null && obj . args . redirport == null ) obj . args . redirport = 80 ;
if ( obj . args . minifycore === 0 ) obj . args . minifycore = false ;
2019-06-17 18:20:47 -04:00
if ( typeof args . agentidletimeout != 'number' ) { args . agentidletimeout = 150000 ; } else { args . agentidletimeout *= 1000 } // Default agent idle timeout is 2m, 30sec.
2019-02-02 17:54:36 -05:00
// Setup a site administrator
if ( ( obj . args . admin ) && ( typeof obj . args . admin == 'string' ) ) {
var adminname = obj . args . admin . split ( '/' ) ;
if ( adminname . length == 1 ) { adminname = 'user//' + adminname [ 0 ] ; }
else if ( adminname . length == 2 ) { adminname = 'user/' + adminname [ 0 ] + '/' + adminname [ 1 ] ; }
else { console . log ( 'Invalid administrator name.' ) ; process . exit ( ) ; return ; }
obj . db . Get ( adminname , function ( err , user ) {
if ( user . length != 1 ) { console . log ( 'Invalid user name.' ) ; process . exit ( ) ; return ; }
2019-06-27 01:13:56 -04:00
user [ 0 ] . siteadmin = 4294967295 ; // 0xFFFFFFFF
2019-02-02 17:54:36 -05:00
obj . db . Set ( user [ 0 ] , function ( ) {
if ( user [ 0 ] . domain == '' ) { console . log ( 'User ' + user [ 0 ] . name + ' set to site administrator.' ) ; } else { console . log ( 'User ' + user [ 0 ] . name + ' of domain ' + user [ 0 ] . domain + ' set to site administrator.' ) ; }
process . exit ( ) ;
2017-08-28 12:27:45 -04:00
return ;
2019-02-02 17:54:36 -05:00
} ) ;
} ) ;
return ;
}
// Remove a site administrator
if ( ( obj . args . unadmin ) && ( typeof obj . args . unadmin == 'string' ) ) {
var adminname = obj . args . unadmin . split ( '/' ) ;
if ( adminname . length == 1 ) { adminname = 'user//' + adminname [ 0 ] ; }
else if ( adminname . length == 2 ) { adminname = 'user/' + adminname [ 0 ] + '/' + adminname [ 1 ] ; }
else { console . log ( 'Invalid administrator name.' ) ; process . exit ( ) ; return ; }
obj . db . Get ( adminname , function ( err , user ) {
if ( user . length != 1 ) { console . log ( 'Invalid user name.' ) ; process . exit ( ) ; return ; }
if ( user [ 0 ] . siteadmin ) { delete user [ 0 ] . siteadmin ; }
obj . db . Set ( user [ 0 ] , function ( ) {
if ( user [ 0 ] . domain == '' ) { console . log ( 'User ' + user [ 0 ] . name + ' is not a site administrator.' ) ; } else { console . log ( 'User ' + user [ 0 ] . name + ' of domain ' + user [ 0 ] . domain + ' is not a site administrator.' ) ; }
process . exit ( ) ;
return ;
} ) ;
} ) ;
return ;
}
2017-08-28 12:27:45 -04:00
2019-02-02 17:54:36 -05:00
// Perform other database cleanup
obj . db . cleanup ( ) ;
// Set all nodes to power state of unknown (0)
2019-02-18 17:32:55 -05:00
obj . db . storePowerEvent ( { time : new Date ( ) , nodeid : '*' , power : 0 , s : 1 } , obj . multiServer ) ; // s:1 indicates that the server is starting up.
2017-08-28 12:27:45 -04:00
2019-02-02 17:54:36 -05:00
// Read or setup database configuration values
obj . db . Get ( 'dbconfig' , function ( err , dbconfig ) {
if ( dbconfig . length == 1 ) { obj . dbconfig = dbconfig [ 0 ] ; } else { obj . dbconfig = { _id : 'dbconfig' , version : 1 } ; }
if ( obj . dbconfig . amtWsEventSecret == null ) { obj . crypto . randomBytes ( 32 , function ( err , buf ) { obj . dbconfig . amtWsEventSecret = buf . toString ( 'hex' ) ; obj . db . Set ( obj . dbconfig ) ; } ) ; }
// This is used by the user to create a username/password for a Intel AMT WSMAN event subscription
if ( obj . args . getwspass ) {
if ( obj . args . getwspass . length == 64 ) {
obj . crypto . randomBytes ( 6 , function ( err , buf ) {
while ( obj . dbconfig . amtWsEventSecret == null ) { process . nextTick ( ) ; }
var username = buf . toString ( 'hex' ) ;
var nodeid = obj . args . getwspass ;
var pass = obj . crypto . createHash ( 'sha384' ) . update ( username . toLowerCase ( ) + ":" + nodeid + ":" + obj . dbconfig . amtWsEventSecret ) . digest ( "base64" ) . substring ( 0 , 12 ) . split ( "/" ) . join ( "x" ) . split ( "\\" ) . join ( "x" ) ;
console . log ( '--- Intel(r) AMT WSMAN eventing credentials ---' ) ;
console . log ( 'Username: ' + username ) ;
console . log ( 'Password: ' + pass ) ;
console . log ( 'Argument: ' + nodeid ) ;
process . exit ( ) ;
} ) ;
2018-01-15 00:01:06 -05:00
} else {
2019-02-02 17:54:36 -05:00
console . log ( 'Invalid NodeID.' ) ;
process . exit ( ) ;
2018-01-15 00:01:06 -05:00
}
2019-02-02 17:54:36 -05:00
return ;
}
// Load the default meshcore and meshcmd
obj . updateMeshCore ( ) ;
obj . updateMeshCmd ( ) ;
// Setup and start the redirection server if needed. We must start the redirection server before Let's Encrypt.
if ( ( obj . args . redirport != null ) && ( typeof obj . args . redirport == 'number' ) && ( obj . args . redirport != 0 ) ) {
obj . redirserver = require ( './redirserver.js' ) . CreateRedirServer ( obj , obj . db , obj . args , obj . StartEx2 ) ;
} else {
obj . StartEx2 ( ) ; // If not needed, move on.
}
2018-01-15 00:01:06 -05:00
} ) ;
2019-02-02 17:54:36 -05:00
}
2018-01-15 00:01:06 -05:00
// Done starting the redirection server, go on to load the server certificates
obj . StartEx2 = function ( ) {
// Load server certificates
2019-02-02 17:54:36 -05:00
obj . certificateOperations = require ( './certoperations.js' ) . CertificateOperations ( obj ) ;
obj . certificateOperations . GetMeshServerCertificate ( obj . args , obj . config , function ( certs ) {
2018-11-12 21:36:20 -05:00
if ( ( obj . config . letsencrypt == null ) || ( obj . redirserver == null ) ) {
2018-01-15 00:01:06 -05:00
obj . StartEx3 ( certs ) ; // Just use the configured certificates
} else {
var le = require ( './letsencrypt.js' ) ;
obj . letsencrypt = le . CreateLetsEncrypt ( obj ) ;
if ( obj . letsencrypt != null ) {
obj . letsencrypt . getCertificate ( certs , obj . StartEx3 ) ; // Use Let's Encrypt certificate
} else {
console . log ( 'ERROR: Unable to setup GreenLock module.' ) ;
obj . StartEx3 ( certs ) ; // Let's Encrypt did not load, just use the configured certificates
}
}
} ) ;
2018-08-30 15:05:23 -04:00
} ;
2018-01-15 00:01:06 -05:00
2018-10-31 19:03:09 -04:00
// Start the server with the given certificates, but check if we have web certificates to load
2018-01-15 00:01:06 -05:00
obj . StartEx3 = function ( certs ) {
2018-10-31 19:03:09 -04:00
var i , webCertLoadCount = 0 ;
2018-01-15 00:01:06 -05:00
obj . certificates = certs ;
obj . certificateOperations . acceleratorStart ( certs ) ; // Set the state of the accelerators
2017-08-28 12:27:45 -04:00
2018-10-31 19:03:09 -04:00
// Load any domain web certificates
for ( i in obj . config . domains ) {
2019-06-12 22:40:27 -04:00
// Load any Intel AMT ACM activation certificates
2019-06-19 20:16:50 -04:00
obj . certificateOperations . loadIntelAmtAcmCerts ( obj . config . domains [ i ] . amtacmactivation ) ;
2019-06-12 22:40:27 -04:00
2018-10-31 19:03:09 -04:00
if ( obj . config . domains [ i ] . certurl != null ) {
2018-11-02 21:13:32 -04:00
// Fix the URL and add 'https://' if needed
if ( obj . config . domains [ i ] . certurl . indexOf ( '://' ) < 0 ) { obj . config . domains [ i ] . certurl = 'https://' + obj . config . domains [ i ] . certurl ; }
2018-10-31 19:03:09 -04:00
// Load web certs
webCertLoadCount ++ ;
obj . certificateOperations . loadCertificate ( obj . config . domains [ i ] . certurl , obj . config . domains [ i ] , function ( url , cert , xdomain ) {
if ( cert != null ) {
2018-11-30 19:42:58 -05:00
// Hash the entire cert
2018-12-04 21:31:33 -05:00
var hash = obj . crypto . createHash ( 'sha384' ) . update ( Buffer . from ( cert , 'binary' ) ) . digest ( 'hex' ) ;
2019-06-19 20:16:50 -04:00
if ( xdomain . certhash != hash ) { xdomain . certkeyhash = hash ; xdomain . certhash = hash ; }
2018-11-30 19:42:58 -05:00
try {
// Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
var forgeCert = obj . certificateOperations . forge . pki . certificateFromAsn1 ( obj . certificateOperations . forge . asn1 . fromDer ( cert ) ) ;
xdomain . certkeyhash = obj . certificateOperations . forge . pki . getPublicKeyFingerprint ( forgeCert . publicKey , { md : obj . certificateOperations . forge . md . sha384 . create ( ) , encoding : 'hex' } ) ;
2018-12-04 21:31:33 -05:00
//console.log('V1: ' + xdomain.certkeyhash);
2018-11-30 19:42:58 -05:00
} catch ( ex ) { }
console . log ( 'Loaded web certificate from ' + url ) ;
console . log ( ' SHA384 cert hash: ' + xdomain . certhash ) ;
if ( xdomain . certhash != xdomain . certkeyhash ) { console . log ( ' SHA384 key hash: ' + xdomain . certkeyhash ) ; }
2018-10-31 19:03:09 -04:00
} else {
console . log ( 'Failed to load web certificate at: ' + url ) ;
}
webCertLoadCount -- ;
if ( webCertLoadCount == 0 ) { obj . StartEx4 ( ) ; } // Done loading all certificates
} ) ;
}
}
// No certificate to load, start the server
if ( webCertLoadCount == 0 ) { obj . StartEx4 ( ) ; }
}
// Start the server with the given certificates
obj . StartEx4 = function ( ) {
var i ;
2018-01-15 00:01:06 -05:00
// If the certificate is un-configured, force LAN-only mode
2019-03-05 02:48:45 -05:00
if ( obj . certificates . CommonName . indexOf ( '.' ) == - 1 ) { /*console.log('Server name not configured, running in LAN-only mode.');*/ obj . args . lanonly = true ; }
2018-11-12 21:36:20 -05:00
// Write server version and run mode
var productionMode = ( process . env . NODE _ENV && ( process . env . NODE _ENV == 'production' ) ) ;
var runmode = ( obj . args . lanonly ? 2 : ( obj . args . wanonly ? 1 : 0 ) ) ;
console . log ( 'MeshCentral v' + obj . currentVer + ', ' + ( [ 'Hybrid (LAN + WAN) mode' , 'WAN mode' , 'LAN mode' ] [ runmode ] ) + ( productionMode ? ', Production mode.' : '.' ) ) ;
2017-08-28 12:27:45 -04:00
2018-01-15 00:01:06 -05:00
// Check that no sub-domains have the same DNS as the parent
2018-08-30 15:05:23 -04:00
for ( i in obj . config . domains ) {
2018-01-15 00:01:06 -05:00
if ( ( obj . config . domains [ i ] . dns != null ) && ( obj . certificates . CommonName . toLowerCase ( ) === obj . config . domains [ i ] . dns . toLowerCase ( ) ) ) {
console . log ( "ERROR: Server sub-domain can't have same DNS name as the parent." ) ; process . exit ( 0 ) ; return ;
}
}
2018-01-02 19:52:49 -05:00
2018-01-15 00:01:06 -05:00
// Load the list of mesh agents and install scripts
2018-08-30 15:05:23 -04:00
if ( obj . args . noagentupdate == 1 ) { for ( i in obj . meshAgentsArchitectureNumbers ) { obj . meshAgentsArchitectureNumbers [ i ] . update = false ; } }
2018-01-15 00:01:06 -05:00
obj . updateMeshAgentsTable ( function ( ) {
obj . updateMeshAgentInstallScripts ( ) ;
// Setup and start the web server
2019-01-24 20:01:50 -05:00
obj . crypto . randomBytes ( 48 , function ( err , buf ) {
2018-01-15 00:01:06 -05:00
// Setup Mesh Multi-Server if needed
obj . multiServer = require ( './multiserver.js' ) . CreateMultiServer ( obj , obj . args ) ;
if ( obj . multiServer != null ) {
2019-08-12 17:58:06 -04:00
if ( ( obj . db . databaseType != 3 ) || ( obj . db . changeStream != true ) ) { console . log ( "ERROR: Multi-server support requires use of MongoDB with ReplicaSet and ChangeStream enabled." ) ; process . exit ( 0 ) ; return ; }
2018-01-15 00:01:06 -05:00
obj . serverId = obj . multiServer . serverid ;
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 == true ) && ( obj . args . userallowedip == null ) ) { obj . args . userallowedip = "::1,127.0.0.1" ; }
2018-08-22 19:18:01 -04:00
// Set the session length to 60 minutes if not set and set a random key if needed
2018-08-27 15:24:15 -04:00
if ( ( obj . args . sessiontime != null ) && ( ( typeof obj . args . sessiontime != 'number' ) || ( obj . args . sessiontime < 1 ) ) ) { delete obj . args . sessiontime ; }
2018-08-22 19:18:01 -04:00
if ( ! obj . args . sessionkey ) { obj . args . sessionkey = buf . toString ( 'hex' ) . toUpperCase ( ) ; }
2019-08-29 17:38:13 -04:00
// Create APF server to hook into webserver
obj . apfserver = require ( './apfserver.js' ) . CreateApfServer ( obj , obj . db , obj . args ) ;
2018-10-31 19:03:09 -04:00
// Start the web server and if needed, the redirection web server.
2018-08-22 19:18:01 -04:00
obj . webserver = require ( './webserver.js' ) . CreateWebServer ( obj , obj . db , obj . args , obj . certificates ) ;
2018-01-15 00:01:06 -05:00
if ( obj . redirserver != null ) { obj . redirserver . hookMainWebServer ( obj . certificates ) ; }
// Setup the Intel AMT event handler
obj . amtEventHandler = require ( './amtevents.js' ) . CreateAmtEventsHandler ( obj ) ;
// Setup the Intel AMT local network scanner
if ( obj . args . wanonly != true ) {
obj . amtScanner = require ( './amtscanner.js' ) . CreateAmtScanner ( obj ) . start ( ) ;
obj . meshScanner = require ( './meshscanner.js' ) . CreateMeshScanner ( obj ) . start ( ) ;
}
// Setup and start the MPS server
2018-01-25 19:12:53 -05:00
if ( ( obj . args . lanonly != true ) && ( obj . args . mpsport !== 0 ) ) {
2018-01-15 00:01:06 -05:00
obj . mpsserver = require ( './mpsserver.js' ) . CreateMpsServer ( obj , obj . db , obj . args , obj . certificates ) ;
}
// Setup and start the legacy swarm server
2018-01-25 19:12:53 -05:00
if ( ( obj . certificates . swarmserver != null ) && ( obj . args . swarmport !== 0 ) ) {
2018-01-15 00:01:06 -05:00
if ( obj . args . swarmport == null ) { obj . args . swarmport = 8080 ; }
obj . swarmserver = require ( './swarmserver.js' ) . CreateSwarmServer ( obj , obj . db , obj . args , obj . certificates ) ;
}
// Setup email server
if ( ( obj . config . smtp != null ) && ( obj . config . smtp . host != null ) && ( obj . config . smtp . from != null ) ) {
2018-09-05 20:40:00 -04:00
obj . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj ) ;
2018-01-15 00:01:06 -05:00
obj . mailserver . verify ( ) ;
2019-06-07 18:55:24 -04:00
if ( obj . args . lanonly == true ) { console . log ( "WARNING: SMTP server has limited use in LAN mode." ) ; }
2018-01-15 00:01:06 -05:00
}
// Start periodic maintenance
obj . maintenanceTimer = setInterval ( obj . maintenanceActions , 1000 * 60 * 60 ) ; // Run this every hour
// Dispatch an event that the server is now running
2018-08-30 15:05:23 -04:00
obj . DispatchEvent ( [ '*' ] , obj , { etype : 'server' , action : 'started' , msg : 'Server started' } ) ;
2018-01-15 00:01:06 -05:00
// Load the login cookie encryption key from the database if allowed
if ( ( obj . config ) && ( obj . config . settings ) && ( obj . config . settings . allowlogintoken == true ) ) {
obj . db . Get ( 'LoginCookieEncryptionKey' , function ( err , docs ) {
2018-12-29 14:38:05 -05:00
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
2018-01-15 00:01:06 -05:00
obj . loginCookieEncryptionKey = Buffer . from ( docs [ 0 ] . key , 'hex' ) ;
} else {
obj . loginCookieEncryptionKey = obj . generateCookieKey ( ) ; obj . db . Set ( { _id : 'LoginCookieEncryptionKey' , key : obj . loginCookieEncryptionKey . toString ( 'hex' ) , time : Date . now ( ) } ) ;
}
2017-08-28 12:27:45 -04:00
} ) ;
2018-01-15 00:01:06 -05:00
}
2019-06-07 20:11:56 -04:00
// Load the invitation link encryption key from the database
obj . db . Get ( 'InvitationLinkEncryptionKey' , function ( err , docs ) {
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
obj . invitationLinkEncryptionKey = Buffer . from ( docs [ 0 ] . key , 'hex' ) ;
} else {
obj . invitationLinkEncryptionKey = obj . generateCookieKey ( ) ; obj . db . Set ( { _id : 'InvitationLinkEncryptionKey' , key : obj . invitationLinkEncryptionKey . toString ( 'hex' ) , time : Date . now ( ) } ) ;
}
} ) ;
2019-03-25 22:59:04 -04:00
// Start collecting server stats every 5 minutes
setInterval ( function ( ) {
2019-03-26 17:11:51 -04:00
obj . serverStatsCounter ++ ;
var hours = 720 ; // Start with all events lasting 30 days.
if ( ( ( obj . serverStatsCounter ) % 2 ) == 1 ) { hours = 3 ; } // Half of the event get removed after 3 hours.
2019-03-26 17:30:40 -04:00
else if ( ( Math . floor ( obj . serverStatsCounter / 2 ) % 2 ) == 1 ) { hours = 8 ; } // Another half of the event get removed after 8 hours.
else if ( ( Math . floor ( obj . serverStatsCounter / 4 ) % 2 ) == 1 ) { hours = 24 ; } // Another half of the event get removed after 24 hours.
else if ( ( Math . floor ( obj . serverStatsCounter / 8 ) % 2 ) == 1 ) { hours = 48 ; } // Another half of the event get removed after 48 hours.
else if ( ( Math . floor ( obj . serverStatsCounter / 16 ) % 2 ) == 1 ) { hours = 72 ; } // Another half of the event get removed after 72 hours.
2019-03-26 17:11:51 -04:00
var expire = new Date ( ) ;
2019-03-26 17:30:40 -04:00
expire . setTime ( expire . getTime ( ) + ( 60 * 60 * 1000 * hours ) ) ;
2019-03-26 17:11:51 -04:00
2019-03-26 04:16:11 -04:00
var data = {
time : new Date ( ) ,
2019-03-26 17:11:51 -04:00
expire : expire ,
2019-03-26 04:16:11 -04:00
mem : process . memoryUsage ( ) ,
//cpu: process.cpuUsage(),
conn : {
ca : Object . keys ( obj . webserver . wsagents ) . length ,
cu : Object . keys ( obj . webserver . wssessions ) . length ,
us : Object . keys ( obj . webserver . wssessions2 ) . length ,
rs : obj . webserver . relaySessionCount
}
} ;
if ( obj . mpsserver != null ) { data . conn . am = Object . keys ( obj . mpsserver . ciraConnections ) . length ; }
obj . db . SetServerStats ( data ) ; // Save the stats to the database
obj . DispatchEvent ( [ '*' ] , obj , { action : 'servertimelinestats' , data : data } ) ; // Event the server stats
2019-03-25 22:59:04 -04:00
} , 300000 ) ;
2019-08-22 18:31:39 -04:00
obj . debug ( 'main' , 'Server started' ) ;
2018-03-14 18:16:55 -04:00
if ( obj . args . nousers == true ) { obj . updateServerState ( 'nousers' , '1' ) ; }
2018-03-14 15:10:13 -04:00
obj . updateServerState ( 'state' , 'running' ) ;
2019-05-17 15:40:15 -04:00
2019-05-20 19:00:33 -04:00
// Setup auto-backup defaults
2019-05-28 20:42:11 -04:00
if ( obj . config . settings . autobackup == null ) { obj . config . settings . autobackup = { backupintervalhours : 24 , keeplastdaysbackup : 10 } ; }
2019-05-20 19:00:33 -04:00
else if ( obj . config . settings . autobackup === false ) { delete obj . config . settings . autobackup ; }
// Setup auto-backup timer
2019-05-28 20:42:11 -04:00
if ( obj . config . settings . autobackup && ( typeof obj . config . settings . autobackup . backupintervalhours == 'number' ) ) {
setInterval ( obj . db . performBackup , obj . config . settings . autobackup . backupintervalhours * 60 * 60 * 1000 ) ;
2019-05-17 15:40:15 -04:00
}
2017-08-28 12:27:45 -04:00
} ) ;
} ) ;
2018-08-30 15:05:23 -04:00
} ;
2017-09-06 21:10:24 -04:00
// Perform maintenance operations (called every hour)
obj . maintenanceActions = function ( ) {
2019-01-27 15:23:38 -05:00
// Check for self-update that targets a specific version
if ( ( typeof obj . args . selfupdate == 'string' ) && ( obj . currentVer === obj . args . selfupdate ) ) { obj . args . selfupdate = false ; }
2017-09-06 21:10:24 -04:00
// Check if we need to perform server self-update
2019-01-27 15:23:38 -05:00
if ( ( obj . args . selfupdate ) && ( obj . serverSelfWriteAllowed == true ) ) {
2017-09-06 21:10:24 -04:00
obj . db . getValueOfTheDay ( 'performSelfUpdate' , 1 , function ( performSelfUpdate ) {
if ( performSelfUpdate . value > 0 ) {
performSelfUpdate . value -- ;
obj . db . Set ( performSelfUpdate ) ;
obj . getLatestServerVersion ( function ( currentVer , latestVer ) { if ( currentVer != latestVer ) { obj . performServerUpdate ( ) ; return ; } } ) ;
}
} ) ;
}
2018-08-30 15:05:23 -04:00
} ;
2017-09-06 21:10:24 -04:00
2017-08-28 12:27:45 -04:00
// Stop the Meshcentral server
obj . Stop = function ( restoreFile ) {
// If the database is not setup, exit now.
if ( ! obj . db ) return ;
// Dispatch an event saying the server is now stopping
2018-08-30 15:05:23 -04:00
obj . DispatchEvent ( [ '*' ] , obj , { etype : 'server' , action : 'stopped' , msg : 'Server stopped' } ) ;
2017-08-28 12:27:45 -04:00
// Set all nodes to power state of unknown (0)
2019-02-18 17:32:55 -05:00
obj . db . storePowerEvent ( { time : new Date ( ) , nodeid : '*' , power : 0 , s : 2 } , obj . multiServer , function ( ) { // s:2 indicates that the server is shutting down.
2017-08-28 12:27:45 -04:00
if ( restoreFile ) {
2019-08-22 18:31:39 -04:00
obj . debug ( 'main' , 'Server stopped, updating settings: ' + restoreFile ) ;
2017-08-28 12:27:45 -04:00
console . log ( 'Updating settings folder...' ) ;
2018-05-02 19:19:45 -04:00
var yauzl = require ( "yauzl" ) ;
yauzl . open ( restoreFile , { lazyEntries : true } , function ( err , zipfile ) {
if ( err ) throw err ;
zipfile . readEntry ( ) ;
zipfile . on ( "entry" , function ( entry ) {
if ( /\/$/ . test ( entry . fileName ) ) {
// Directory file names end with '/'.
// Note that entires for directories themselves are optional.
// An entry's fileName implicitly requires its parent directories to exist.
zipfile . readEntry ( ) ;
} else {
// file entry
zipfile . openReadStream ( entry , function ( err , readStream ) {
if ( err ) throw err ;
readStream . on ( "end" , function ( ) { zipfile . readEntry ( ) ; } ) ;
2018-07-13 22:18:43 -04:00
// console.log('Extracting:', obj.getConfigFilePath(entry.fileName));
readStream . pipe ( obj . fs . createWriteStream ( obj . getConfigFilePath ( entry . fileName ) ) ) ;
2018-05-02 19:19:45 -04:00
} ) ;
}
} ) ;
2018-08-30 15:05:23 -04:00
zipfile . on ( "end" , function ( ) { setTimeout ( function ( ) { obj . fs . unlinkSync ( restoreFile ) ; process . exit ( 123 ) ; } ) ; } ) ;
2018-05-02 19:19:45 -04:00
} ) ;
2017-08-28 12:27:45 -04:00
} else {
2019-08-22 18:31:39 -04:00
obj . debug ( 'main' , 'Server stopped' ) ;
2017-08-28 12:27:45 -04:00
process . exit ( 0 ) ;
}
} ) ;
2018-03-14 15:10:13 -04:00
// Update the server state
obj . updateServerState ( 'state' , 'stopped' ) ;
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Event Dispatch
obj . AddEventDispatch = function ( ids , target ) {
2019-08-22 18:31:39 -04:00
obj . debug ( 'dispatch' , 'AddEventDispatch' , ids ) ;
2017-08-28 12:27:45 -04:00
for ( var i in ids ) { var id = ids [ i ] ; if ( ! obj . eventsDispatch [ id ] ) { obj . eventsDispatch [ id ] = [ target ] ; } else { obj . eventsDispatch [ id ] . push ( target ) ; } }
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
obj . RemoveEventDispatch = function ( ids , target ) {
2019-08-22 18:31:39 -04:00
obj . debug ( 'dispatch' , 'RemoveEventDispatch' , id ) ;
2019-03-09 17:28:08 -05:00
for ( var i in ids ) { var id = ids [ i ] ; if ( obj . eventsDispatch [ id ] ) { var j = obj . eventsDispatch [ id ] . indexOf ( target ) ; if ( j >= 0 ) { if ( obj . eventsDispatch [ id ] . length == 1 ) { delete obj . eventsDispatch [ id ] ; } else { obj . eventsDispatch [ id ] . splice ( j , 1 ) ; } } } }
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
obj . RemoveEventDispatchId = function ( id ) {
2019-08-22 18:31:39 -04:00
obj . debug ( 'dispatch' , 'RemoveEventDispatchId' , id ) ;
2017-09-15 14:45:06 -04:00
if ( obj . eventsDispatch [ id ] != null ) { delete obj . eventsDispatch [ id ] ; }
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
obj . RemoveAllEventDispatch = function ( target ) {
2019-08-22 18:31:39 -04:00
obj . debug ( 'dispatch' , 'RemoveAllEventDispatch' ) ;
2019-03-09 17:28:08 -05:00
for ( var i in obj . eventsDispatch ) { var j = obj . eventsDispatch [ i ] . indexOf ( target ) ; if ( j >= 0 ) { if ( obj . eventsDispatch [ i ] . length == 1 ) { delete obj . eventsDispatch [ i ] ; } else { obj . eventsDispatch [ i ] . splice ( j , 1 ) ; } } }
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
obj . DispatchEvent = function ( ids , source , event , fromPeerServer ) {
// If the database is not setup, exit now.
if ( ! obj . db ) return ;
2019-08-22 18:31:39 -04:00
obj . debug ( 'dispatch' , 'DispatchEvent' , ids ) ;
2019-02-18 17:32:55 -05:00
if ( ( typeof event == 'object' ) && ( ! event . nolog ) ) {
event . time = new Date ( ) ;
// The event we store is going to skip some of the fields so we don't store too much stuff in the database.
var storeEvent = { } ;
for ( var i in event ) { if ( i != 'node' ) { storeEvent [ i ] = event [ i ] ; } } // Skip the "node" field. May skip more in the future.
storeEvent . ids = ids ;
obj . db . StoreEvent ( storeEvent ) ;
2017-12-14 17:57:52 -05:00
}
2017-08-28 12:27:45 -04:00
var targets = [ ] ; // List of targets we dispatched the event to, we don't want to dispatch to the same target twice.
for ( var j in ids ) {
var id = ids [ j ] ;
if ( obj . eventsDispatch [ id ] ) {
for ( var i in obj . eventsDispatch [ id ] ) {
if ( targets . indexOf ( obj . eventsDispatch [ id ] [ i ] ) == - 1 ) { // Check if we already displatched to this target
targets . push ( obj . eventsDispatch [ id ] [ i ] ) ;
2019-09-19 14:21:35 -04:00
try { obj . eventsDispatch [ id ] [ i ] . HandleEvent ( source , event , ids , id ) ; } catch ( ex ) {
2019-06-13 19:39:21 -04:00
console . log ( ex , obj . eventsDispatch [ id ] [ i ] ) ;
}
2017-08-28 12:27:45 -04:00
}
}
}
}
2017-12-14 17:57:52 -05:00
if ( ( fromPeerServer == null ) && ( obj . multiServer != null ) && ( ( typeof event != 'object' ) || ( event . nopeers != 1 ) ) ) { obj . multiServer . DispatchEvent ( ids , source , event ) ; }
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Get the connection state of a node
2018-08-30 15:05:23 -04:00
obj . GetConnectivityState = function ( nodeid ) { return obj . connectivityByNode [ nodeid ] ; } ;
2017-09-13 14:25:57 -04:00
2017-09-15 14:45:06 -04:00
// Get the routing server id for a given node and connection type, can never be self.
obj . GetRoutingServerId = function ( nodeid , connectType ) {
if ( obj . multiServer == null ) return null ;
2018-08-30 15:05:23 -04:00
for ( var serverid in obj . peerConnectivityByNode ) {
2017-09-15 14:45:06 -04:00
if ( serverid == obj . serverId ) continue ;
var state = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
if ( ( state != null ) && ( ( state . connectivity & connectType ) != 0 ) ) { return { serverid : serverid , meshid : state . meshid } ; }
}
return null ;
2018-08-30 15:05:23 -04:00
} ;
2017-09-15 14:45:06 -04:00
2017-09-13 14:25:57 -04:00
// Update the connection state of a node when in multi-server mode
// Update obj.connectivityByNode using obj.peerConnectivityByNode for the list of nodes in argument
obj . UpdateConnectivityState = function ( nodeids ) {
for ( var nodeid in nodeids ) {
var meshid = null , state = null , oldConnectivity = 0 , oldPowerState = 0 , newConnectivity = 0 , newPowerState = 0 ;
var oldState = obj . connectivityByNode [ nodeid ] ;
2018-08-30 15:05:23 -04:00
if ( oldState != null ) { meshid = oldState . meshid ; oldConnectivity = oldState . connectivity ; oldPowerState = oldState . powerState ; }
for ( var serverid in obj . peerConnectivityByNode ) {
2017-09-13 14:25:57 -04:00
var peerState = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
if ( peerState != null ) {
if ( state == null ) {
// Copy the state
state = { } ;
newConnectivity = state . connectivity = peerState . connectivity ;
newPowerState = state . powerState = peerState . powerState ;
meshid = state . meshid = peerState . meshid ;
//if (peerState.agentPower) { state.agentPower = peerState.agentPower; }
//if (peerState.ciraPower) { state.ciraPower = peerState.ciraPower; }
//if (peerState.amtPower) { state.amtPower = peerState.amtPower; }
} else {
// Merge the state
state . connectivity |= peerState . connectivity ;
newConnectivity = state . connectivity ;
if ( ( peerState . powerState != 0 ) && ( ( state . powerState == 0 ) || ( peerState . powerState < state . powerState ) ) ) { newPowerState = state . powerState = peerState . powerState ; }
meshid = state . meshid = peerState . meshid ;
//if (peerState.agentPower) { state.agentPower = peerState.agentPower; }
//if (peerState.ciraPower) { state.ciraPower = peerState.ciraPower; }
//if (peerState.amtPower) { state.amtPower = peerState.amtPower; }
}
}
}
obj . connectivityByNode [ nodeid ] = state ;
//console.log('xx', nodeid, meshid, newConnectivity, oldPowerState, newPowerState, oldPowerState);
// Event any changes on this server only
if ( ( newConnectivity != oldPowerState ) || ( newPowerState != oldPowerState ) ) {
obj . DispatchEvent ( [ '*' , meshid ] , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , conn : newConnectivity , pwr : newPowerState , nolog : 1 , nopeers : 1 } ) ;
}
}
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Set the connectivity state of a node and setup the server so that messages can be routed correctly.
// meshId: mesh identifier of format mesh/domain/meshidhex
// nodeId: node identifier of format node/domain/nodeidhex
// connectTime: time of connection, milliseconds elapsed since the UNIX epoch.
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local.
// powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present
2018-08-30 15:05:23 -04:00
//var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local'];
//var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present'];
2017-09-13 14:25:57 -04:00
obj . SetConnectivityState = function ( meshid , nodeid , connectTime , connectType , powerState , serverid ) {
//console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid)));
if ( ( serverid == null ) && ( obj . multiServer != null ) ) { obj . multiServer . DispatchMessage ( { action : 'SetConnectivityState' , meshid : meshid , nodeid : nodeid , connectTime : connectTime , connectType : connectType , powerState : powerState } ) ; }
if ( obj . multiServer == null ) {
// Single server mode
// Change the node connection state
var eventConnectChange = 0 ;
var state = obj . connectivityByNode [ nodeid ] ;
if ( state ) {
// Change the connection in the node and mesh state lists
if ( ( state . connectivity & connectType ) == 0 ) { state . connectivity |= connectType ; eventConnectChange = 1 ; }
state . meshid = meshid ;
} else {
// Add the connection to the node and mesh state list
obj . connectivityByNode [ nodeid ] = state = { connectivity : connectType , meshid : meshid } ;
eventConnectChange = 1 ;
}
// Set node power state
if ( connectType == 1 ) { state . agentPower = powerState ; } else if ( connectType == 2 ) { state . ciraPower = powerState ; } else if ( connectType == 4 ) { state . amtPower = powerState ; }
var powerState = 0 , oldPowerState = state . powerState ;
if ( ( state . connectivity & 1 ) != 0 ) { powerState = state . agentPower ; } else if ( ( state . connectivity & 2 ) != 0 ) { powerState = state . ciraPower ; } else if ( ( state . connectivity & 4 ) != 0 ) { powerState = state . amtPower ; }
2017-09-15 14:45:06 -04:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) {
2017-09-13 14:25:57 -04:00
state . powerState = powerState ;
2017-08-28 12:27:45 -04:00
eventConnectChange = 1 ;
2017-09-13 14:25:57 -04:00
// Set new power state in database
2019-02-18 17:32:55 -05:00
var record = { time : new Date ( connectTime ) , nodeid : nodeid , power : powerState } ;
if ( oldPowerState != null ) { record . oldPower = oldPowerState ; }
obj . db . storePowerEvent ( record , obj . multiServer ) ;
2017-08-28 12:27:45 -04:00
}
2017-09-13 14:25:57 -04:00
// Event the node connection change
if ( eventConnectChange == 1 ) { obj . DispatchEvent ( [ '*' , meshid ] , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , conn : state . connectivity , pwr : state . powerState , ct : connectTime , nolog : 1 , nopeers : 1 } ) ; }
2017-08-28 12:27:45 -04:00
} else {
2017-09-13 14:25:57 -04:00
// Multi server mode
// Change the node connection state
if ( serverid == null ) { serverid = obj . serverId ; }
if ( obj . peerConnectivityByNode [ serverid ] == null ) return ; // Guard against unknown serverid's
var state = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
if ( state ) {
// Change the connection in the node and mesh state lists
if ( ( state . connectivity & connectType ) == 0 ) { state . connectivity |= connectType ; }
state . meshid = meshid ;
} else {
// Add the connection to the node and mesh state list
obj . peerConnectivityByNode [ serverid ] [ nodeid ] = state = { connectivity : connectType , meshid : meshid } ;
}
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Set node power state
if ( connectType == 1 ) { state . agentPower = powerState ; } else if ( connectType == 2 ) { state . ciraPower = powerState ; } else if ( connectType == 4 ) { state . amtPower = powerState ; }
2018-08-30 15:05:23 -04:00
var powerState = 0 , oldPowerState = state . powerState ;
2017-09-13 14:25:57 -04:00
if ( ( state . connectivity & 1 ) != 0 ) { powerState = state . agentPower ; } else if ( ( state . connectivity & 2 ) != 0 ) { powerState = state . ciraPower ; } else if ( ( state . connectivity & 4 ) != 0 ) { powerState = state . amtPower ; }
2018-01-02 19:52:49 -05:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) {
state . powerState = powerState ;
// Set new power state in database
2019-02-18 17:32:55 -05:00
var record = { time : new Date ( connectTime ) , nodeid : nodeid , power : powerState , server : obj . multiServer . serverid } ;
if ( oldPowerState != null ) { record . oldPower = oldPowerState ; }
obj . db . storePowerEvent ( record , obj . multiServer ) ;
2018-01-02 19:52:49 -05:00
}
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Update the combined node state
var x = { } ; x [ nodeid ] = 1 ;
obj . UpdateConnectivityState ( x ) ;
2017-08-28 12:27:45 -04:00
}
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Clear the connectivity state of a node and setup the server so that messages can be routed correctly.
// meshId: mesh identifier of format mesh/domain/meshidhex
// nodeId: node identifier of format node/domain/nodeidhex
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 3 = Intel AMT local.
2017-09-13 14:25:57 -04:00
obj . ClearConnectivityState = function ( meshid , nodeid , connectType , serverid ) {
//console.log('ClearConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + (serverid == null?(''):(', ServerId: ' + serverid)));
if ( ( serverid == null ) && ( obj . multiServer != null ) ) { obj . multiServer . DispatchMessage ( { action : 'ClearConnectivityState' , meshid : meshid , nodeid : nodeid , connectType : connectType } ) ; }
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
if ( obj . multiServer == null ) {
// Single server mode
2018-08-27 15:24:15 -04:00
var eventConnectChange = 0 ;
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Remove the agent connection from the nodes connection list
var state = obj . connectivityByNode [ nodeid ] ;
2017-09-15 14:45:06 -04:00
if ( state == null ) return ;
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
if ( ( state . connectivity & connectType ) != 0 ) {
state . connectivity -= connectType ;
// If the node is completely disconnected, clean it up completely
if ( state . connectivity == 0 ) { delete obj . connectivityByNode [ nodeid ] ; state . powerState = 0 ; }
eventConnectChange = 1 ;
2017-08-28 12:27:45 -04:00
}
2017-09-13 14:25:57 -04:00
// Clear node power state
if ( connectType == 1 ) { state . agentPower = 0 ; } else if ( connectType == 2 ) { state . ciraPower = 0 ; } else if ( connectType == 4 ) { state . amtPower = 0 ; }
var powerState = 0 , oldPowerState = state . powerState ;
if ( ( state . connectivity & 1 ) != 0 ) { powerState = state . agentPower ; } else if ( ( state . connectivity & 2 ) != 0 ) { powerState = state . ciraPower ; } else if ( ( state . connectivity & 4 ) != 0 ) { powerState = state . amtPower ; }
2017-09-15 14:45:06 -04:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) {
2017-09-13 14:25:57 -04:00
state . powerState = powerState ;
eventConnectChange = 1 ;
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Set new power state in database
2019-02-18 17:32:55 -05:00
obj . db . storePowerEvent ( { time : new Date ( ) , nodeid : nodeid , power : powerState , oldPower : oldPowerState } , obj . multiServer ) ;
2017-09-13 14:25:57 -04:00
}
2017-08-28 12:27:45 -04:00
2017-09-13 14:25:57 -04:00
// Event the node connection change
if ( eventConnectChange == 1 ) { obj . DispatchEvent ( [ '*' , meshid ] , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , conn : state . connectivity , pwr : state . powerState , nolog : 1 , nopeers : 1 } ) ; }
} else {
// Multi server mode
// Remove the agent connection from the nodes connection list
if ( serverid == null ) { serverid = obj . serverId ; }
if ( obj . peerConnectivityByNode [ serverid ] == null ) return ; // Guard against unknown serverid's
var state = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
2017-09-15 14:45:06 -04:00
if ( state == null ) return ;
2017-09-13 14:25:57 -04:00
// If existing state exist, remove this connection
if ( ( state . connectivity & connectType ) != 0 ) {
state . connectivity -= connectType ; // Remove one connectivity mode
// If the node is completely disconnected, clean it up completely
if ( state . connectivity == 0 ) { delete obj . peerConnectivityByNode [ serverid ] [ nodeid ] ; state . powerState = 0 ; }
}
// Clear node power state
if ( connectType == 1 ) { state . agentPower = 0 ; } else if ( connectType == 2 ) { state . ciraPower = 0 ; } else if ( connectType == 4 ) { state . amtPower = 0 ; }
var powerState = 0 ;
if ( ( state . connectivity & 1 ) != 0 ) { powerState = state . agentPower ; } else if ( ( state . connectivity & 2 ) != 0 ) { powerState = state . ciraPower ; } else if ( ( state . connectivity & 4 ) != 0 ) { powerState = state . amtPower ; }
2017-09-15 14:45:06 -04:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) { state . powerState = powerState ; }
2017-09-13 14:25:57 -04:00
// Update the combined node state
var x = { } ; x [ nodeid ] = 1 ;
obj . UpdateConnectivityState ( x ) ;
}
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
2018-12-29 18:24:33 -05:00
// Escape a code string
obj . escapeCodeString = function ( str ) {
const escapeCodeStringTable = { '\'' : '\\\'' , '\"' : '\\"' , '\\' : '\\\\' , '\b' : '\\b' , '\f' : '\\f' , '\n' : '\\n' , '\r' : '\\r' , '\t' : '\\t' } ;
var r = '' , c , cr , table ;
for ( var i = 0 ; i < str . length ; i ++ ) {
c = str [ i ] ;
table = escapeCodeStringTable [ c ] ;
if ( table != null ) {
r += table ;
} else {
cr = c . charCodeAt ( 0 ) ;
if ( ( cr >= 32 ) && ( cr <= 127 ) ) { r += c ; }
}
}
return r ;
}
2017-08-28 12:27:45 -04:00
// Update the default mesh core
obj . updateMeshCore = function ( func ) {
2017-10-03 21:31:20 -04:00
// Figure out where meshcore.js is
var meshcorePath = obj . datapath ;
if ( obj . fs . existsSync ( obj . path . join ( meshcorePath , 'meshcore.js' ) ) == false ) {
meshcorePath = obj . path . join ( _ _dirname , 'agents' ) ;
if ( obj . fs . existsSync ( obj . path . join ( meshcorePath , 'meshcore.js' ) ) == false ) {
2018-12-29 18:24:33 -05:00
obj . defaultMeshCores = obj . defaultMeshCoresHash = { } ; if ( func != null ) { func ( false ) ; } // meshcore.js not found
2017-10-03 21:31:20 -04:00
}
}
// Read meshcore.js and all .js files in the modules folder.
2018-12-29 18:24:33 -05:00
var meshCore = null , modulesDir = null ;
const modulesAdd = {
2019-03-09 17:28:08 -05:00
'windows-amt' : [ 'var addedModules = [];\r\n' ] ,
'linux-amt' : [ 'var addedModules = [];\r\n' ] ,
'linux-noamt' : [ 'var addedModules = [];\r\n' ]
2018-12-29 18:24:33 -05:00
} ;
2019-01-21 17:05:50 -05:00
// Read the recovery core if present
var meshRecoveryCore = null ;
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'agents' , 'recoverycore.js' ) ) == true ) {
try { meshRecoveryCore = obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'agents' , 'recoverycore.js' ) ) . toString ( ) ; } catch ( ex ) { }
if ( meshRecoveryCore != null ) {
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-recovery' ] = [ 'var addedModules = [];\r\n' ] ;
modulesAdd [ 'linux-recovery' ] = [ 'var addedModules = [];\r\n' ] ;
2019-01-21 17:05:50 -05:00
}
}
2019-02-25 17:35:08 -05:00
// Read the agent recovery core if present
var meshAgentRecoveryCore = null ;
2019-04-12 20:43:20 -04:00
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'agents' , 'meshcore_diagnostic.js' ) ) == true ) {
try { meshAgentRecoveryCore = obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'agents' , 'meshcore_diagnostic.js' ) ) . toString ( ) ; } catch ( ex ) { }
2019-02-25 17:35:08 -05:00
if ( meshAgentRecoveryCore != null ) {
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-agentrecovery' ] = [ 'var addedModules = [];\r\n' ] ;
modulesAdd [ 'linux-agentrecovery' ] = [ 'var addedModules = [];\r\n' ] ;
2019-02-25 17:35:08 -05:00
}
}
2019-01-03 17:46:52 -05:00
if ( obj . args . minifycore !== false ) { try { meshCore = obj . fs . readFileSync ( obj . path . join ( meshcorePath , 'meshcore.min.js' ) ) . toString ( ) ; } catch ( e ) { } } // Favor minified meshcore if present.
2018-12-29 18:24:33 -05:00
if ( meshCore == null ) { try { meshCore = obj . fs . readFileSync ( obj . path . join ( meshcorePath , 'meshcore.js' ) ) . toString ( ) ; } catch ( e ) { } } // Use non-minified meshcore.
2018-04-12 14:15:01 -04:00
if ( meshCore != null ) {
2018-12-29 18:24:33 -05:00
var moduleDirPath = null ;
2019-01-03 17:46:52 -05:00
if ( obj . args . minifycore !== false ) { try { moduleDirPath = obj . path . join ( meshcorePath , 'modules_meshcore_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } // Favor minified modules if present.
2018-12-29 18:24:33 -05:00
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( meshcorePath , 'modules_meshcore' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } // Use non-minified mofules.
2018-04-12 14:15:01 -04:00
if ( modulesDir != null ) {
for ( var i in modulesDir ) {
if ( modulesDir [ i ] . toLowerCase ( ) . endsWith ( '.js' ) ) {
var moduleName = modulesDir [ i ] . substring ( 0 , modulesDir [ i ] . length - 3 ) ;
2018-12-29 18:24:33 -05:00
if ( moduleName . endsWith ( '.min' ) ) { moduleName = moduleName . substring ( 0 , moduleName . length - 4 ) ; } // Remove the ".min" for ".min.js" files.
2019-03-09 17:28:08 -05:00
var moduleData = [ 'try { addModule("' , moduleName , '", "' , obj . escapeCodeString ( obj . fs . readFileSync ( obj . path . join ( moduleDirPath , modulesDir [ i ] ) ) . toString ( 'binary' ) ) , '"); addedModules.push("' , moduleName , '"); } catch (e) { }\r\n' ] ;
2018-12-29 18:24:33 -05:00
// Merge this module
2018-12-30 16:32:40 -05:00
// NOTE: "smbios" module makes some non-AI Linux segfault, only include for IA platforms.
if ( moduleName . startsWith ( 'amt-' ) || ( moduleName == 'smbios' ) ) {
// Add to IA / Intel AMT cores only
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
2018-12-29 18:24:33 -05:00
} else if ( moduleName . startsWith ( 'win-' ) ) {
// Add to Windows cores only
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
2018-12-29 18:24:33 -05:00
} else if ( moduleName . startsWith ( 'linux-' ) ) {
// Add to Linux cores only
2019-03-09 17:28:08 -05:00
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-noamt' ] . push ( ... moduleData ) ;
2018-12-29 18:24:33 -05:00
} else {
// Add to all cores
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-noamt' ] . push ( ... moduleData ) ;
2018-04-12 14:15:01 -04:00
}
2019-01-21 17:05:50 -05:00
// Merge this module to recovery modules if needed
if ( modulesAdd [ 'windows-recovery' ] != null ) {
if ( ( moduleName == 'win-console' ) || ( moduleName == 'win-message-pump' ) || ( moduleName == 'win-terminal' ) ) {
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-recovery' ] . push ( ... moduleData ) ;
2019-01-21 17:05:50 -05:00
}
}
2019-02-25 17:35:08 -05:00
// Merge this module to agent recovery modules if needed
if ( modulesAdd [ 'windows-agentrecovery' ] != null ) {
if ( ( moduleName == 'win-console' ) || ( moduleName == 'win-message-pump' ) || ( moduleName == 'win-terminal' ) ) {
2019-03-09 17:28:08 -05:00
modulesAdd [ 'windows-agentrecovery' ] . push ( ... moduleData ) ;
2019-02-25 17:35:08 -05:00
}
}
2017-12-12 19:04:54 -05:00
}
}
}
2018-12-29 18:24:33 -05:00
// Merge the cores and compute the hashes
for ( var i in modulesAdd ) {
2019-01-21 17:05:50 -05:00
if ( ( i == 'windows-recovery' ) || ( i == 'linux-recovery' ) ) {
2019-03-09 17:28:08 -05:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshRecoveryCore ] . join ( '' ) ;
2019-02-25 17:35:08 -05:00
} else if ( ( i == 'windows-agentrecovery' ) || ( i == 'linux-agentrecovery' ) ) {
2019-03-09 17:28:08 -05:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshAgentRecoveryCore ] . join ( '' ) ;
2019-01-21 17:05:50 -05:00
} else {
2019-03-09 17:28:08 -05:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshCore ] . join ( '' ) ;
2019-01-21 17:05:50 -05:00
}
2018-12-29 18:24:33 -05:00
obj . defaultMeshCoresHash [ i ] = obj . crypto . createHash ( 'sha384' ) . update ( obj . defaultMeshCores [ i ] ) . digest ( "binary" ) ;
2019-08-22 18:31:39 -04:00
obj . debug ( 'main' , 'Core module ' + i + ' is ' + obj . defaultMeshCores [ i ] . length + ' bytes.' ) ;
2018-12-29 18:24:33 -05:00
//console.log('Core module ' + i + ' is ' + obj.defaultMeshCores[i].length + ' bytes.'); // DEBUG, Print the core size
//obj.fs.writeFile("C:\\temp\\" + i + ".js", obj.defaultMeshCores[i].substring(4)); // DEBUG, Write the core to file
}
}
// We are done creating all the mesh cores.
2017-12-12 19:04:54 -05:00
if ( func != null ) { func ( true ) ; }
2018-08-30 15:05:23 -04:00
} ;
2017-12-12 19:04:54 -05:00
// Update the default meshcmd
2018-01-04 18:59:57 -05:00
obj . updateMeshCmdTimer = 'notset' ;
2017-12-12 19:04:54 -05:00
obj . updateMeshCmd = function ( func ) {
2018-12-31 21:09:19 -05:00
// Figure out where meshcmd.js is and read it.
2019-03-09 17:28:08 -05:00
var meshCmd = null , meshcmdPath , moduleAdditions = [ 'var addedModules = [];\r\n' ] , moduleDirPath , modulesDir = null ;
2019-01-03 17:46:52 -05:00
if ( ( obj . args . minifycore !== false ) && ( obj . fs . existsSync ( obj . path . join ( obj . datapath , 'meshcmd.min.js' ) ) ) ) { meshcmdPath = obj . path . join ( obj . datapath , 'meshcmd.min.js' ) ; meshCmd = obj . fs . readFileSync ( meshcmdPath ) . toString ( ) ; }
2018-12-31 21:09:19 -05:00
else if ( obj . fs . existsSync ( obj . path . join ( obj . datapath , 'meshcmd.js' ) ) ) { meshcmdPath = obj . path . join ( obj . datapath , 'meshcmd.js' ) ; meshCmd = obj . fs . readFileSync ( meshcmdPath ) . toString ( ) ; }
2019-01-03 17:46:52 -05:00
else if ( ( obj . args . minifycore !== false ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'agents' , 'meshcmd.min.js' ) ) ) ) { meshcmdPath = obj . path . join ( _ _dirname , 'agents' , 'meshcmd.min.js' ) ; meshCmd = obj . fs . readFileSync ( meshcmdPath ) . toString ( ) ; }
2018-12-31 21:09:19 -05:00
else if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'agents' , 'meshcmd.js' ) ) ) { meshcmdPath = obj . path . join ( _ _dirname , 'agents' , 'meshcmd.js' ) ; meshCmd = obj . fs . readFileSync ( meshcmdPath ) . toString ( ) ; }
else { obj . defaultMeshCmd = null ; if ( func != null ) { func ( false ) ; } } // meshcmd.js not found
meshCmd = meshCmd . replace ( "'***Mesh*Cmd*Version***'" , '\'' + obj . currentVer + '\'' ) ;
// Figure out where the modules_meshcmd folder is.
2019-01-03 17:46:52 -05:00
if ( obj . args . minifycore !== false ) { try { moduleDirPath = obj . path . join ( meshcmdPath , 'modules_meshcmd_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } // Favor minified modules if present.
2018-12-31 21:09:19 -05:00
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( meshcmdPath , 'modules_meshcmd' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } // Use non-minified mofules.
2019-01-03 17:46:52 -05:00
if ( obj . args . minifycore !== false ) { if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( _ _dirname , 'agents' , 'modules_meshcmd_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } } // Favor minified modules if present.
2018-12-31 21:09:19 -05:00
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( _ _dirname , 'agents' , 'modules_meshcmd' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( e ) { } } // Use non-minified mofules.
// Read all .js files in the meshcmd modules folder.
2017-10-16 23:11:03 -04:00
if ( modulesDir != null ) {
for ( var i in modulesDir ) {
if ( modulesDir [ i ] . toLowerCase ( ) . endsWith ( '.js' ) ) {
// Merge this module
var moduleName = modulesDir [ i ] . substring ( 0 , modulesDir [ i ] . length - 3 ) ;
2018-12-31 21:09:19 -05:00
if ( moduleName . endsWith ( '.min' ) ) { moduleName = moduleName . substring ( 0 , moduleName . length - 4 ) ; } // Remove the ".min" for ".min.js" files.
2019-03-09 17:28:08 -05:00
moduleAdditions . push ( 'try { addModule("' , moduleName , '", "' , obj . escapeCodeString ( obj . fs . readFileSync ( obj . path . join ( moduleDirPath , modulesDir [ i ] ) ) . toString ( 'binary' ) ) , '"); addedModules.push("' , moduleName , '"); } catch (e) { }\r\n' ) ;
2017-10-16 23:11:03 -04:00
}
2017-10-03 21:31:20 -04:00
}
}
2017-12-12 19:04:54 -05:00
// Set the new default meshcmd.js
2019-03-09 17:28:08 -05:00
moduleAdditions . push ( meshCmd ) ;
obj . defaultMeshCmd = moduleAdditions . join ( '' ) ;
2018-12-31 21:09:19 -05:00
//console.log('MeshCmd is ' + obj.defaultMeshCmd.length + ' bytes.'); // DEBUG, Print the merged meshcmd.js size
//obj.fs.writeFile("C:\\temp\\meshcmd.js", obj.defaultMeshCmd.substring(4)); // DEBUG, Write merged meshcmd.js to file
2017-10-03 21:31:20 -04:00
if ( func != null ) { func ( true ) ; }
2018-01-04 18:59:57 -05:00
2018-12-31 21:09:19 -05:00
// Monitor for changes in meshcmd.js
if ( obj . updateMeshCmdTimer === 'notset' ) {
2018-01-04 18:59:57 -05:00
obj . updateMeshCmdTimer = null ;
2018-12-31 21:09:19 -05:00
obj . fs . watch ( meshcmdPath , function ( eventType , filename ) {
2018-01-04 18:59:57 -05:00
if ( obj . updateMeshCmdTimer != null ) { clearTimeout ( obj . updateMeshCmdTimer ) ; obj . updateMeshCmdTimer = null ; }
2019-03-09 17:28:08 -05:00
obj . updateMeshCmdTimer = setTimeout ( function ( ) { obj . updateMeshCmd ( ) ; } , 5000 ) ;
2018-08-30 15:05:23 -04:00
} ) ;
2018-01-04 18:59:57 -05:00
}
2018-08-30 15:05:23 -04:00
} ;
2017-10-03 21:31:20 -04:00
2017-08-28 12:27:45 -04:00
// List of possible mesh agent install scripts
var meshAgentsInstallScriptList = {
2019-03-01 21:07:38 -05:00
1 : { id : 1 , localname : 'meshinstall-linux.sh' , rname : 'meshinstall.sh' , linux : true } ,
2019-07-22 20:26:26 -04:00
2 : { id : 2 , localname : 'meshinstall-initd.sh' , rname : 'meshagent' , linux : true } ,
5 : { id : 5 , localname : 'meshinstall-bsd-rcd.sh' , rname : 'meshagent' , linux : true }
2017-08-28 12:27:45 -04:00
} ;
// Update the list of available mesh agents
obj . updateMeshAgentInstallScripts = function ( ) {
for ( var scriptid in meshAgentsInstallScriptList ) {
var scriptpath = obj . path . join ( _ _dirname , 'agents' , meshAgentsInstallScriptList [ scriptid ] . localname ) ;
var stream = null ;
try {
stream = obj . fs . createReadStream ( scriptpath ) ;
2019-01-30 16:43:42 -05:00
stream . xdata = '' ;
stream . on ( 'data' , function ( data ) { this . hash . update ( data , 'binary' ) ; this . xdata += data ; } ) ;
2017-08-28 12:27:45 -04:00
stream . on ( 'error' , function ( data ) {
// If there is an error reading this file, make sure this agent is not in the agent table
2017-09-15 14:45:06 -04:00
if ( obj . meshAgentInstallScripts [ this . info . id ] != null ) { delete obj . meshAgentInstallScripts [ this . info . id ] ; }
2017-08-28 12:27:45 -04:00
} ) ;
stream . on ( 'end' , function ( ) {
// Add the agent to the agent table with all information and the hash
obj . meshAgentInstallScripts [ this . info . id ] = obj . common . Clone ( this . info ) ;
obj . meshAgentInstallScripts [ this . info . id ] . hash = this . hash . digest ( 'hex' ) ;
obj . meshAgentInstallScripts [ this . info . id ] . path = this . agentpath ;
2019-01-30 16:43:42 -05:00
obj . meshAgentInstallScripts [ this . info . id ] . data = this . xdata ;
2019-03-11 00:40:25 -04:00
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 ;
2017-08-28 12:27:45 -04:00
var stats = null ;
2018-08-30 15:05:23 -04:00
try { stats = obj . fs . statSync ( this . agentpath ) ; } catch ( e ) { }
2017-08-28 12:27:45 -04:00
if ( stats != null ) { obj . meshAgentInstallScripts [ this . info . id ] . size = stats . size ; }
2019-03-01 21:07:38 -05:00
// Place Unit line breaks on Linux scripts if not already present.
if ( obj . meshAgentInstallScripts [ this . info . id ] . linux === true ) { obj . meshAgentInstallScripts [ this . info . id ] . data = obj . meshAgentInstallScripts [ this . info . id ] . data . split ( '\r\n' ) . join ( '\n' ) }
2017-08-28 12:27:45 -04:00
} ) ;
stream . info = meshAgentsInstallScriptList [ scriptid ] ;
stream . agentpath = scriptpath ;
2017-10-15 02:22:19 -04:00
stream . hash = obj . crypto . createHash ( 'sha384' , stream ) ;
2017-08-28 12:27:45 -04:00
} catch ( e ) { }
}
2018-08-30 15:05:23 -04:00
} ;
2018-12-29 18:24:33 -05:00
2017-08-28 12:27:45 -04:00
// List of possible mesh agents
2018-01-02 19:52:49 -05:00
obj . meshAgentsArchitectureNumbers = {
2019-02-25 17:35:08 -05:00
0 : { id : 0 , localname : 'Unknown' , rname : 'meshconsole.exe' , desc : 'Unknown agent' , update : false , amt : true , platform : 'unknown' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
2019-07-31 19:49:23 -04:00
1 : { id : 1 , localname : 'MeshConsole.exe' , rname : 'meshconsole32.exe' , desc : 'Windows x86-32 console' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
2 : { id : 2 , localname : 'MeshConsole64.exe' , rname : 'meshconsole64.exe' , desc : 'Windows x86-64 console' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
3 : { id : 3 , localname : 'MeshService-signed.exe' , rname : 'meshagent32.exe' , desc : 'Windows x86-32 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
4 : { id : 4 , localname : 'MeshService64-signed.exe' , rname : 'meshagent64.exe' , desc : 'Windows x86-64 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
2019-02-25 17:35:08 -05:00
5 : { id : 5 , localname : 'meshagent_x86' , rname : 'meshagent' , desc : 'Linux x86-32' , update : true , amt : true , platform : 'linux' , core : 'linux-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
6 : { id : 6 , localname : 'meshagent_x86-64' , rname : 'meshagent' , desc : 'Linux x86-64' , update : true , amt : true , platform : 'linux' , core : 'linux-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
7 : { id : 7 , localname : 'meshagent_mips' , rname : 'meshagent' , desc : 'Linux MIPS' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
8 : { id : 8 , localname : 'MeshAgent-Linux-XEN-x86-32' , rname : 'meshagent' , desc : 'XEN x86-64' , update : true , amt : false , platform : 'linux' , core : 'linux-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
9 : { id : 9 , localname : 'meshagent_arm' , rname : 'meshagent' , desc : 'Linux ARM5' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
10 : { id : 10 , localname : 'MeshAgent-Linux-ARM-PlugPC' , rname : 'meshagent' , desc : 'Linux ARM PlugPC' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
11 : { id : 11 , localname : 'meshagent_osx-x86-32' , rname : 'meshosx' , desc : 'Apple OSX x86-32' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
12 : { id : 12 , localname : 'MeshAgent-Android-x86' , rname : 'meshandroid' , desc : 'Android x86-32' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
13 : { id : 13 , localname : 'meshagent_pogo' , rname : 'meshagent' , desc : 'Linux ARM PogoPlug' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
14 : { id : 14 , localname : 'MeshAgent-Android-APK' , rname : 'meshandroid' , desc : 'Android Market' , update : false , amt : false , platform : 'android' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // Get this one from Google Play
15 : { id : 15 , localname : 'meshagent_poky' , rname : 'meshagent' , desc : 'Linux Poky x86-32' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
16 : { id : 16 , localname : 'meshagent_osx-x86-64' , rname : 'meshagent' , desc : 'Apple OSX x86-64' , update : true , amt : false , platform : 'osx' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
17 : { id : 17 , localname : 'MeshAgent-ChromeOS' , rname : 'meshagent' , desc : 'Google ChromeOS' , update : false , amt : false , platform : 'chromeos' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // Get this one from Chrome store
18 : { id : 18 , localname : 'meshagent_poky64' , rname : 'meshagent' , desc : 'Linux Poky x86-64' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
19 : { id : 19 , localname : 'meshagent_x86_nokvm' , rname : 'meshagent' , desc : 'Linux x86-32 NoKVM' , update : true , amt : true , platform : 'linux' , core : 'linux-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
20 : { id : 20 , localname : 'meshagent_x86-64_nokvm' , rname : 'meshagent' , desc : 'Linux x86-64 NoKVM' , update : true , amt : true , platform : 'linux' , core : 'linux-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
21 : { id : 21 , localname : 'MeshAgent-WinMinCore-Console-x86-32.exe' , rname : 'meshagent.exe' , desc : 'Windows MinCore Console x86-32' , update : true , amt : false , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
22 : { id : 22 , localname : 'MeshAgent-WinMinCore-Service-x86-64.exe' , rname : 'meshagent.exe' , desc : 'Windows MinCore Service x86-32' , update : true , amt : false , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' } ,
23 : { id : 23 , localname : 'MeshAgent-NodeJS' , rname : 'meshagent' , desc : 'NodeJS' , update : false , amt : false , platform : 'node' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // Get this one from NPM
24 : { id : 24 , localname : 'meshagent_arm-linaro' , rname : 'meshagent' , desc : 'Linux ARM Linaro' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } ,
25 : { id : 25 , localname : 'meshagent_armhf' , rname : 'meshagent' , desc : 'Linux ARM - HardFloat' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // "armv6l" and "armv7l"
2019-04-28 13:25:53 -04:00
26 : { id : 26 , localname : 'meshagent_arm64' , rname : 'meshagent' , desc : 'Linux ARMv8-64' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // "aarch64"
2019-06-21 13:14:56 -04:00
27 : { id : 27 , localname : 'meshagent_armhf2' , rname : 'meshagent' , desc : 'Linux ARM - HardFloat' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // Raspbian 7 2015-02-02 for old Raspberry Pi.
2019-06-17 20:17:23 -04:00
30 : { id : 30 , localname : 'meshagent_freebsd64' , rname : 'meshagent' , desc : 'FreeBSD x86-64' , update : true , amt : false , platform : 'freebsd' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // FreeBSD x64
2019-02-25 17:35:08 -05:00
10003 : { id : 3 , localname : 'MeshService.exe' , rname : 'meshagent.exe' , desc : 'Windows x86-32 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } , // Unsigned version of the Windows MeshAgent x86
10004 : { id : 4 , localname : 'MeshService64.exe' , rname : 'meshagent.exe' , desc : 'Windows x86-64 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' } // Unsigned version of the Windows MeshAgent x64
2017-08-28 12:27:45 -04:00
} ;
// Update the list of available mesh agents
2017-10-25 12:58:14 -04:00
obj . updateMeshAgentsTable = function ( func ) {
var archcount = 0 ;
2018-01-02 19:52:49 -05:00
for ( var archid in obj . meshAgentsArchitectureNumbers ) {
var agentpath = obj . path . join ( _ _dirname , 'agents' , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2018-08-30 15:05:23 -04:00
2018-01-19 21:04:54 -05:00
// Fetch all the agent binary information
var stats = null ;
2018-08-30 15:05:23 -04:00
try { stats = obj . fs . statSync ( agentpath ) ; } catch ( e ) { }
2018-01-19 21:04:54 -05:00
if ( ( stats != null ) ) {
// If file exists
archcount ++ ;
2018-03-09 19:39:14 -05:00
obj . meshAgentBinaries [ archid ] = obj . common . Clone ( obj . meshAgentsArchitectureNumbers [ archid ] ) ;
obj . meshAgentBinaries [ archid ] . path = agentpath ;
2019-03-11 00:40:25 -04:00
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 ;
2018-01-19 21:04:54 -05:00
obj . meshAgentBinaries [ archid ] . size = stats . size ;
2019-03-08 01:47:27 -05:00
if ( obj . args . agentsinram ) { obj . meshAgentBinaries [ archid ] . data = obj . fs . readFileSync ( agentpath ) ; }
2018-01-19 21:04:54 -05:00
// If this is a windows binary, pull binary information
if ( obj . meshAgentsArchitectureNumbers [ archid ] . platform == 'win32' ) {
try { obj . meshAgentBinaries [ archid ] . pe = obj . exeHandler . parseWindowsExecutable ( agentpath ) ; } catch ( e ) { }
}
2019-02-15 01:16:15 -05:00
2018-01-19 21:04:54 -05:00
// Hash the binary
var hashStream = obj . crypto . createHash ( 'sha384' ) ;
hashStream . archid = archid ;
hashStream . on ( 'data' , function ( data ) {
2019-03-08 01:47:27 -05:00
obj . meshAgentBinaries [ this . archid ] . hash = data . toString ( 'binary' ) ;
2017-10-25 12:58:14 -04:00
if ( ( -- archcount == 0 ) && ( func != null ) ) { func ( ) ; }
2017-08-28 12:27:45 -04:00
} ) ;
2018-01-19 21:04:54 -05:00
var options = { sourcePath : agentpath , targetStream : hashStream , platform : obj . meshAgentsArchitectureNumbers [ archid ] . platform } ;
if ( obj . meshAgentBinaries [ archid ] . pe != null ) { options . peinfo = obj . meshAgentBinaries [ archid ] . pe ; }
obj . exeHandler . hashExecutableFile ( options ) ;
}
2017-08-28 12:27:45 -04:00
}
2018-03-09 19:39:14 -05:00
if ( ( obj . meshAgentBinaries [ 3 ] == null ) && ( obj . meshAgentBinaries [ 10003 ] != null ) ) { obj . meshAgentBinaries [ 3 ] = obj . meshAgentBinaries [ 10003 ] ; } // If only the unsigned windows binaries are present, use them.
if ( ( obj . meshAgentBinaries [ 4 ] == null ) && ( obj . meshAgentBinaries [ 10004 ] != null ) ) { obj . meshAgentBinaries [ 4 ] = obj . meshAgentBinaries [ 10004 ] ; } // If only the unsigned windows binaries are present, use them.
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
2017-12-13 17:52:57 -05:00
// Generate a time limited user login token
obj . getLoginToken = function ( userid , func ) {
2018-01-02 19:52:49 -05:00
if ( ( userid == null ) || ( typeof userid != 'string' ) ) { func ( 'Invalid userid.' ) ; return ; }
2017-12-13 17:52:57 -05:00
var x = userid . split ( '/' ) ;
if ( x == null || x . length != 3 || x [ 0 ] != 'user' ) { func ( 'Invalid userid.' ) ; return ; }
obj . db . Get ( userid , function ( err , docs ) {
if ( err != null || docs == null || docs . length == 0 ) {
func ( 'User ' + userid + ' not found.' ) ; return ;
} else {
// Load the login cookie encryption key from the database
obj . db . Get ( 'LoginCookieEncryptionKey' , function ( err , docs ) {
2018-12-29 14:38:05 -05:00
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
2017-12-13 17:52:57 -05:00
// Key is present, use it.
obj . loginCookieEncryptionKey = Buffer . from ( docs [ 0 ] . key , 'hex' ) ;
func ( obj . encodeCookie ( { u : userid , a : 3 } , obj . loginCookieEncryptionKey ) ) ;
} else {
// Key is not present, generate one.
obj . loginCookieEncryptionKey = obj . generateCookieKey ( ) ;
obj . db . Set ( { _id : 'LoginCookieEncryptionKey' , key : obj . loginCookieEncryptionKey . toString ( 'hex' ) , time : Date . now ( ) } , function ( ) { func ( obj . encodeCookie ( { u : userid , a : 3 } , obj . loginCookieEncryptionKey ) ) ; } ) ;
}
} ) ;
}
} ) ;
2018-08-30 15:05:23 -04:00
} ;
2017-12-13 17:52:57 -05:00
2018-12-29 00:55:23 -05:00
// Show the user login token generation key
2017-12-13 17:52:57 -05:00
obj . showLoginTokenKey = function ( func ) {
// Load the login cookie encryption key from the database
obj . db . Get ( 'LoginCookieEncryptionKey' , function ( err , docs ) {
2018-12-29 14:38:05 -05:00
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
2017-12-13 17:52:57 -05:00
// Key is present, use it.
func ( docs [ 0 ] . key ) ;
} else {
// Key is not present, generate one.
obj . loginCookieEncryptionKey = obj . generateCookieKey ( ) ;
obj . db . Set ( { _id : 'LoginCookieEncryptionKey' , key : obj . loginCookieEncryptionKey . toString ( 'hex' ) , time : Date . now ( ) } , function ( ) { func ( obj . loginCookieEncryptionKey . toString ( 'hex' ) ) ; } ) ;
}
} ) ;
2018-08-30 15:05:23 -04:00
} ;
2017-12-13 17:52:57 -05:00
// Generate a cryptographic key used to encode and decode cookies
obj . generateCookieKey = function ( ) {
2019-01-02 21:03:34 -05:00
return Buffer . from ( obj . crypto . randomBytes ( 80 ) , 'binary' ) ;
2018-12-29 14:38:05 -05:00
//return Buffer.alloc(80, 0); // Sets the key to zeros, debug only.
2018-08-30 15:05:23 -04:00
} ;
2017-12-13 17:52:57 -05:00
2018-12-29 00:55:23 -05:00
// Encode an object as a cookie using a key using AES-GCM. (key must be 32 bytes or more)
2017-12-13 17:52:57 -05:00
obj . encodeCookie = function ( o , key ) {
try {
if ( key == null ) { key = obj . serverKey ; }
o . time = Math . floor ( Date . now ( ) / 1000 ) ; // Add the cookie creation time
2019-01-02 21:03:34 -05:00
const iv = Buffer . from ( obj . crypto . randomBytes ( 12 ) , 'binary' ) , cipher = obj . crypto . createCipheriv ( 'aes-256-gcm' , key . slice ( 0 , 32 ) , iv ) ;
2018-12-29 00:55:23 -05:00
const crypted = Buffer . concat ( [ cipher . update ( JSON . stringify ( o ) , 'utf8' ) , cipher . final ( ) ] ) ;
2019-08-23 14:51:48 -04:00
var r = Buffer . concat ( [ iv , cipher . getAuthTag ( ) , crypted ] ) . toString ( 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ;
obj . debug ( 'cookie' , 'Encoded AESGCM cookie: ' + JSON . stringify ( o ) ) ;
return r ;
} catch ( ex ) { obj . debug ( 'cookie' , 'ERR: Failed to encode AESGCM cookie due to exception: ' + ex ) ; return null ; }
2018-08-30 15:05:23 -04:00
} ;
2017-12-13 17:52:57 -05:00
2018-12-29 14:38:05 -05:00
// Decode a cookie back into an object using a key using AES256-GCM or AES128-CBC/HMAC-SHA386. Return null if it's not a valid cookie. (key must be 32 bytes or more)
2017-12-13 17:52:57 -05:00
obj . decodeCookie = function ( cookie , key , timeout ) {
2018-12-29 00:55:23 -05:00
const r = obj . decodeCookieAESGCM ( cookie , key , timeout ) ;
if ( r == null ) { return obj . decodeCookieAESSHA ( cookie , key , timeout ) ; }
return r ;
}
2018-12-29 14:38:05 -05:00
// Decode a cookie back into an object using a key using AES256-GCM. Return null if it's not a valid cookie. (key must be 32 bytes or more)
2018-12-29 00:55:23 -05:00
obj . decodeCookieAESGCM = function ( cookie , key , timeout ) {
2017-12-13 17:52:57 -05:00
try {
if ( key == null ) { key = obj . serverKey ; }
2019-01-02 21:03:34 -05:00
cookie = Buffer . from ( cookie . replace ( /\@/g , '+' ) . replace ( /\$/g , '/' ) , 'base64' ) ;
2018-12-29 00:55:23 -05:00
const decipher = obj . crypto . createDecipheriv ( 'aes-256-gcm' , key . slice ( 0 , 32 ) , cookie . slice ( 0 , 12 ) ) ;
2017-12-13 17:52:57 -05:00
decipher . setAuthTag ( cookie . slice ( 12 , 16 ) ) ;
2018-12-29 00:55:23 -05:00
const o = JSON . parse ( decipher . update ( cookie . slice ( 28 ) , 'binary' , 'utf8' ) + decipher . final ( 'utf8' ) ) ;
2019-08-22 18:31:39 -04:00
if ( ( o . time == null ) || ( o . time == null ) || ( typeof o . time != 'number' ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to invalid time' ) ; return null ; }
2017-12-13 17:52:57 -05:00
o . time = o . time * 1000 ; // Decode the cookie creation time
o . dtime = Date . now ( ) - o . time ; // Decode how long ago the cookie was created (in milliseconds)
2019-06-03 16:15:38 -04:00
if ( ( o . expire ) == null || ( typeof o . expire != 'number' ) ) {
// Use a fixed cookie expire time
if ( timeout == null ) { timeout = 2 ; }
2019-08-22 18:31:39 -04:00
if ( ( o . dtime > ( timeout * 60000 ) ) || ( o . dtime < - 30000 ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to timeout' ) ; return null ; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
2019-06-03 16:15:38 -04:00
} else {
// An expire time is included in the cookie (in minutes), use this.
2019-08-22 18:31:39 -04:00
if ( ( o . expire !== 0 ) && ( ( o . dtime > ( o . expire * 60000 ) ) || ( o . dtime < - 30000 ) ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to timeout' ) ; return null ; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
2019-06-03 16:15:38 -04:00
}
2019-08-23 14:51:48 -04:00
obj . debug ( 'cookie' , 'Decoded AESGCM cookie: ' + JSON . stringify ( o ) ) ;
2017-12-13 17:52:57 -05:00
return o ;
2019-08-22 18:31:39 -04:00
} catch ( ex ) { obj . debug ( 'cookie' , 'ERR: Bad AESGCM cookie due to exception: ' + ex ) ; return null ; }
2018-08-30 15:05:23 -04:00
} ;
2017-12-13 17:52:57 -05:00
2018-12-29 14:38:05 -05:00
// Decode a cookie back into an object using a key using AES256 / HMAC-SHA386. Return null if it's not a valid cookie. (key must be 80 bytes or more)
// We do this because poor .NET does not support AES256-GCM.
2018-12-29 00:55:23 -05:00
obj . decodeCookieAESSHA = function ( cookie , key , timeout ) {
try {
if ( key == null ) { key = obj . serverKey ; }
2018-12-29 14:38:05 -05:00
if ( key . length < 80 ) { return null ; }
2019-01-02 21:03:34 -05:00
cookie = Buffer . from ( cookie . replace ( /\@/g , '+' ) . replace ( /\$/g , '/' ) , 'base64' ) ;
2018-12-29 14:38:05 -05:00
const decipher = obj . crypto . createDecipheriv ( 'aes-256-cbc' , key . slice ( 48 , 80 ) , cookie . slice ( 0 , 16 ) ) ;
2018-12-29 00:55:23 -05:00
const rawmsg = decipher . update ( cookie . slice ( 16 ) , 'binary' , 'binary' ) + decipher . final ( 'binary' ) ;
2018-12-29 14:38:05 -05:00
const hmac = obj . crypto . createHmac ( 'sha384' , key . slice ( 0 , 48 ) ) ;
hmac . update ( rawmsg . slice ( 48 ) ) ;
if ( Buffer . compare ( hmac . digest ( ) , Buffer . from ( rawmsg . slice ( 0 , 48 ) ) ) == false ) { return null ; }
const o = JSON . parse ( rawmsg . slice ( 48 ) . toString ( 'utf8' ) ) ;
2019-08-22 18:31:39 -04:00
if ( ( o . time == null ) || ( o . time == null ) || ( typeof o . time != 'number' ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to invalid time' ) ; return null ; }
2018-12-29 00:55:23 -05:00
o . time = o . time * 1000 ; // Decode the cookie creation time
o . dtime = Date . now ( ) - o . time ; // Decode how long ago the cookie was created (in milliseconds)
2019-06-03 16:15:38 -04:00
if ( ( o . expire ) == null || ( typeof o . expire != 'number' ) ) {
// Use a fixed cookie expire time
if ( timeout == null ) { timeout = 2 ; }
2019-08-22 18:31:39 -04:00
if ( ( o . dtime > ( timeout * 60000 ) ) || ( o . dtime < - 30000 ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to timeout' ) ; return null ; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
2019-06-03 16:15:38 -04:00
} else {
// An expire time is included in the cookie (in minutes), use this.
2019-08-22 18:31:39 -04:00
if ( ( o . expire !== 0 ) && ( ( o . dtime > ( o . expire * 60000 ) ) || ( o . dtime < - 30000 ) ) ) { obj . debug ( 'cookie' , 'ERR: Bad cookie due to timeout' ) ; return null ; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
2019-06-03 16:15:38 -04:00
}
2019-08-23 14:51:48 -04:00
obj . debug ( 'cookie' , 'Decoded AESSHA cookie: ' + JSON . stringify ( o ) ) ;
2018-12-29 00:55:23 -05:00
return o ;
2019-08-22 18:31:39 -04:00
} catch ( ex ) { obj . debug ( 'cookie' , 'ERR: Bad AESSHA cookie due to exception: ' + ex ) ; return null ; }
2018-12-29 00:55:23 -05:00
} ;
2017-08-28 12:27:45 -04:00
// Debug
2019-08-22 18:31:39 -04:00
obj . debug = function ( source , ... args ) {
// Send event to console
if ( ( obj . debugSources != null ) && ( ( obj . debugSources == '*' ) || ( obj . debugSources . indexOf ( source ) >= 0 ) ) ) { console . log ( source . toUpperCase ( ) + ':' , ... args ) ; }
// Send the event to logged in administrators
if ( ( obj . debugRemoteSources != null ) && ( ( obj . debugRemoteSources == '*' ) || ( obj . debugRemoteSources . indexOf ( source ) >= 0 ) ) ) {
2019-08-23 14:51:48 -04:00
var sendcount = 0 ;
2019-08-22 18:31:39 -04:00
for ( var sessionid in obj . webserver . wssessions2 ) {
var ws = obj . webserver . wssessions2 [ sessionid ] ;
if ( ( ws != null ) && ( ws . userid != null ) ) {
var user = obj . webserver . users [ ws . userid ] ;
if ( ( user != null ) && ( user . siteadmin == 4294967295 ) ) {
2019-08-23 14:51:48 -04:00
try { ws . send ( JSON . stringify ( { action : 'trace' , source : source , args : args , time : Date . now ( ) } ) ) ; sendcount ++ ; } catch ( ex ) { }
2019-08-22 18:31:39 -04:00
}
}
}
2019-08-23 14:51:48 -04:00
if ( sendcount == 0 ) { obj . debugRemoteSources = null ; } // If there are no listeners, remove debug sources.
2019-08-22 18:31:39 -04:00
}
2018-08-30 15:05:23 -04:00
} ;
2018-03-14 15:10:13 -04:00
// Update server state. Writes a server state file.
var meshServerState = { } ;
2018-08-30 15:05:23 -04:00
obj . updateServerState = function ( name , val ) {
2019-01-03 17:46:52 -05:00
//console.log('updateServerState', name, val);
2018-09-20 14:45:12 -04:00
try {
if ( ( name != null ) && ( val != null ) ) {
var changed = false ;
if ( ( name != null ) && ( meshServerState [ name ] != val ) ) { if ( ( val == null ) && ( meshServerState [ name ] != null ) ) { delete meshServerState [ name ] ; changed = true ; } else { if ( meshServerState [ name ] != val ) { meshServerState [ name ] = val ; changed = true ; } } }
if ( changed == false ) return ;
}
var r = 'time=' + Date . now ( ) + '\r\n' ;
for ( var i in meshServerState ) { r += ( i + '=' + meshServerState [ i ] + '\r\n' ) ; }
2018-09-26 17:58:55 -04:00
try {
obj . fs . writeFileSync ( obj . getConfigFilePath ( 'serverstate.txt' ) , r ) ; // Try to write the server state, this may fail if we don't have permission.
} catch ( ex ) { obj . serverSelfWriteAllowed = false ; }
} catch ( ex ) { } // Do nothing since this is not a critical feature.
2018-08-30 15:05:23 -04:00
} ;
2017-08-28 12:27:45 -04:00
// Logging funtions
function logException ( e ) { e += '' ; logErrorEvent ( e ) ; }
function logInfoEvent ( msg ) { if ( obj . servicelog != null ) { obj . servicelog . info ( msg ) ; } console . log ( msg ) ; }
function logWarnEvent ( msg ) { if ( obj . servicelog != null ) { obj . servicelog . warn ( msg ) ; } console . log ( msg ) ; }
function logErrorEvent ( msg ) { if ( obj . servicelog != null ) { obj . servicelog . error ( msg ) ; } console . error ( msg ) ; }
2018-07-13 22:18:43 -04:00
// Return the path of a file into the meshcentral-data path
obj . getConfigFilePath = function ( filename ) {
if ( ( obj . config != null ) && ( obj . config . configfiles != null ) && ( obj . config . configfiles [ filename ] != null ) && ( typeof obj . config . configfiles [ filename ] == 'string' ) ) {
//console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.config.configfiles[filename]);
return obj . config . configfiles [ filename ] ;
}
//console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.path.join(obj.datapath, filename));
return obj . path . join ( obj . datapath , filename ) ;
2018-08-30 15:05:23 -04:00
} ;
2018-07-13 22:18:43 -04:00
2017-08-28 12:27:45 -04:00
return obj ;
}
2018-01-31 19:10:15 -05:00
// Return the server configuration
2019-02-02 17:54:36 -05:00
function getConfig ( createSampleConfig ) {
2018-01-31 19:10:15 -05:00
// Figure out the datapath location
2019-05-17 15:40:15 -04:00
var i , fs = require ( 'fs' ) , path = require ( 'path' ) , datapath = null ;
2018-01-31 19:10:15 -05:00
var args = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
if ( ( _ _dirname . endsWith ( '/node_modules/meshcentral' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral' ) ) || ( _ _dirname . endsWith ( '/node_modules/meshcentral/' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral\\' ) ) ) {
datapath = path . join ( _ _dirname , '../../meshcentral-data' ) ;
} else {
datapath = path . join ( _ _dirname , '../meshcentral-data' ) ;
}
if ( args . datapath ) { datapath = args . datapath ; }
try { fs . mkdirSync ( datapath ) ; } catch ( e ) { }
// Read configuration file if present and change arguments.
var config = { } , configFilePath = path . join ( datapath , 'config.json' ) ;
if ( fs . existsSync ( configFilePath ) ) {
// Load and validate the configuration file
try { config = require ( configFilePath ) ; } catch ( e ) { console . log ( 'ERROR: Unable to parse ' + configFilePath + '.' ) ; return null ; }
if ( config . domains == null ) { config . domains = { } ; }
2018-08-30 15:05:23 -04:00
for ( i in config . domains ) { if ( ( i . split ( '/' ) . length > 1 ) || ( i . split ( ' ' ) . length > 1 ) ) { console . log ( "ERROR: Error in config.json, domain names can't have spaces or /." ) ; return null ; } }
2018-01-31 19:10:15 -05:00
} else {
2019-02-02 17:54:36 -05:00
if ( createSampleConfig === true ) {
// Copy the "sample-config.json" to give users a starting point
var sampleConfigPath = path . join ( _ _dirname , 'sample-config.json' ) ;
if ( fs . existsSync ( sampleConfigPath ) ) { fs . createReadStream ( sampleConfigPath ) . pipe ( fs . createWriteStream ( configFilePath ) ) ; }
}
2018-01-31 19:10:15 -05:00
}
// Set the command line arguments to the config file if they are not present
if ( ! config . settings ) { config . settings = { } ; }
2018-08-30 15:05:23 -04:00
for ( i in args ) { config . settings [ i ] = args [ i ] ; }
2018-01-31 19:10:15 -05:00
// Lower case all keys in the config file
2019-01-04 17:35:01 -05:00
try {
2019-04-11 16:41:51 -04:00
require ( './common.js' ) . objKeysToLower ( config , [ "ldapoptions" ] ) ;
2019-01-04 17:35:01 -05:00
} catch ( ex ) {
console . log ( 'CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.' ) ;
process . exit ( ) ;
}
2019-04-11 16:41:51 -04:00
2018-01-31 19:10:15 -05:00
return config ;
}
// Check if a list of modules are present and install any missing ones
2017-08-28 12:27:45 -04:00
function InstallModules ( modules , func ) {
2019-03-23 16:28:17 -04:00
var missingModules = [ ] ;
if ( modules . length > 0 ) {
2019-03-26 17:11:51 -04:00
for ( var i in modules ) {
try {
var xxmodule = require ( modules [ i ] ) ;
} catch ( e ) {
2019-05-03 16:57:52 -04:00
if ( previouslyInstalledModules [ modules [ i ] ] !== true ) { missingModules . push ( modules [ i ] ) ; }
2019-03-26 17:11:51 -04:00
}
}
2019-03-25 22:59:04 -04:00
if ( missingModules . length > 0 ) { InstallModule ( missingModules . shift ( ) , InstallModules , modules , func ) ; } else { func ( ) ; }
2019-03-23 16:28:17 -04:00
}
2017-08-28 12:27:45 -04:00
}
2018-01-31 19:10:15 -05:00
// Check if a module is present and install it if missing
2019-03-23 16:28:17 -04:00
var InstallModuleChildProcess = null ;
2017-08-28 12:27:45 -04:00
function InstallModule ( modulename , func , tag1 , tag2 ) {
2019-03-25 14:32:16 -04:00
console . log ( 'Installing ' + modulename + '...' ) ;
var child _process = require ( 'child_process' ) ;
2019-04-05 12:21:21 -04:00
var parentpath = _ _dirname ;
// Get the working directory
if ( ( _ _dirname . endsWith ( '/node_modules/meshcentral' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral' ) ) || ( _ _dirname . endsWith ( '/node_modules/meshcentral/' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral\\' ) ) ) { parentpath = require ( 'path' ) . join ( _ _dirname , '../..' ) ; }
2019-03-25 14:32:16 -04:00
// Looks like we need to keep a global reference to the child process object for this to work correctly.
2019-04-05 12:21:21 -04:00
InstallModuleChildProcess = child _process . exec ( 'npm install --no-optional --save ' + modulename , { maxBuffer : 512000 , timeout : 10000 , cwd : parentpath } , function ( error , stdout , stderr ) {
2019-03-25 14:32:16 -04:00
InstallModuleChildProcess = null ;
2019-05-17 15:40:15 -04:00
if ( ( error != null ) && ( error != '' ) ) {
2019-03-28 18:56:48 -04:00
console . log ( 'ERROR: Unable to install required module "' + modulename + '". MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. Try "npm install ' + modulename + '" to manualy install this module.\r\n' ) ;
2019-03-27 15:55:31 -04:00
process . exit ( ) ;
return ;
}
2019-05-03 16:57:52 -04:00
previouslyInstalledModules [ modulename ] = true ;
2019-03-25 14:32:16 -04:00
func ( tag1 , tag2 ) ;
2017-08-28 12:27:45 -04:00
return ;
2019-03-25 14:32:16 -04:00
} ) ;
2017-08-28 12:27:45 -04:00
}
// Detect CTRL-C on Linux and stop nicely
2017-09-04 15:20:18 -04:00
process . on ( 'SIGINT' , function ( ) { if ( meshserver != null ) { meshserver . Stop ( ) ; meshserver = null ; } console . log ( 'Server Ctrl-C exit...' ) ; process . exit ( ) ; } ) ;
2017-08-28 12:27:45 -04:00
2018-01-31 19:10:15 -05:00
// Load the really basic modules
2017-08-28 12:27:45 -04:00
var meshserver = null ;
2019-07-17 18:57:42 -04:00
var childProcess = null ;
2019-03-26 17:11:51 -04:00
var previouslyInstalledModules = { } ;
2019-05-17 18:44:01 -04:00
function mainStart ( ) {
2018-08-31 18:23:42 -04:00
// Check the NodeJS is version 6 or better.
if ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) < 6 ) { console . log ( "MeshCentral requires Node v6.x or above, current version is " + process . version + "." ) ; return ; }
// Check for any missing modules.
2018-03-14 18:16:55 -04:00
InstallModules ( [ 'minimist' ] , function ( ) {
2019-05-17 18:44:01 -04:00
// Parse inbound arguments
var args = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
2018-03-14 18:16:55 -04:00
// Get the server configuration
2019-02-02 17:54:36 -05:00
var config = getConfig ( false ) ;
2018-03-14 18:16:55 -04:00
if ( config == null ) { process . exit ( ) ; }
2019-05-27 14:58:31 -04:00
// Lowercase the auth value if present
2019-04-11 16:41:51 -04:00
for ( var i in config . domains ) { if ( typeof config . domains [ i ] . auth == 'string' ) { config . domains [ i ] . auth = config . domains [ i ] . auth . toLowerCase ( ) ; } }
2019-03-25 14:32:16 -04:00
// Check is Windows SSPI and YubiKey OTP will be used
2018-05-29 19:57:08 -04:00
var sspi = false ;
2019-04-11 16:41:51 -04:00
var ldap = false ;
2019-03-23 16:28:17 -04:00
var allsspi = true ;
2019-03-25 14:32:16 -04:00
var yubikey = false ;
2019-04-22 20:00:46 -04:00
var domainCount = 0 ;
if ( require ( 'os' ) . platform ( ) == 'win32' ) { for ( var i in config . domains ) { domainCount ++ ; if ( config . domains [ i ] . auth == 'sspi' ) { sspi = true ; } else { allsspi = false ; } } } else { allsspi = false ; }
if ( domainCount == 0 ) { allsspi = false ; }
2019-04-11 16:41:51 -04:00
for ( var i in config . domains ) {
if ( config . domains [ i ] . yubikey != null ) { yubikey = true ; }
if ( config . domains [ i ] . auth == 'ldap' ) { ldap = true ; }
}
2018-05-29 19:57:08 -04:00
2018-03-14 18:16:55 -04:00
// Build the list of required modules
2019-05-16 22:32:04 -04:00
var modules = [ 'ws' , 'cbor' , 'nedb' , 'https' , 'yauzl' , 'xmldom' , 'ipcheck' , 'express' , 'archiver' , 'multiparty' , 'node-forge' , 'express-ws' , 'compression' , 'body-parser' , 'connect-redis' , 'cookie-session' , 'express-handlebars' ] ;
2018-05-29 19:57:08 -04:00
if ( require ( 'os' ) . platform ( ) == 'win32' ) { modules . push ( 'node-windows' ) ; if ( sspi == true ) { modules . push ( 'node-sspi' ) ; } } // Add Windows modules
2019-04-11 16:41:51 -04:00
if ( ldap == true ) { modules . push ( 'ldapauth-fork' ) ; }
2018-03-14 18:16:55 -04:00
if ( config . letsencrypt != null ) { modules . push ( 'greenlock' ) ; modules . push ( 'le-store-certbot' ) ; modules . push ( 'le-challenge-fs' ) ; modules . push ( 'le-acme-core' ) ; } // Add Greenlock Modules
2019-05-20 21:03:14 -04:00
if ( config . settings . mongodb != null ) { modules . push ( 'mongodb' ) ; } // Add MongoDB, official driver.
else if ( config . settings . xmongodb != null ) { modules . push ( 'mongojs' ) ; } // Add MongoJS, old driver.
2018-03-14 18:16:55 -04:00
if ( config . smtp != null ) { modules . push ( 'nodemailer' ) ; } // Add SMTP support
2019-03-23 16:28:17 -04:00
// Get the current node version
var nodeVersion = Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) ;
2018-11-29 20:59:29 -05:00
// If running NodeJS < 8, install "util.promisify"
2019-03-23 16:28:17 -04:00
if ( nodeVersion < 8 ) { modules . push ( 'util.promisify' ) ; }
2019-05-17 15:40:15 -04:00
// Setup encrypted zip support if needed
if ( config . settings . autobackup && config . settings . autobackup . zippassword ) { modules . push ( 'archiver-zip-encrypted' ) ; }
2019-03-26 17:11:51 -04:00
// Setup 2nd factor authentication
if ( config . settings . no2factorauth !== true ) {
// Setup YubiKey OTP if configured
if ( yubikey == true ) { modules . push ( 'yubikeyotp' ) ; } // Add YubiKey OTP support
2019-05-16 17:55:07 -04:00
if ( allsspi == false ) { modules . push ( 'otplib' ) ; } // Google Authenticator support
2019-03-26 17:11:51 -04:00
}
2019-03-25 14:32:16 -04:00
2018-03-14 18:16:55 -04:00
// Install any missing modules and launch the server
InstallModules ( modules , function ( ) { meshserver = CreateMeshCentralServer ( config , args ) ; meshserver . Start ( ) ; } ) ;
2019-07-17 18:57:42 -04:00
// On exit, also terminate the child process if applicable
process . on ( "exit" , function ( ) { if ( childProcess ) { childProcess . kill ( ) ; childProcess = null ; } } ) ;
// If our parent exits, we also exit
2019-07-17 20:34:34 -04:00
if ( args . launch ) {
process . stderr . on ( 'end' , function ( ) { process . exit ( ) ; } ) ;
process . stdout . on ( 'end' , function ( ) { process . exit ( ) ; } ) ;
process . stdin . on ( 'end' , function ( ) { process . exit ( ) ; } ) ;
process . stdin . on ( 'data' , function ( data ) { } ) ;
}
2018-03-14 18:16:55 -04:00
} ) ;
}
if ( require . main === module ) {
2019-05-17 18:44:01 -04:00
mainStart ( ) ; // Called directly, launch normally.
2018-03-14 18:16:55 -04:00
} else {
module . exports . mainStart = mainStart ; // Required as a module, useful for winservice.js
2019-03-25 14:32:16 -04:00
}