2017-08-28 19:27:45 +03:00
/ * *
2018-01-04 23:15:21 +03:00
* @ description MeshCentral main module
2017-08-28 19:27:45 +03:00
* @ author Ylian Saint - Hilaire
2022-01-24 10:21:24 +03:00
* @ copyright Intel Corporation 2018 - 2022
2018-01-04 23:15:21 +03:00
* @ license Apache - 2.0
2017-08-28 19:27:45 +03:00
* @ version v0 . 0.1
* /
2018-08-30 22:05:23 +03:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 22:24:15 +03:00
2021-07-29 17:38:44 +03:00
const common = require ( './common.js' ) ;
2018-01-10 07:13:41 +03:00
// If app metrics is available
2022-02-04 06:11:35 +03:00
if ( process . argv [ 2 ] == '--launch' ) { try { require ( 'appmetrics-dash' ) . monitor ( { url : '/' , title : 'MeshCentral' , port : 88 , host : '127.0.0.1' } ) ; } catch ( ex ) { } }
2018-01-10 07:13:41 +03:00
2018-03-15 01:16:55 +03:00
function CreateMeshCentralServer ( config , args ) {
2022-04-14 23:07:05 +03:00
const obj = { } ;
2018-08-30 22:05:23 +03:00
obj . db = null ;
2022-10-22 17:23:55 +03:00
obj . webserver = null ; // HTTPS main web server, typically on port 443
obj . redirserver = null ; // HTTP relay web server, typically on port 80
obj . mpsserver = null ; // Intel AMT CIRA server, typically on port 4433
obj . mqttbroker = null ; // MQTT server, not is not often used
obj . swarmserver = null ; // Swarm server, this is used only to update older MeshCentral v1 agents
obj . smsserver = null ; // SMS server, used to send user SMS messages
obj . msgserver = null ; // Messaging server, used to sent used messages
2018-08-30 22:05:23 +03:00
obj . amtEventHandler = null ;
2019-10-10 21:13:25 +03:00
obj . pluginHandler = null ;
2018-08-30 22:05:23 +03:00
obj . amtScanner = null ;
2022-10-22 17:23:55 +03:00
obj . amtManager = null ; // Intel AMT manager, used to oversee all Intel AMT devices, activate them and sync policies
2018-08-30 22:05:23 +03:00
obj . meshScanner = null ;
2022-08-19 04:03:03 +03:00
obj . taskManager = null ;
2022-10-22 17:23:55 +03:00
obj . letsencrypt = null ; // Let's encrypt server, used to get and renew TLS certificates
2017-08-28 19:27:45 +03:00
obj . eventsDispatch = { } ;
obj . fs = require ( 'fs' ) ;
obj . path = require ( 'path' ) ;
obj . crypto = require ( 'crypto' ) ;
2018-01-20 05:04:54 +03:00
obj . exeHandler = require ( './exeHandler.js' ) ;
2017-08-28 19:27:45 +03:00
obj . platform = require ( 'os' ) . platform ( ) ;
2018-03-15 01:16:55 +03:00
obj . args = args ;
2021-07-29 17:38:44 +03:00
obj . common = common ;
2019-02-03 01:54:36 +03:00
obj . configurationFiles = null ;
2017-08-28 19:27:45 +03: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 21:25:57 +03: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-23 01:31:39 +03:00
obj . debugSources = [ ] ;
2019-08-23 21:51:48 +03:00
obj . debugRemoteSources = null ;
2018-02-01 03:10:15 +03:00
obj . config = config ; // Configuration file
2017-08-28 19:27:45 +03:00
obj . dbconfig = { } ; // Persistance values, loaded from database
2018-01-10 07:13:41 +03:00
obj . certificateOperations = null ;
2017-12-13 03:04:54 +03:00
obj . defaultMeshCmd = null ;
2018-12-30 02:24:33 +03:00
obj . defaultMeshCores = { } ;
2020-07-17 21:14:24 +03:00
obj . defaultMeshCoresDeflate = { } ;
2018-12-30 02:24:33 +03:00
obj . defaultMeshCoresHash = { } ;
2020-11-03 08:41:58 +03:00
obj . meshToolsBinaries = { } ; // Mesh Tools Binaries, ToolName --> { hash:(sha384 hash), size:(binary size), path:(binary path) }
2017-10-15 09:22:19 +03: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 19:27:45 +03:00
obj . multiServer = null ;
2021-12-03 07:20:37 +03:00
obj . ipKvmManager = null ;
2017-09-07 04:10:24 +03:00
obj . maintenanceTimer = null ;
2017-09-13 21:25:57 +03:00
obj . serverId = null ;
2019-01-03 05:03:34 +03:00
obj . serverKey = Buffer . from ( obj . crypto . randomBytes ( 48 ) , 'binary' ) ;
2017-12-14 01:52:57 +03:00
obj . loginCookieEncryptionKey = null ;
2019-06-08 03:11:56 +03:00
obj . invitationLinkEncryptionKey = null ;
2018-09-27 00:58:55 +03:00
obj . serverSelfWriteAllowed = true ;
2019-03-27 00:11:51 +03:00
obj . serverStatsCounter = Math . floor ( Math . random ( ) * 1000 ) ;
2019-03-10 01:28:08 +03:00
obj . taskLimiter = obj . common . createTaskLimiterQueue ( 50 , 20 , 60 ) ; // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
2019-10-29 02:52:12 +03:00
obj . agentUpdateBlockSize = 65531 ; // MeshAgent update block size
2019-11-27 01:11:09 +03:00
obj . serverWarnings = [ ] ; // List of warnings that should be shown to administrators
2019-12-12 02:44:10 +03:00
obj . cookieUseOnceTable = { } ; // List of cookies that are already expired
obj . cookieUseOnceTableCleanCounter = 0 ; // Clean the cookieUseOnceTable each 20 additions
2020-03-22 03:33:09 +03:00
obj . firstStats = true ; // True until this server saves it's not stats to the database
2019-12-21 01:02:49 +03:00
// Server version
obj . currentVer = null ;
2022-02-04 06:11:35 +03:00
function getCurrentVersion ( ) { try { obj . currentVer = JSON . parse ( obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'package.json' ) , 'utf8' ) ) . version ; } catch ( ex ) { } return obj . currentVer ; } // Fetch server version
2021-05-03 11:58:49 +03:00
getCurrentVersion ( ) ;
2017-09-15 21:45:06 +03:00
2017-09-27 22:43:20 +03: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 19:21:21 +03:00
obj . parentpath = obj . path . join ( _ _dirname , '../..' ) ;
2017-10-15 09:22:19 +03:00
obj . datapath = obj . path . join ( _ _dirname , '../../meshcentral-data' ) ;
obj . filespath = obj . path . join ( _ _dirname , '../../meshcentral-files' ) ;
2022-12-10 08:38:56 +03:00
obj . backuppath = obj . path . join ( _ _dirname , '../../meshcentral-backups' ) ;
2019-08-05 21:30:07 +03:00
obj . recordpath = obj . path . join ( _ _dirname , '../../meshcentral-recordings' ) ;
2019-08-25 21:08:32 +03:00
obj . webViewsPath = obj . path . join ( _ _dirname , 'views' ) ;
2020-04-05 05:29:20 +03:00
obj . webPublicPath = obj . path . join ( _ _dirname , 'public' ) ;
obj . webEmailsPath = obj . path . join ( _ _dirname , 'emails' ) ;
2019-08-25 21:08:32 +03:00
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' ) ; }
2020-04-05 05:29:20 +03:00
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web/emails' ) ) ) { obj . webEmailsOverridePath = obj . path . join ( _ _dirname , '../../meshcentral-web/emails' ) ; }
2017-09-27 22:43:20 +03:00
} else {
2019-04-05 19:21:21 +03:00
obj . parentpath = _ _dirname ;
2017-10-15 09:22:19 +03:00
obj . datapath = obj . path . join ( _ _dirname , '../meshcentral-data' ) ;
obj . filespath = obj . path . join ( _ _dirname , '../meshcentral-files' ) ;
2019-05-17 22:40:15 +03:00
obj . backuppath = obj . path . join ( _ _dirname , '../meshcentral-backups' ) ;
2019-08-05 21:30:07 +03:00
obj . recordpath = obj . path . join ( _ _dirname , '../meshcentral-recordings' ) ;
2019-08-25 21:08:32 +03:00
obj . webViewsPath = obj . path . join ( _ _dirname , 'views' ) ;
2020-04-05 05:29:20 +03:00
obj . webPublicPath = obj . path . join ( _ _dirname , 'public' ) ;
obj . webEmailsPath = obj . path . join ( _ _dirname , 'emails' ) ;
2019-08-25 21:08:32 +03:00
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' ) ; }
2020-04-05 05:29:20 +03:00
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web/emails' ) ) ) { obj . webEmailsOverridePath = obj . path . join ( _ _dirname , '../meshcentral-web/emails' ) ; }
2017-09-27 22:43:20 +03:00
}
2020-11-08 12:13:23 +03:00
// Clean up any temporary files
2022-04-14 23:07:05 +03:00
const removeTime = new Date ( Date . now ( ) ) . getTime ( ) - ( 30 * 60 * 1000 ) ; // 30 minutes
const dir = obj . fs . readdir ( obj . path . join ( obj . filespath , 'tmp' ) , function ( err , files ) {
2020-11-08 12:13:23 +03:00
if ( err != null ) return ;
for ( var i in files ) { try { const filepath = obj . path . join ( obj . filespath , 'tmp' , files [ i ] ) ; if ( obj . fs . statSync ( filepath ) . mtime . getTime ( ) < removeTime ) { obj . fs . unlink ( filepath , function ( ) { } ) ; } } catch ( ex ) { } }
} ) ;
2019-08-13 00:58:06 +03:00
// Look to see if data and/or file path is specified
2019-10-29 03:18:39 +03:00
if ( obj . config . settings && ( typeof obj . config . settings . datapath == 'string' ) ) { obj . datapath = obj . config . settings . datapath ; }
if ( obj . config . settings && ( typeof obj . config . settings . filespath == 'string' ) ) { obj . filespath = obj . config . settings . filespath ; }
2019-08-13 00:58:06 +03:00
2017-08-28 19:27:45 +03:00
// Create data and files folders if needed
2022-02-04 06:11:35 +03:00
try { obj . fs . mkdirSync ( obj . datapath ) ; } catch ( ex ) { }
try { obj . fs . mkdirSync ( obj . filespath ) ; } catch ( ex ) { }
2017-08-28 19:27:45 +03:00
// Windows Specific Code, setup service and event log
obj . service = null ;
obj . servicelog = null ;
if ( obj . platform == 'win32' ) {
2022-04-14 23:07:05 +03:00
const nodewindows = require ( 'node-windows' ) ;
2017-08-28 19:27:45 +03:00
obj . service = nodewindows . Service ;
2022-04-14 23:07:05 +03:00
const eventlogger = nodewindows . EventLogger ;
2017-08-28 19:27:45 +03:00
obj . servicelog = new eventlogger ( 'MeshCentral' ) ;
}
// Start the Meshcentral server
obj . Start = function ( ) {
2018-08-30 22:05:23 +03:00
var i ;
2022-02-04 06:11:35 +03:00
try { require ( './pass' ) . hash ( 'test' , function ( ) { } , 0 ) ; } catch ( ex ) { console . log ( 'Old version of node, must upgrade.' ) ; return ; } // TODO: Not sure if this test works or not.
2018-07-24 03:34:24 +03:00
2017-08-28 19:27:45 +03:00
// Check for invalid arguments
2024-04-18 19:07:01 +03:00
const validArguments = [ '_' , 'user' , 'port' , 'aliasport' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'rediraliasport' , 'cert' , 'mpscert' , 'deletedomain' , 'deletedefaultdomain' , 'showall' , 'showusers' , 'showitem' , 'listuserids' , 'showusergroups' , 'shownodes' , 'showallmeshes' , 'showmeshes' , 'showevents' , 'showsmbios' , 'showpower' , 'clearpower' , 'showiplocations' , 'help' , 'exactports' , 'xinstall' , 'xuninstall' , 'install' , 'uninstall' , 'start' , 'stop' , 'restart' , 'debug' , 'filespath' , 'datapath' , 'noagentupdate' , 'launch' , 'noserverbackup' , 'mongodb' , 'mongodbcol' , 'wanonly' , 'lanonly' , 'nousers' , 'mpspass' , 'ciralocalfqdn' , 'dbexport' , 'dbexportmin' , 'dbimport' , 'dbmerge' , 'dbfix' , 'dbencryptkey' , 'selfupdate' , 'tlsoffload' , 'usenodedefaulttlsciphers' , 'tlsciphers' , 'userallowedip' , 'userblockedip' , 'swarmallowedip' , 'agentallowedip' , 'agentblockedip' , 'fastcert' , 'swarmport' , 'logintoken' , 'logintokenkey' , 'logintokengen' , 'mailtokengen' , 'admin' , 'unadmin' , 'sessionkey' , 'sessiontime' , 'minify' , 'minifycore' , 'dblistconfigfiles' , 'dbshowconfigfile' , 'dbpushconfigfiles' , 'dbpullconfigfiles' , 'dbdeleteconfigfiles' , 'vaultpushconfigfiles' , 'vaultpullconfigfiles' , 'vaultdeleteconfigfiles' , 'configkey' , 'loadconfigfromdb' , 'npmpath' , 'serverid' , 'recordencryptionrecode' , 'vault' , 'token' , 'unsealkey' , 'name' , 'log' , 'dbstats' , 'translate' , 'createaccount' , 'setuptelegram' , 'resetaccount' , 'pass' , 'removesubdomain' , 'adminaccount' , 'domain' , 'email' , 'configfile' , 'maintenancemode' , 'nedbtodb' , 'removetestagents' , 'agentupdatetest' , 'hashpassword' , 'hashpass' , 'indexmcrec' , 'mpsdebug' , 'dumpcores' , 'dev' , 'mysql' , 'mariadb' ] ;
2017-09-27 22:43:20 +03: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 19:27:45 +03: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 ; }
2024-04-18 19:07:01 +03:00
if ( obj . args . mysql == true ) { console . log ( 'Must specify: --mysql [connectionstring] \r\nExample mysql://user:password@127.0.0.1:3306/database' ) ; return ; }
if ( obj . args . mariadb == true ) { console . log ( 'Must specify: --mariadb [connectionstring] \r\nExample mariadb://user:password@127.0.0.1:3306/database' ) ; return ; }
2018-08-30 22:05:23 +03: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 19:27:45 +03:00
if ( ( obj . args . help == true ) || ( obj . args [ '?' ] == true ) ) {
2021-05-03 11:58:49 +03:00
console . log ( 'MeshCentral v' + getCurrentVersion ( ) + ', remote computer management web portal.' ) ;
2020-10-16 21:57:29 +03:00
console . log ( 'This software is open source under Apache 2.0 license.' ) ;
2022-06-28 01:22:44 +03:00
console . log ( 'Details at: https://www.meshcentral.com\r\n' ) ;
2020-01-10 03:35:48 +03:00
if ( ( obj . platform == 'win32' ) || ( obj . platform == 'linux' ) ) {
console . log ( 'Run as a background service' ) ;
console . log ( ' --install/uninstall Install MeshCentral as a background service.' ) ;
console . log ( ' --start/stop/restart Control MeshCentral background service.' ) ;
console . log ( '' ) ;
2017-08-28 19:27:45 +03:00
console . log ( 'Run standalone, console application' ) ;
}
console . log ( ' --user [username] Always login as [username] if account exists.' ) ;
console . log ( ' --port [number] Web 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.' ) ;
2021-01-05 06:14:06 +03:00
console . log ( ' --nedbtodb Transfer all NeDB records into current database.' ) ;
2020-03-22 21:57:52 +03:00
console . log ( ' --listuserids Show a list of a user identifiers in the database.' ) ;
2020-05-27 03:49:42 +03:00
console . log ( ' --cert [name], (country), (org) Create a web server certificate with [name] server name.' ) ;
2020-10-16 21:57:29 +03:00
console . log ( ' country and organization can optionally be set.' ) ;
2020-05-27 03:49:42 +03:00
console . log ( '' ) ;
console . log ( 'Server recovery commands, use only when MeshCentral is offline.' ) ;
2020-05-26 22:46:48 +03:00
console . log ( ' --createaccount [userid] Create a new user account.' ) ;
console . log ( ' --resetaccount [userid] Unlock an account, disable 2FA and set a new account password.' ) ;
console . log ( ' --adminaccount [userid] Promote account to site administrator.' ) ;
2017-08-28 19:27:45 +03:00
return ;
}
2018-08-30 22:05:23 +03:00
2020-08-24 23:26:27 +03:00
// Fix a NeDB database
if ( obj . args . dbfix ) {
2022-07-20 01:46:23 +03:00
var lines = null , badJsonCount = 0 , fieldNames = [ ] , fixedDb = [ ] ;
2022-02-04 06:11:35 +03:00
try { lines = obj . fs . readFileSync ( obj . getConfigFilePath ( obj . args . dbfix ) , { encoding : 'utf8' } ) . split ( '\n' ) ; } catch ( ex ) { console . log ( 'Invalid file: ' + obj . args . dbfix + ': ' + ex ) ; process . exit ( ) ; }
2020-08-24 23:26:27 +03:00
for ( var i = 0 ; i < lines . length ; i ++ ) {
var x = null ;
try { x = JSON . parse ( lines [ i ] ) ; } catch ( ex ) { badJsonCount ++ ; }
2022-07-20 01:46:23 +03:00
if ( x != null ) { fixedDb . push ( lines [ i ] ) ; for ( var j in x ) { if ( fieldNames . indexOf ( j ) == - 1 ) { fieldNames . push ( j ) ; } } }
2020-08-24 23:26:27 +03:00
}
2022-07-20 01:46:23 +03:00
console . log ( 'Lines: ' + lines . length + ', badJSON: ' + badJsonCount + ', Feilds: ' + fieldNames ) ;
2020-08-24 23:26:27 +03:00
obj . fs . writeFileSync ( obj . getConfigFilePath ( obj . args . dbfix ) + '-fixed' , fixedDb . join ( '\n' ) , { encoding : 'utf8' } ) ;
return ;
}
2021-06-28 22:49:50 +03:00
// Check for invalid cert name
if ( ( obj . args . cert != null ) && ( ( typeof obj . args . cert != "string" ) || ( obj . args . cert . indexOf ( '@' ) >= 0 ) || ( obj . args . cert . indexOf ( '/' ) >= 0 ) || ( obj . args . cert . indexOf ( ':' ) >= 0 ) ) ) { console . log ( "Invalid certificate name" ) ; process . exit ( ) ; return ; }
2021-02-17 11:24:49 +03:00
// Perform a password hash
if ( obj . args . hashpassword ) { require ( './pass' ) . hash ( obj . args . hashpassword , function ( err , salt , hash , tag ) { console . log ( salt + ',' + hash ) ; process . exit ( ) ; } ) ; return ; }
2022-02-04 06:11:35 +03:00
// Dump to mesh cores
if ( obj . args . dumpcores ) { obj . updateMeshCore ( function ( ) { console . log ( 'Done.' ) ; } , true ) ; return ; }
2022-10-23 00:51:16 +03:00
// Setup Telegram
if ( obj . args . setuptelegram ) { require ( './meshmessaging.js' ) . SetupTelegram ( obj ) ; return ; }
2020-01-26 22:59:38 +03:00
// Perform web site translations into different languages
if ( obj . args . translate ) {
2020-01-28 04:52:20 +03:00
// Check NodeJS version
const NodeJSVer = Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) ;
if ( NodeJSVer < 8 ) { console . log ( "Translation feature requires Node v8 or above, current version is " + process . version + "." ) ; process . exit ( ) ; return ; }
2020-01-26 22:59:38 +03:00
// Check if translate.json is in the "meshcentral-data" folder, if so use that and translate default pages.
2020-01-28 04:52:20 +03:00
var translationFile = null , customTranslation = false ;
2022-07-20 00:52:30 +03:00
if ( require ( 'fs' ) . existsSync ( obj . path . join ( obj . datapath , 'translate.json' ) ) ) { translationFile = obj . path . join ( obj . datapath , 'translate.json' ) ; console . log ( "Using translate.json in meshcentral-data." ) ; customTranslation = true ; }
2020-01-28 04:52:20 +03:00
if ( translationFile == null ) { if ( require ( 'fs' ) . existsSync ( obj . path . join ( _ _dirname , 'translate' , 'translate.json' ) ) ) { translationFile = obj . path . join ( _ _dirname , 'translate' , 'translate.json' ) ; console . log ( "Using default translate.json." ) ; } }
if ( translationFile == null ) { console . log ( "Unable to find translate.json." ) ; process . exit ( ) ; return ; }
2020-01-26 22:59:38 +03:00
2020-01-28 04:52:20 +03:00
// Perform translation operations
var didSomething = false ;
2020-02-05 03:48:54 +03:00
process . chdir ( obj . path . join ( _ _dirname , 'translate' ) ) ;
2022-04-14 23:07:05 +03:00
const translateEngine = require ( './translate/translate.js' )
2020-01-28 04:52:20 +03:00
if ( customTranslation == true ) {
// Translate all of the default files using custom translation file
translateEngine . startEx ( [ '' , '' , 'minifyall' ] ) ;
translateEngine . startEx ( [ '' , '' , 'translateall' , translationFile ] ) ;
translateEngine . startEx ( [ '' , '' , 'extractall' , translationFile ] ) ;
didSomething = true ;
2024-04-15 15:00:42 +03:00
} else {
// Translate all of the default files
translateEngine . startEx ( [ '' , '' , 'minifyall' ] ) ;
translateEngine . startEx ( [ '' , '' , 'translateall' ] ) ;
translateEngine . startEx ( [ '' , '' , 'extractall' ] ) ;
didSomething = true ;
2020-01-28 04:52:20 +03:00
}
2020-01-26 22:59:38 +03:00
2020-03-25 23:21:14 +03:00
// Check if "meshcentral-web" exists, if so, translate all pages in that folder.
2020-01-28 04:52:20 +03:00
if ( obj . webViewsOverridePath != null ) {
didSomething = true ;
var files = obj . fs . readdirSync ( obj . webViewsOverridePath ) ;
for ( var i in files ) {
var file = obj . path . join ( obj . webViewsOverridePath , files [ i ] ) ;
if ( file . endsWith ( '.handlebars' ) && ! file . endsWith ( '-min.handlebars' ) ) {
2020-02-06 22:33:21 +03:00
translateEngine . startEx ( [ '' , '' , 'minify' , file ] ) ;
}
}
files = obj . fs . readdirSync ( obj . webViewsOverridePath ) ;
for ( var i in files ) {
var file = obj . path . join ( obj . webViewsOverridePath , files [ i ] ) ;
2024-04-15 15:00:42 +03:00
if ( file . endsWith ( '.handlebars' ) && ! file . endsWith ( '-min.handlebars' ) ) {
2020-01-28 04:52:20 +03:00
translateEngine . startEx ( [ '' , '' , 'translate' , '*' , translationFile , file , '--subdir:translations' ] ) ;
}
}
}
2024-04-15 15:00:42 +03:00
// Check domains and see if "meshcentral-web-DOMAIN" exists, if so, translate all pages in that folder
for ( i in obj . config . domains ) {
if ( i == "" ) continue ;
var path = obj . path . join ( obj . datapath , '..' , 'meshcentral-web-' + i , 'views' ) ;
if ( require ( 'fs' ) . existsSync ( path ) ) {
didSomething = true ;
var files = obj . fs . readdirSync ( path ) ;
for ( var a in files ) {
var file = obj . path . join ( path , files [ a ] ) ;
if ( file . endsWith ( '.handlebars' ) && ! file . endsWith ( '-min.handlebars' ) ) {
translateEngine . startEx ( [ '' , '' , 'minify' , file ] ) ;
}
}
files = obj . fs . readdirSync ( path ) ;
for ( var a in files ) {
var file = obj . path . join ( path , files [ a ] ) ;
if ( file . endsWith ( '.handlebars' ) && ! file . endsWith ( '-min.handlebars' ) ) {
translateEngine . startEx ( [ '' , '' , 'translate' , '*' , translationFile , file , '--subdir:translations' ] ) ;
}
}
}
}
2020-02-06 22:33:21 +03:00
/ *
2020-01-28 04:52:20 +03:00
if ( obj . webPublicOverridePath != null ) {
didSomething = true ;
var files = obj . fs . readdirSync ( obj . webPublicOverridePath ) ;
for ( var i in files ) {
var file = obj . path . join ( obj . webPublicOverridePath , files [ i ] ) ;
if ( file . endsWith ( '.htm' ) && ! file . endsWith ( '-min.htm' ) ) {
translateEngine . startEx ( [ '' , '' , 'translate' , '*' , translationFile , file , '--subdir:translations' ] ) ;
}
}
}
2020-02-06 22:33:21 +03:00
* /
2020-01-26 22:59:38 +03:00
2020-01-28 04:52:20 +03:00
if ( didSomething == false ) { console . log ( "Nothing to do." ) ; }
2024-04-15 15:00:42 +03:00
console . log ( 'Finished Translating.' )
2020-01-28 04:52:20 +03:00
process . exit ( ) ;
2020-01-26 22:59:38 +03:00
return ;
}
2020-02-11 00:17:24 +03:00
// Setup the Node+NPM path if possible, this makes it possible to update the server even if NodeJS and NPM are not in default paths.
if ( obj . args . npmpath == null ) {
try {
var nodepath = process . argv [ 0 ] ;
var npmpath = obj . path . join ( obj . path . dirname ( process . argv [ 0 ] ) , 'npm' ) ;
if ( obj . fs . existsSync ( nodepath ) && obj . fs . existsSync ( npmpath ) ) {
if ( nodepath . indexOf ( ' ' ) >= 0 ) { nodepath = '"' + nodepath + '"' ; }
if ( npmpath . indexOf ( ' ' ) >= 0 ) { npmpath = '"' + npmpath + '"' ; }
if ( obj . platform == 'win32' ) { obj . args . npmpath = npmpath ; } else { obj . args . npmpath = ( nodepath + ' ' + npmpath ) ; }
}
} catch ( ex ) { }
}
2020-01-10 03:35:48 +03:00
// Linux background service systemd handling
if ( obj . platform == 'linux' ) {
if ( obj . args . install == true ) {
// Install MeshCentral in Systemd
console . log ( 'Installing MeshCentral as background Service...' ) ;
2022-04-14 23:07:05 +03:00
var systemdConf = null ;
const userinfo = require ( 'os' ) . userInfo ( ) ;
2020-01-10 03:35:48 +03:00
if ( require ( 'fs' ) . existsSync ( '/etc/systemd/system' ) ) { systemdConf = '/etc/systemd/system/meshcentral.service' ; }
else if ( require ( 'fs' ) . existsSync ( '/lib/systemd/system' ) ) { systemdConf = '/lib/systemd/system/meshcentral.service' ; }
else if ( require ( 'fs' ) . existsSync ( '/usr/lib/systemd/system' ) ) { systemdConf = '/usr/lib/systemd/system/meshcentral.service' ; }
else { console . log ( 'Unable to find systemd configuration folder.' ) ; process . exit ( ) ; return ; }
console . log ( 'Writing config file...' ) ;
2020-01-13 21:34:10 +03:00
require ( 'child_process' ) . exec ( 'which node' , { } , function ( error , stdout , stderr ) {
if ( ( error != null ) || ( stdout . indexOf ( '\n' ) == - 1 ) ) { console . log ( 'ERROR: Unable to get node location: ' + error ) ; process . exit ( ) ; return ; }
2022-04-14 23:07:05 +03:00
const nodePath = stdout . substring ( 0 , stdout . indexOf ( '\n' ) ) ;
2023-08-29 23:05:30 +03:00
const config = '[Unit]\nDescription=MeshCentral Server\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nLimitNOFILE=1000000\nExecStart=' + nodePath + ' ' + _ _dirname + '/meshcentral\nWorkingDirectory=' + userinfo . homedir + '\nEnvironment=NODE_ENV=production\nUser=' + userinfo . username + '\nGroup=' + userinfo . username + '\nRestart=always\n# Restart service after 10 seconds if node service crashes\nRestartSec=10\n# Set port permissions capability\nAmbientCapabilities=cap_net_bind_service\n\n[Install]\nWantedBy=multi-user.target\n' ;
2020-01-14 16:17:12 +03:00
require ( 'child_process' ) . exec ( 'echo \"' + config + '\" | sudo tee ' + systemdConf , { } , function ( error , stdout , stderr ) {
2020-01-10 03:35:48 +03:00
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to write config file: ' + error ) ; process . exit ( ) ; return ; }
console . log ( 'Enabling service...' ) ;
require ( 'child_process' ) . exec ( 'sudo systemctl enable meshcentral.service' , { } , function ( error , stdout , stderr ) {
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to enable MeshCentral as a service: ' + error ) ; process . exit ( ) ; return ; }
if ( stdout . length > 0 ) { console . log ( stdout ) ; }
console . log ( 'Starting service...' ) ;
require ( 'child_process' ) . exec ( 'sudo systemctl start meshcentral.service' , { } , function ( error , stdout , stderr ) {
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to start MeshCentral as a service: ' + error ) ; process . exit ( ) ; return ; }
if ( stdout . length > 0 ) { console . log ( stdout ) ; }
console . log ( 'Done.' ) ;
} ) ;
} ) ;
} ) ;
} ) ;
return ;
} else if ( obj . args . uninstall == true ) {
// Uninstall MeshCentral in Systemd
console . log ( 'Uninstalling MeshCentral background service...' ) ;
var systemdConf = null ;
if ( require ( 'fs' ) . existsSync ( '/etc/systemd/system' ) ) { systemdConf = '/etc/systemd/system/meshcentral.service' ; }
else if ( require ( 'fs' ) . existsSync ( '/lib/systemd/system' ) ) { systemdConf = '/lib/systemd/system/meshcentral.service' ; }
else if ( require ( 'fs' ) . existsSync ( '/usr/lib/systemd/system' ) ) { systemdConf = '/usr/lib/systemd/system/meshcentral.service' ; }
else { console . log ( 'Unable to find systemd configuration folder.' ) ; process . exit ( ) ; return ; }
console . log ( 'Stopping service...' ) ;
require ( 'child_process' ) . exec ( 'sudo systemctl stop meshcentral.service' , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to stop MeshCentral as a service: ' + err ) ; }
if ( stdout . length > 0 ) { console . log ( stdout ) ; }
console . log ( 'Disabling service...' ) ;
require ( 'child_process' ) . exec ( 'sudo systemctl disable meshcentral.service' , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to disable MeshCentral as a service: ' + err ) ; }
if ( stdout . length > 0 ) { console . log ( stdout ) ; }
console . log ( 'Removing config file...' ) ;
require ( 'child_process' ) . exec ( 'sudo rm ' + systemdConf , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to delete MeshCentral config file: ' + err ) ; }
console . log ( 'Done.' ) ;
} ) ;
} ) ;
} ) ;
return ;
} else if ( obj . args . start == true ) {
// Start MeshCentral in Systemd
require ( 'child_process' ) . exec ( 'sudo systemctl start meshcentral.service' , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to start MeshCentral: ' + err ) ; process . exit ( ) ; return ; }
console . log ( 'Done.' ) ;
} ) ;
2020-01-10 03:41:14 +03:00
return ;
2020-01-10 03:35:48 +03:00
} else if ( obj . args . stop == true ) {
// Stop MeshCentral in Systemd
require ( 'child_process' ) . exec ( 'sudo systemctl stop meshcentral.service' , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to stop MeshCentral: ' + err ) ; process . exit ( ) ; return ; }
console . log ( 'Done.' ) ;
} ) ;
2020-01-10 03:41:14 +03:00
return ;
2020-01-10 03:35:48 +03:00
} else if ( obj . args . restart == true ) {
// Restart MeshCentral in Systemd
require ( 'child_process' ) . exec ( 'sudo systemctl restart meshcentral.service' , { } , function ( err , stdout , stderr ) {
if ( ( err != null ) && ( err != '' ) ) { console . log ( 'ERROR: Unable to restart MeshCentral: ' + err ) ; process . exit ( ) ; return ; }
console . log ( 'Done.' ) ;
} ) ;
2020-01-10 03:41:14 +03:00
return ;
2020-01-10 03:35:48 +03:00
}
}
2021-06-22 18:24:06 +03:00
// Index a recorded file
if ( obj . args . indexmcrec != null ) {
if ( typeof obj . args . indexmcrec != 'string' ) {
console . log ( 'Usage: --indexmrec [filename.mcrec]' ) ;
} else if ( obj . fs . existsSync ( obj . args . indexmcrec ) ) {
console . log ( 'Indexing file: ' + obj . args . indexmcrec ) ;
require ( require ( 'path' ) . join ( _ _dirname , 'mcrec.js' ) ) . indexFile ( obj . args . indexmcrec ) ;
} else {
console . log ( 'Unable to find file: ' + obj . args . indexmcrec ) ;
}
return ;
}
2020-01-10 03:35:48 +03:00
// Windows background service handling
if ( ( obj . platform == 'win32' ) && ( obj . service != null ) ) {
2020-11-28 23:51:23 +03:00
// Build MeshCentral parent path and Windows Service path
var mcpath = _ _dirname ;
if ( mcpath . endsWith ( '\\node_modules\\meshcentral' ) || mcpath . endsWith ( '/node_modules/meshcentral' ) ) { mcpath = require ( 'path' ) . join ( mcpath , '..' , '..' ) ; }
2022-04-14 23:07:05 +03:00
const servicepath = obj . path . join ( mcpath , 'WinService' ) ;
2020-11-28 23:51:23 +03:00
2019-11-22 05:03:02 +03:00
// Check if we need to install, start, stop, remove ourself as a background service
if ( ( ( obj . args . xinstall == true ) || ( obj . args . xuninstall == true ) || ( obj . args . start == true ) || ( obj . args . stop == true ) || ( obj . args . restart == true ) ) ) {
var env = [ ] , xenv = [ 'user' , 'port' , 'aliasport' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'exactport' , 'rediraliasport' , 'debug' ] ;
2022-07-20 00:52:30 +03: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 environment variables.
2020-11-28 23:51:23 +03:00
var serviceFilePath = null ;
if ( obj . fs . existsSync ( obj . path . join ( servicepath , 'winservice.js' ) ) ) { serviceFilePath = obj . path . join ( servicepath , 'winservice.js' ) ; }
else if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../WinService/winservice.js' ) ) ) { serviceFilePath = obj . path . join ( _ _dirname , '../WinService/winservice.js' ) ; }
else if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'winservice.js' ) ) ) { serviceFilePath = obj . path . join ( _ _dirname , 'winservice.js' ) ; }
if ( serviceFilePath == null ) { console . log ( 'Unable to find winservice.js' ) ; return ; }
2022-04-14 23:07:05 +03:00
const svc = new obj . service ( { name : 'MeshCentral' , description : 'MeshCentral Remote Management Server' , script : servicepath , env : env , wait : 2 , grow : 0.5 } ) ;
2019-11-22 05:03:02 +03:00
svc . on ( 'install' , function ( ) { console . log ( 'MeshCentral service installed.' ) ; svc . start ( ) ; } ) ;
svc . on ( 'uninstall' , function ( ) { console . log ( 'MeshCentral service uninstalled.' ) ; process . exit ( ) ; } ) ;
svc . on ( 'start' , function ( ) { console . log ( 'MeshCentral service started.' ) ; process . exit ( ) ; } ) ;
svc . on ( 'stop' , function ( ) { console . log ( 'MeshCentral service stopped.' ) ; if ( obj . args . stop ) { process . exit ( ) ; } if ( obj . args . restart ) { console . log ( 'Holding 5 seconds...' ) ; setTimeout ( function ( ) { svc . start ( ) ; } , 5000 ) ; } } ) ;
svc . on ( 'alreadyinstalled' , function ( ) { console . log ( 'MeshCentral service already installed.' ) ; process . exit ( ) ; } ) ;
svc . on ( 'invalidinstallation' , function ( ) { console . log ( 'Invalid MeshCentral service installation.' ) ; process . exit ( ) ; } ) ;
2018-08-30 22:05:23 +03:00
2022-02-04 06:11:35 +03:00
if ( obj . args . xinstall == true ) { try { svc . install ( ) ; } catch ( ex ) { logException ( ex ) ; } }
if ( obj . args . stop == true || obj . args . restart == true ) { try { svc . stop ( ) ; } catch ( ex ) { logException ( ex ) ; } }
if ( obj . args . start == true ) { try { svc . start ( ) ; } catch ( ex ) { logException ( ex ) ; } }
if ( obj . args . xuninstall == true ) { try { svc . uninstall ( ) ; } catch ( ex ) { logException ( ex ) ; } }
2019-11-22 05:03:02 +03:00
return ;
}
// Windows service install using the external winservice.js
if ( obj . args . install == true ) {
console . log ( 'Installing MeshCentral as Windows Service...' ) ;
2020-11-28 23:51:23 +03:00
if ( obj . fs . existsSync ( servicepath ) == false ) { try { obj . fs . mkdirSync ( servicepath ) ; } catch ( ex ) { console . log ( 'ERROR: Unable to create WinService folder: ' + ex ) ; process . exit ( ) ; return ; } }
try { obj . fs . createReadStream ( obj . path . join ( _ _dirname , 'winservice.js' ) ) . pipe ( obj . fs . createWriteStream ( obj . path . join ( servicepath , 'winservice.js' ) ) ) ; } catch ( ex ) { console . log ( 'ERROR: Unable to copy winservice.js: ' + ex ) ; process . exit ( ) ; return ; }
require ( 'child_process' ) . exec ( 'node winservice.js --install' , { maxBuffer : 512000 , timeout : 120000 , cwd : servicepath } , function ( error , stdout , stderr ) {
2019-11-22 05:03:02 +03:00
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to install MeshCentral as a service: ' + error ) ; process . exit ( ) ; return ; }
console . log ( stdout ) ;
} ) ;
return ;
} else if ( obj . args . uninstall == true ) {
console . log ( 'Uninstalling MeshCentral Windows Service...' ) ;
2020-11-28 23:51:23 +03:00
if ( obj . fs . existsSync ( servicepath ) == true ) {
require ( 'child_process' ) . exec ( 'node winservice.js --uninstall' , { maxBuffer : 512000 , timeout : 120000 , cwd : servicepath } , function ( error , stdout , stderr ) {
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to uninstall MeshCentral service: ' + error ) ; process . exit ( ) ; return ; }
console . log ( stdout ) ;
try { obj . fs . unlinkSync ( obj . path . join ( servicepath , 'winservice.js' ) ) ; } catch ( ex ) { }
try { obj . fs . rmdirSync ( servicepath ) ; } catch ( ex ) { }
} ) ;
} else if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../WinService' ) ) == true ) {
2019-11-22 05:03:02 +03:00
require ( 'child_process' ) . exec ( 'node winservice.js --uninstall' , { maxBuffer : 512000 , timeout : 120000 , cwd : obj . path . join ( _ _dirname , '../WinService' ) } , function ( error , stdout , stderr ) {
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to uninstall MeshCentral service: ' + error ) ; process . exit ( ) ; return ; }
console . log ( stdout ) ;
try { obj . fs . unlinkSync ( obj . path . join ( _ _dirname , '../WinService/winservice.js' ) ) ; } catch ( ex ) { }
try { obj . fs . rmdirSync ( obj . path . join ( _ _dirname , '../WinService' ) ) ; } catch ( ex ) { }
} ) ;
} else {
require ( 'child_process' ) . exec ( 'node winservice.js --uninstall' , { maxBuffer : 512000 , timeout : 120000 , cwd : _ _dirname } , function ( error , stdout , stderr ) {
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to uninstall MeshCentral service: ' + error ) ; process . exit ( ) ; return ; }
console . log ( stdout ) ;
} ) ;
}
return ;
}
2017-08-28 19:27:45 +03:00
}
// If "--launch" is in the arguments, launch now
2019-07-18 01:57:42 +03:00
if ( obj . args . launch ) {
2019-10-14 23:35:27 +03:00
if ( obj . args . vault ) { obj . StartVault ( ) ; } else { obj . StartEx ( ) ; }
2017-08-28 19:27:45 +03:00
} else {
// if "--launch" is not specified, launch the server as a child process.
2022-04-14 23:07:05 +03:00
const startArgs = [ ] ;
2018-08-30 22:05:23 +03:00
for ( i in process . argv ) {
2019-07-18 03:34:34 +03:00
if ( i > 0 ) {
2022-04-14 23:07:05 +03:00
const arg = process . argv [ i ] ;
2019-07-24 00:09:44 +03:00
if ( ( arg . length > 0 ) && ( ( arg . indexOf ( ' ' ) >= 0 ) || ( arg . indexOf ( '&' ) >= 0 ) ) ) { startArgs . push ( arg ) ; } else { startArgs . push ( arg ) ; }
2017-08-28 19:27:45 +03:00
}
}
2019-07-18 03:34:34 +03:00
startArgs . push ( '--launch' , process . pid ) ;
obj . launchChildServer ( startArgs ) ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
// Launch MeshCentral as a child server and monitor it.
2019-07-18 03:34:34 +03:00
obj . launchChildServer = function ( startArgs ) {
2022-04-14 23:07:05 +03:00
const child _process = require ( 'child_process' ) ;
2022-04-14 21:47:04 +03:00
try { if ( process . traceDeprecation === true ) { startArgs . unshift ( '--trace-deprecation' ) ; } } catch ( ex ) { }
2023-11-07 17:42:02 +03:00
try { if ( process . traceProcessWarnings === true ) { startArgs . unshift ( '--trace-warnings' ) ; } } catch ( ex ) { }
2019-07-18 03:34:34 +03:00
childProcess = child _process . execFile ( process . argv [ 0 ] , startArgs , { maxBuffer : Infinity , cwd : obj . parentpath } , function ( error , stdout , stderr ) {
2019-07-18 01:57:42 +03:00
if ( childProcess . xrestart == 1 ) {
2019-07-18 20:42:22 +03:00
setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 500 ) ; // This is an expected restart.
2019-07-18 01:57:42 +03:00
} else if ( childProcess . xrestart == 2 ) {
2017-09-04 22:20:18 +03:00
console . log ( 'Expected exit...' ) ;
process . exit ( ) ; // User CTRL-C exit.
2019-07-18 01:57:42 +03:00
} else if ( childProcess . xrestart == 3 ) {
2017-09-07 04:10:24 +03:00
// Server self-update exit
2019-07-18 23:30:49 +03:00
var version = '' ;
if ( typeof obj . args . selfupdate == 'string' ) { version = '@' + obj . args . selfupdate ; }
2020-04-18 09:52:35 +03:00
else if ( typeof obj . args . specificupdate == 'string' ) { version = '@' + obj . args . specificupdate ; delete obj . args . specificupdate ; }
2022-04-14 23:07:05 +03:00
const child _process = require ( 'child_process' ) ;
const npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
const npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
const env = Object . assign ( { } , process . env ) ; // Shallow clone
2019-12-21 03:45:41 +03:00
if ( typeof obj . args . npmproxy == 'string' ) { env [ 'HTTP_PROXY' ] = env [ 'HTTPS_PROXY' ] = env [ 'http_proxy' ] = env [ 'https_proxy' ] = obj . args . npmproxy ; }
2024-01-13 21:52:59 +03:00
// always use --save-exact - https://stackoverflow.com/a/64507176/1210734
const xxprocess = child _process . exec ( npmpath + ' install --save-exact --no-audit meshcentral' + version + npmproxy , { maxBuffer : Infinity , cwd : obj . parentpath , env : env } , function ( error , stdout , stderr ) {
2021-04-19 01:11:52 +03:00
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'Update failed: ' + error ) ; }
2022-11-02 04:04:43 +03:00
} ) ;
2019-01-31 21:41:07 +03:00
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
2021-04-19 01:11:52 +03:00
xxprocess . on ( 'close' , function ( code ) {
if ( code == 0 ) { console . log ( 'Update completed...' ) ; }
2022-03-17 21:19:18 +03:00
// Run the server updated script if present
if ( typeof obj . config . settings . runonserverupdated == 'string' ) {
const child _process = require ( 'child_process' ) ;
var parentpath = _ _dirname ;
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 , '../..' ) ; }
child _process . exec ( obj . config . settings . runonserverupdated + ' ' + getCurrentVersion ( ) , { maxBuffer : 512000 , timeout : 120000 , cwd : parentpath } , function ( error , stdout , stderr ) { } ) ;
}
2021-10-29 09:00:42 +03:00
if ( obj . args . cleannpmcacheonupdate === true ) {
// Perform NPM cache clean
console . log ( 'Cleaning NPM cache...' ) ;
2022-04-14 23:07:05 +03:00
const xxxprocess = child _process . exec ( npmpath + ' cache clean --force' , { maxBuffer : Infinity , cwd : obj . parentpath , env : env } , function ( error , stdout , stderr ) { } ) ;
2021-10-29 09:00:42 +03:00
xxxprocess . on ( 'close' , function ( code ) { setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 1000 ) ; } ) ;
} else {
// Run the updated server
setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 1000 ) ;
}
2021-04-19 01:11:52 +03:00
} ) ;
2017-08-28 19:27:45 +03:00
} else {
2017-09-02 03:34:02 +03:00
if ( error != null ) {
2017-09-04 22:20:18 +03:00
// This is an un-expected restart
2017-09-13 21:25:57 +03:00
console . log ( error ) ;
2021-03-06 01:58:00 +03:00
console . log ( 'ERROR: MeshCentral failed with critical error, check mesherrors.txt. Restarting in 5 seconds...' ) ;
2019-07-18 20:42:22 +03:00
setTimeout ( function ( ) { obj . launchChildServer ( startArgs ) ; } , 5000 ) ;
2022-03-17 21:19:18 +03:00
// Run the server error script if present
if ( typeof obj . config . settings . runonservererror == 'string' ) {
const child _process = require ( 'child_process' ) ;
var parentpath = _ _dirname ;
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 , '../..' ) ; }
child _process . exec ( obj . config . settings . runonservererror + ' ' + getCurrentVersion ( ) , { maxBuffer : 512000 , timeout : 120000 , cwd : parentpath } , function ( error , stdout , stderr ) { } ) ;
}
2018-08-30 22:05:23 +03:00
}
2017-08-28 19:27:45 +03:00
}
} ) ;
2019-12-04 04:44:08 +03: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 ; }
2020-04-18 09:52:35 +03:00
else if ( data . indexOf ( 'Starting self upgrade to: ' ) >= 0 ) { obj . args . specificupdate = data . substring ( 26 ) . split ( '\r' ) [ 0 ] . split ( '\n' ) [ 0 ] ; childProcess . xrestart = 3 ; }
2019-12-04 04:44:08 +03:00
var datastr = data ;
while ( datastr . endsWith ( '\r' ) || datastr . endsWith ( '\n' ) ) { datastr = datastr . substring ( 0 , datastr . length - 1 ) ; }
console . log ( datastr ) ;
} ) ;
2019-07-18 01:57:42 +03:00
childProcess . stderr . on ( 'data' , function ( data ) {
2019-12-04 04:44:08 +03:00
var datastr = data ;
while ( datastr . endsWith ( '\r' ) || datastr . endsWith ( '\n' ) ) { datastr = datastr . substring ( 0 , datastr . length - 1 ) ; }
console . log ( 'ERR: ' + datastr ) ;
2018-01-15 08:01:06 +03:00
if ( data . startsWith ( 'le.challenges[tls-sni-01].loopback' ) ) { return ; } // Ignore this error output from GreenLock
2018-12-02 10:41:57 +03:00
if ( data [ data . length - 1 ] == '\n' ) { data = data . substring ( 0 , data . length - 1 ) ; }
2021-07-11 03:14:15 +03:00
obj . logError ( data ) ;
2018-01-15 08:01:06 +03:00
} ) ;
2019-07-18 01:57:42 +03:00
childProcess . on ( 'close' , function ( code ) { if ( ( code != 0 ) && ( code != 123 ) ) { /* console.log("Exited with code " + code); */ } } ) ;
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2021-07-11 03:14:15 +03:00
obj . logError = function ( err ) {
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 ( errlogpath , '-------- ' + new Date ( ) . toLocaleString ( ) + ' ---- ' + getCurrentVersion ( ) + ' --------\r\n\r\n' + err + '\r\n\r\n\r\n' ) ;
} catch ( ex ) { console . log ( 'ERROR: Unable to write to mesherrors.txt.' ) ; }
} ;
2017-08-28 19:27:45 +03:00
// Get current and latest MeshCentral server versions using NPM
obj . getLatestServerVersion = function ( callback ) {
2017-09-15 21:45:06 +03:00
if ( callback == null ) return ;
2019-03-08 06:56:24 +03:00
try {
2021-05-03 11:58:49 +03:00
if ( typeof obj . args . selfupdate == 'string' ) { callback ( getCurrentVersion ( ) , obj . args . selfupdate ) ; return ; } // If we are targetting a specific version, return that one as current.
2022-04-14 23:07:05 +03:00
const child _process = require ( 'child_process' ) ;
const npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
const npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
const env = Object . assign ( { } , process . env ) ; // Shallow clone
2019-12-21 03:45:41 +03:00
if ( typeof obj . args . npmproxy == 'string' ) { env [ 'HTTP_PROXY' ] = env [ 'HTTPS_PROXY' ] = env [ 'http_proxy' ] = env [ 'https_proxy' ] = obj . args . npmproxy ; }
2022-04-14 23:07:05 +03:00
const xxprocess = child _process . exec ( npmpath + npmproxy + ' view meshcentral dist-tags.latest' , { maxBuffer : 512000 , cwd : obj . parentpath , env : env } , function ( error , stdout , stderr ) { } ) ;
2019-07-18 01:57:42 +03:00
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { } ) ;
xxprocess . on ( 'close' , function ( code ) {
2019-03-08 06:56:24 +03:00
var latestVer = null ;
2022-02-04 06:11:35 +03:00
if ( code == 0 ) { try { latestVer = xxprocess . data . split ( ' ' ) . join ( '' ) . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; } catch ( ex ) { } }
2021-05-03 11:58:49 +03:00
callback ( getCurrentVersion ( ) , latestVer ) ;
2019-03-08 06:56:24 +03:00
} ) ;
2021-05-03 11:58:49 +03:00
} catch ( ex ) { callback ( getCurrentVersion ( ) , null , ex ) ; } // If the system is running out of memory, an exception here can easily happen.
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2020-04-18 09:17:45 +03:00
// Get current version and all MeshCentral server tags using NPM
obj . getServerTags = function ( callback ) {
if ( callback == null ) return ;
try {
2021-05-03 11:58:49 +03:00
if ( typeof obj . args . selfupdate == 'string' ) { callback ( { current : getCurrentVersion ( ) , latest : obj . args . selfupdate } ) ; return ; } // If we are targetting a specific version, return that one as current.
2022-04-14 23:07:05 +03:00
const child _process = require ( 'child_process' ) ;
const npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
const npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
const env = Object . assign ( { } , process . env ) ; // Shallow clone
2020-04-18 09:17:45 +03:00
if ( typeof obj . args . npmproxy == 'string' ) { env [ 'HTTP_PROXY' ] = env [ 'HTTPS_PROXY' ] = env [ 'http_proxy' ] = env [ 'https_proxy' ] = obj . args . npmproxy ; }
2022-04-14 23:07:05 +03:00
const xxprocess = child _process . exec ( npmpath + npmproxy + ' dist-tag ls meshcentral' , { maxBuffer : 512000 , cwd : obj . parentpath , env : env } , function ( error , stdout , stderr ) { } ) ;
2020-04-18 09:17:45 +03:00
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { } ) ;
xxprocess . on ( 'close' , function ( code ) {
2021-05-03 11:58:49 +03:00
var tags = { current : getCurrentVersion ( ) } ;
2020-04-18 09:17:45 +03:00
if ( code == 0 ) {
try {
var lines = xxprocess . data . split ( '\r\n' ) . join ( '\n' ) . split ( '\n' ) ;
for ( var i in lines ) { var s = lines [ i ] . split ( ': ' ) ; if ( ( s . length == 2 ) && ( obj . args . npmtag == null ) || ( obj . args . npmtag == s [ 0 ] ) ) { tags [ s [ 0 ] ] = s [ 1 ] ; } }
2022-02-04 06:11:35 +03:00
} catch ( ex ) { }
2020-04-18 09:17:45 +03:00
}
callback ( tags ) ;
} ) ;
2021-05-03 11:58:49 +03:00
} catch ( ex ) { callback ( { current : getCurrentVersion ( ) } , ex ) ; } // If the system is running out of memory, an exception here can easily happen.
2020-04-18 09:17:45 +03:00
} ;
2021-05-29 21:17:34 +03:00
// Use NPM to get list of versions
obj . getServerVersions = function ( callback ) {
try {
2022-04-14 23:07:05 +03:00
const child _process = require ( 'child_process' ) ;
const npmpath = ( ( typeof obj . args . npmpath == 'string' ) ? obj . args . npmpath : 'npm' ) ;
const npmproxy = ( ( typeof obj . args . npmproxy == 'string' ) ? ( ' --proxy ' + obj . args . npmproxy ) : '' ) ;
const env = Object . assign ( { } , process . env ) ; // Shallow clone
2021-05-29 21:17:34 +03:00
if ( typeof obj . args . npmproxy == 'string' ) { env [ 'HTTP_PROXY' ] = env [ 'HTTPS_PROXY' ] = env [ 'http_proxy' ] = env [ 'https_proxy' ] = obj . args . npmproxy ; }
2022-04-14 23:07:05 +03:00
const xxprocess = child _process . exec ( npmpath + npmproxy + ' view meshcentral versions --json' , { maxBuffer : 512000 , cwd : obj . parentpath , env : env } , function ( error , stdout , stderr ) { } ) ;
2021-05-29 21:17:34 +03:00
xxprocess . data = '' ;
xxprocess . stdout . on ( 'data' , function ( data ) { xxprocess . data += data ; } ) ;
xxprocess . stderr . on ( 'data' , function ( data ) { } ) ;
xxprocess . on ( 'close' , function ( code ) {
( code == 0 ) ? callback ( xxprocess . data ) : callback ( '{}' ) ;
} ) ;
} catch ( ex ) { callback ( '{}' ) ; }
} ;
2017-09-07 04:10:24 +03:00
// Initiate server self-update
2020-04-18 09:17:45 +03:00
obj . performServerUpdate = function ( version ) {
if ( obj . serverSelfWriteAllowed != true ) return false ;
if ( ( version == null ) || ( version == '' ) || ( typeof version != 'string' ) ) { console . log ( 'Starting self upgrade...' ) ; } else { console . log ( 'Starting self upgrade to: ' + version ) ; }
2024-03-10 10:46:01 +03:00
process . exit ( 200 ) ;
2020-04-18 09:17:45 +03:00
return true ;
2019-07-18 23:11:08 +03:00
} ;
2017-09-07 04:10:24 +03:00
2018-01-15 08:01:06 +03:00
// Initiate server self-update
2018-08-30 22:05:23 +03:00
obj . performServerCertUpdate = function ( ) { console . log ( 'Updating server certificates...' ) ; process . exit ( 200 ) ; } ;
2018-01-15 08:01:06 +03:00
2019-10-14 23:35:27 +03:00
// Start by loading configuration from Vault
obj . StartVault = function ( ) {
// Check that the configuration can only be loaded from one place
if ( ( obj . args . vault != null ) && ( obj . args . loadconfigfromdb != null ) ) { console . log ( "Can't load configuration from both database and Vault." ) ; process . exit ( ) ; return ; }
// Fix arguments if needed
if ( typeof obj . args . vault == 'string' ) {
obj . args . vault = { endpoint : obj . args . vault } ;
if ( typeof obj . args . token == 'string' ) { obj . args . vault . token = obj . args . token ; }
if ( typeof obj . args . unsealkey == 'string' ) { obj . args . vault . unsealkey = obj . args . unsealkey ; }
if ( typeof obj . args . name == 'string' ) { obj . args . vault . name = obj . args . name ; }
}
// Load configuration for HashiCorp's Vault if needed
if ( obj . args . vault ) {
if ( obj . args . vault . endpoint == null ) { console . log ( 'Missing Vault endpoint.' ) ; process . exit ( ) ; return ; }
if ( obj . args . vault . token == null ) { console . log ( 'Missing Vault token.' ) ; process . exit ( ) ; return ; }
if ( obj . args . vault . unsealkey == null ) { console . log ( 'Missing Vault unsealkey.' ) ; process . exit ( ) ; return ; }
if ( obj . args . vault . name == null ) { obj . args . vault . name = 'meshcentral' ; }
// Get new instance of the client
2022-04-14 23:07:05 +03:00
const vault = require ( "node-vault" ) ( { endpoint : obj . args . vault . endpoint , token : obj . args . vault . token } ) ;
2019-10-14 23:35:27 +03:00
vault . unseal ( { key : obj . args . vault . unsealkey } )
2024-03-10 10:46:01 +03:00
. then ( function ( ) {
2019-10-14 23:35:27 +03:00
if ( obj . args . vaultdeleteconfigfiles ) {
vault . delete ( 'secret/data/' + obj . args . vault . name )
. then ( function ( r ) { console . log ( 'Done.' ) ; process . exit ( ) ; } )
. catch ( function ( x ) { console . log ( x ) ; process . exit ( ) ; } ) ;
} else if ( obj . args . vaultpushconfigfiles ) {
// Push configuration files into Vault
if ( ( obj . args . vaultpushconfigfiles == '*' ) || ( obj . args . vaultpushconfigfiles === true ) ) { obj . args . vaultpushconfigfiles = obj . datapath ; }
obj . fs . readdir ( obj . args . vaultpushconfigfiles , function ( err , files ) {
if ( err != null ) { console . log ( 'ERROR: Unable to read from folder ' + obj . args . vaultpushconfigfiles ) ; 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 . vaultpushconfigfiles ) ; process . exit ( ) ; return ; }
var configFiles = { } ;
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 . vaultpushconfigfiles , files [ i ] ) , binary = Buffer . from ( obj . fs . readFileSync ( path , { encoding : 'binary' } ) , 'binary' ) ;
console . log ( 'Pushing ' + file + ', ' + binary . length + ' bytes.' ) ;
if ( file . endsWith ( '.json' ) || file . endsWith ( '.key' ) || file . endsWith ( '.crt' ) ) { configFiles [ file ] = binary . toString ( ) ; } else { configFiles [ file ] = binary . toString ( 'base64' ) ; }
}
}
vault . write ( 'secret/data/' + obj . args . vault . name , { "data" : configFiles } )
. then ( function ( r ) { console . log ( 'Done.' ) ; process . exit ( ) ; } )
. catch ( function ( x ) { console . log ( x ) ; process . exit ( ) ; } ) ;
} ) ;
} else {
// Read configuration files from Vault
vault . read ( 'secret/data/' + obj . args . vault . name )
. then ( function ( r ) {
if ( ( r == null ) || ( r . data == null ) || ( r . data . data == null ) ) { console . log ( 'Unable to read configuration from Vault.' ) ; process . exit ( ) ; return ; }
var configFiles = obj . configurationFiles = r . data . data ;
// Decode Base64 when needed
for ( var file in configFiles ) { if ( ! file . endsWith ( '.json' ) && ! file . endsWith ( '.key' ) && ! file . endsWith ( '.crt' ) ) { configFiles [ file ] = Buffer . from ( configFiles [ file ] , 'base64' ) ; } }
// Save all of the files
if ( obj . args . vaultpullconfigfiles ) {
for ( var i in configFiles ) {
var fullFileName = obj . path . join ( obj . args . vaultpullconfigfiles , i ) ;
try { obj . fs . writeFileSync ( fullFileName , configFiles [ i ] ) ; } catch ( ex ) { console . log ( 'Unable to write to ' + fullFileName ) ; process . exit ( ) ; return ; }
console . log ( 'Pulling ' + i + ', ' + configFiles [ i ] . length + ' bytes.' ) ;
}
console . log ( 'Done.' ) ;
process . exit ( ) ;
}
// 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 Vault.' ) ; process . exit ( ) ; return ; }
// Set the command line arguments to the config file if they are not present
if ( ! config2 . settings ) { config2 . settings = { } ; }
for ( var i in args ) { config2 . settings [ i ] = args [ i ] ; }
obj . args = args = config2 . settings ;
// Lower case all keys in the config file
2022-11-04 21:50:42 +03:00
obj . common . objKeysToLower ( config2 , [ 'ldapoptions' , 'defaultuserwebstate' , 'forceduserwebstate' , 'httpheaders' , 'telegram/proxy' ] ) ;
2019-10-14 23:35:27 +03:00
// Grad some of the values from the original config.json file if present.
if ( ( config . settings . vault != null ) && ( config2 . settings != null ) ) { config2 . settings . vault = config . settings . vault ; }
// We got a new config.json from the database, let's use it.
config = obj . config = config2 ;
obj . StartEx ( ) ;
} )
. catch ( function ( x ) { console . log ( x ) ; process . exit ( ) ; } ) ;
}
} ) . catch ( function ( x ) { console . log ( x ) ; process . exit ( ) ; } ) ;
return ;
}
}
2019-02-03 01:54:36 +03:00
// Look for easy command line instructions and do them here.
2017-08-28 19:27:45 +03:00
obj . StartEx = function ( ) {
2018-08-30 22:05:23 +03:00
var i ;
2021-02-21 22:17:18 +03:00
//var wincmd = require('node-windows');
2018-03-15 01:16:55 +03:00
//wincmd.list(function (svc) { console.log(svc); }, true);
2018-08-30 22:05:23 +03:00
2021-05-19 09:54:36 +03:00
// Setup syslog support. Not supported on Windows.
2020-04-20 08:37:26 +03:00
if ( ( require ( 'os' ) . platform ( ) != 'win32' ) && ( ( config . settings . syslog != null ) || ( config . settings . syslogjson != null ) || ( config . settings . syslogauth != null ) ) ) {
2020-04-07 22:02:29 +03:00
if ( config . settings . syslog === true ) { config . settings . syslog = 'meshcentral' ; }
if ( config . settings . syslogjson === true ) { config . settings . syslogjson = 'meshcentral-json' ; }
2020-04-20 08:37:26 +03:00
if ( config . settings . syslogauth === true ) { config . settings . syslogauth = 'meshcentral-auth' ; }
2020-04-07 22:02:29 +03:00
if ( typeof config . settings . syslog == 'string' ) {
obj . syslog = require ( 'modern-syslog' ) ;
console . log ( 'Starting ' + config . settings . syslog + ' syslog.' ) ;
obj . syslog . init ( config . settings . syslog , obj . syslog . LOG _PID | obj . syslog . LOG _ODELAY , obj . syslog . LOG _LOCAL0 ) ;
2021-05-03 11:58:49 +03:00
obj . syslog . log ( obj . syslog . LOG _INFO , "MeshCentral v" + getCurrentVersion ( ) + " Server Start" ) ;
2020-04-07 22:02:29 +03:00
}
if ( typeof config . settings . syslogjson == 'string' ) {
obj . syslogjson = require ( 'modern-syslog' ) ;
console . log ( 'Starting ' + config . settings . syslogjson + ' JSON syslog.' ) ;
obj . syslogjson . init ( config . settings . syslogjson , obj . syslogjson . LOG _PID | obj . syslogjson . LOG _ODELAY , obj . syslogjson . LOG _LOCAL0 ) ;
2021-05-03 11:58:49 +03:00
obj . syslogjson . log ( obj . syslogjson . LOG _INFO , "MeshCentral v" + getCurrentVersion ( ) + " Server Start" ) ;
2020-04-07 22:02:29 +03:00
}
2020-04-20 08:37:26 +03:00
if ( typeof config . settings . syslogauth == 'string' ) {
obj . syslogauth = require ( 'modern-syslog' ) ;
console . log ( 'Starting ' + config . settings . syslogauth + ' auth syslog.' ) ;
obj . syslogauth . init ( config . settings . syslogauth , obj . syslogauth . LOG _PID | obj . syslogauth . LOG _ODELAY , obj . syslogauth . LOG _LOCAL0 ) ;
2021-05-03 11:58:49 +03:00
obj . syslogauth . log ( obj . syslogauth . LOG _INFO , "MeshCentral v" + getCurrentVersion ( ) + " Server Start" ) ;
2020-04-20 08:37:26 +03:00
}
2020-04-07 22:02:29 +03:00
}
2021-05-19 09:54:36 +03:00
// Setup TCP syslog support, this works on all OS's.
if ( config . settings . syslogtcp != null ) {
const syslog = require ( 'syslog' ) ;
if ( config . settings . syslogtcp === true ) {
obj . syslogtcp = syslog . createClient ( 514 , 'localhost' ) ;
} else {
const sp = config . settings . syslogtcp . split ( ':' ) ;
obj . syslogtcp = syslog . createClient ( parseInt ( sp [ 1 ] ) , sp [ 0 ] ) ;
}
obj . syslogtcp . log ( "MeshCentral v" + getCurrentVersion ( ) + " Server Start" , obj . syslogtcp . LOG _INFO ) ;
}
2020-04-07 22:02:29 +03:00
2022-07-20 00:52:30 +03:00
// Check top level configuration for any unrecognized values
2024-03-10 10:46:01 +03:00
if ( config ) { for ( var i in config ) { if ( ( typeof i == 'string' ) && ( i . length > 0 ) && ( i [ 0 ] != '_' ) && ( [ 'settings' , 'domaindefaults' , 'domains' , 'configfiles' , 'smtp' , 'letsencrypt' , 'peers' , 'sms' , 'messaging' , 'sendgrid' , 'sendmail' , 'firebase' , 'firebaserelay' , '$schema' ] . indexOf ( i ) == - 1 ) ) { addServerWarning ( 'Unrecognized configuration option \"' + i + '\".' , 3 , [ i ] ) ; } } }
2019-06-12 20:23:26 +03:00
2022-01-04 04:12:02 +03:00
// Read IP lists from files if applicable
config . settings . userallowedip = obj . args . userallowedip = readIpListFromFile ( obj . args . userallowedip ) ;
config . settings . userblockedip = obj . args . userblockedip = readIpListFromFile ( obj . args . userblockedip ) ;
config . settings . agentallowedip = obj . args . agentallowedip = readIpListFromFile ( obj . args . agentallowedip ) ;
config . settings . agentblockedip = obj . args . agentblockedip = readIpListFromFile ( obj . args . agentblockedip ) ;
config . settings . swarmallowedip = obj . args . swarmallowedip = readIpListFromFile ( obj . args . swarmallowedip ) ;
// Check IP lists and ranges
2024-02-17 18:10:47 +03:00
if ( typeof obj . args . userallowedip == 'string' ) { if ( obj . args . userallowedip == '' ) { config . settings . userallowedip = obj . args . userallowedip = null ; } else { config . settings . userallowedip = obj . args . userallowedip = obj . args . userallowedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . args . userblockedip == 'string' ) { if ( obj . args . userblockedip == '' ) { config . settings . userblockedip = obj . args . userblockedip = null ; } else { config . settings . userblockedip = obj . args . userblockedip = obj . args . userblockedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . args . agentallowedip == 'string' ) { if ( obj . args . agentallowedip == '' ) { config . settings . agentallowedip = obj . args . agentallowedip = null ; } else { config . settings . agentallowedip = obj . args . agentallowedip = obj . args . agentallowedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . args . agentblockedip == 'string' ) { if ( obj . args . agentblockedip == '' ) { config . settings . agentblockedip = obj . args . agentblockedip = null ; } else { config . settings . agentblockedip = obj . args . agentblockedip = obj . args . agentblockedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . args . swarmallowedip == 'string' ) { if ( obj . args . swarmallowedip == '' ) { obj . args . swarmallowedip = null ; } else { obj . args . swarmallowedip = obj . args . swarmallowedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
2019-10-29 02:52:12 +03:00
if ( ( typeof obj . args . agentupdateblocksize == 'number' ) && ( obj . args . agentupdateblocksize >= 1024 ) && ( obj . args . agentupdateblocksize <= 65531 ) ) { obj . agentUpdateBlockSize = obj . args . agentupdateblocksize ; }
2020-05-08 00:48:51 +03:00
if ( typeof obj . args . trustedproxy == 'string' ) { obj . args . trustedproxy = obj . args . trustedproxy . split ( ' ' ) . join ( '' ) . split ( ',' ) ; }
if ( typeof obj . args . tlsoffload == 'string' ) { obj . args . tlsoffload = obj . args . tlsoffload . split ( ' ' ) . join ( '' ) . split ( ',' ) ; }
2019-08-23 01:31:39 +03:00
2022-04-10 03:12:52 +03:00
// Check the "cookieIpCheck" value
2022-05-17 02:32:19 +03:00
if ( ( obj . args . cookieipcheck === false ) || ( obj . args . cookieipcheck == 'none' ) ) { obj . args . cookieipcheck = 'none' ; }
2022-04-10 03:12:52 +03:00
else if ( ( typeof obj . args . cookieipcheck != 'string' ) || ( obj . args . cookieipcheck . toLowerCase ( ) != 'strict' ) ) { obj . args . cookieipcheck = 'lax' ; }
else { obj . args . cookieipcheck = 'strict' ; }
// Check the "cookieSameSite" value
if ( typeof obj . args . cookiesamesite != 'string' ) { delete obj . args . cookiesamesite ; }
else if ( [ 'none' , 'lax' , 'strict' ] . indexOf ( obj . args . cookiesamesite . toLowerCase ( ) ) == - 1 ) { delete obj . args . cookiesamesite ; } else { obj . args . cookiesamesite = obj . args . cookiesamesite . toLowerCase ( ) ; }
2020-08-21 21:47:34 +03:00
// Check if WebSocket compression is supported. It's known to be broken in NodeJS v11.11 to v12.15, and v13.2
2020-07-22 02:20:17 +03:00
const verSplit = process . version . substring ( 1 ) . split ( '.' ) ;
2022-04-14 23:07:05 +03:00
const ver = parseInt ( verSplit [ 0 ] ) + ( parseInt ( verSplit [ 1 ] ) / 100 ) ;
2020-08-21 21:47:34 +03:00
if ( ( ( ver >= 11.11 ) && ( ver <= 12.15 ) ) || ( ver == 13.2 ) ) {
2021-09-02 06:06:14 +03:00
if ( ( obj . args . wscompression === true ) || ( obj . args . agentwscompression === true ) ) { addServerWarning ( 'WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15 and v13.2' , 4 ) ; }
2020-07-22 02:20:17 +03:00
obj . args . wscompression = obj . args . agentwscompression = false ;
obj . config . settings . wscompression = obj . config . settings . agentwscompression = false ;
2020-07-21 08:26:58 +03:00
}
2019-08-23 01:31:39 +03: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-09 04:14:30 +03: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 ; }
2020-07-28 09:10:02 +03:00
if ( obj . args . showall ) { obj . db . GetAll ( function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showusers ) { obj . db . GetAllType ( 'user' , function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showitem ) { obj . db . Get ( obj . args . showitem , function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
2020-03-22 21:57:52 +03:00
if ( obj . args . listuserids ) { obj . db . GetAllType ( 'user' , function ( err , docs ) { for ( var i in docs ) { console . log ( docs [ i ] . _id ) ; } process . exit ( ) ; } ) ; return ; }
2020-07-28 09:10:02 +03:00
if ( obj . args . showusergroups ) { obj . db . GetAllType ( 'ugrp' , function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . shownodes ) { obj . db . GetAllType ( 'node' , function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
2020-08-02 07:12:07 +03:00
if ( obj . args . showallmeshes ) { obj . db . GetAllType ( 'mesh' , function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showmeshes ) { obj . db . GetAllType ( 'mesh' , function ( err , docs ) { var x = [ ] ; for ( var i in docs ) { if ( docs [ i ] . deleted == null ) { x . push ( docs [ i ] ) ; } } console . log ( JSON . stringify ( x , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
2020-07-28 09:10:02 +03:00
if ( obj . args . showevents ) { obj . db . GetAllEvents ( function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showsmbios ) { obj . db . GetAllSMBIOS ( function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
if ( obj . args . showpower ) { obj . db . getAllPower ( function ( err , docs ) { console . log ( JSON . stringify ( docs , null , 2 ) ) ; process . exit ( ) ; } ) ; return ; }
2019-05-09 04:14:30 +03:00
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 ; }
2019-10-03 23:32:54 +03:00
if ( obj . args . recordencryptionrecode ) { obj . db . performRecordEncryptionRecode ( function ( count ) { console . log ( 'Re-encoded ' + count + ' record(s).' ) ; process . exit ( ) ; } ) ; return ; }
2019-12-09 07:46:25 +03:00
if ( obj . args . dbstats ) { obj . db . getDbStats ( function ( stats ) { console . log ( stats ) ; process . exit ( ) ; } ) ; return ; }
2020-04-21 02:59:59 +03:00
if ( obj . args . createaccount ) { // Create a new user account
2021-02-17 11:24:49 +03:00
if ( ( typeof obj . args . createaccount != 'string' ) || ( ( obj . args . pass == null ) && ( obj . args . hashpass == null ) ) || ( obj . args . pass == '' ) || ( obj . args . hashpass == '' ) || ( obj . args . createaccount . indexOf ( ' ' ) >= 0 ) ) { console . log ( "Usage: --createaccount [userid] --pass [password] --domain (domain) --email (email) --name (name)." ) ; process . exit ( ) ; return ; }
2020-04-21 02:59:59 +03:00
var userid = 'user/' + ( obj . args . domain ? obj . args . domain : '' ) + '/' + obj . args . createaccount . toLowerCase ( ) , domainid = obj . args . domain ? obj . args . domain : '' ;
2020-05-27 03:49:42 +03:00
if ( obj . args . createaccount . startsWith ( 'user/' ) ) { userid = obj . args . createaccount ; domainid = obj . args . createaccount . split ( '/' ) [ 1 ] ; }
if ( userid . split ( '/' ) . length != 3 ) { console . log ( "Invalid userid." ) ; process . exit ( ) ; return ; }
2020-04-21 02:59:59 +03:00
obj . db . Get ( userid , function ( err , docs ) {
2020-12-29 10:14:15 +03:00
if ( err != null ) { console . log ( "Database error: " + err ) ; process . exit ( ) ; return ; }
2020-04-21 02:59:59 +03:00
if ( ( docs != null ) && ( docs . length != 0 ) ) { console . log ( 'User already exists.' ) ; process . exit ( ) ; return ; }
2020-12-29 10:14:15 +03:00
if ( ( domainid != '' ) && ( ( config . domains == null ) || ( config . domains [ domainid ] == null ) ) ) { console . log ( "Invalid domain." ) ; process . exit ( ) ; return ; }
2022-04-14 23:07:05 +03:00
const user = { _id : userid , type : 'user' , name : ( typeof obj . args . name == 'string' ) ? obj . args . name : ( userid . split ( '/' ) [ 2 ] ) , domain : domainid , creation : Math . floor ( Date . now ( ) / 1000 ) , links : { } } ;
2020-04-21 02:59:59 +03:00
if ( typeof obj . args . email == 'string' ) { user . email = obj . args . email ; user . emailVerified = true ; }
2021-02-17 11:24:49 +03:00
if ( obj . args . hashpass ) {
// Create an account using a pre-hashed password. Use --hashpassword to pre-hash a password.
var hashpasssplit = obj . args . hashpass . split ( ',' ) ;
if ( hashpasssplit . length != 2 ) { console . log ( "Invalid hashed password." ) ; process . exit ( ) ; return ; }
user . salt = hashpasssplit [ 0 ] ;
user . hash = hashpasssplit [ 1 ] ;
2022-08-31 00:41:21 +03:00
obj . db . Set ( user , function ( ) { console . log ( "Done. This command will only work if MeshCentral is stopped." ) ; process . exit ( ) ; return ; } ) ;
2021-02-17 11:24:49 +03:00
} else {
// Hash the password and create the account.
require ( './pass' ) . hash ( obj . args . pass , function ( err , salt , hash , tag ) { if ( err ) { console . log ( "Unable create account password: " + err ) ; process . exit ( ) ; return ; } user . salt = salt ; user . hash = hash ; obj . db . Set ( user , function ( ) { console . log ( "Done." ) ; process . exit ( ) ; return ; } ) ; } , 0 ) ;
}
2020-04-21 02:59:59 +03:00
} ) ;
return ;
}
if ( obj . args . resetaccount ) { // Unlock a user account, set a new password and remove 2FA
2023-09-30 14:20:39 +03:00
if ( ( typeof obj . args . resetaccount != 'string' ) || ( obj . args . resetaccount . indexOf ( ' ' ) >= 0 ) ) { console . log ( "Usage: --resetaccount [userid] --domain (domain) --pass [password]." ) ; process . exit ( ) ; return ; }
2020-05-27 03:49:42 +03:00
var userid = 'user/' + ( obj . args . domain ? obj . args . domain : '' ) + '/' + obj . args . resetaccount . toLowerCase ( ) ;
if ( obj . args . resetaccount . startsWith ( 'user/' ) ) { userid = obj . args . resetaccount ; }
if ( userid . split ( '/' ) . length != 3 ) { console . log ( "Invalid userid." ) ; process . exit ( ) ; return ; }
2020-04-21 02:59:59 +03:00
obj . db . Get ( userid , function ( err , docs ) {
if ( err != null ) { console . log ( "Database error: " + err ) ; process . exit ( ) ; return ; }
2020-05-26 22:46:48 +03:00
if ( ( docs == null ) || ( docs . length == 0 ) ) { console . log ( "Unknown userid, usage: --resetaccount [userid] --domain (domain) --pass [password]." ) ; process . exit ( ) ; return ; }
2022-04-14 23:07:05 +03:00
const user = docs [ 0 ] ; if ( ( user . siteadmin ) && ( user . siteadmin != 0xFFFFFFFF ) && ( user . siteadmin & 32 ) != 0 ) { user . siteadmin -= 32 ; } // Unlock the account.
2022-10-22 17:23:55 +03:00
delete user . phone ; delete user . otpekey ; delete user . otpsecret ; delete user . otpkeys ; delete user . otphkeys ; delete user . otpdev ; delete user . otpsms ; delete user . otpmsg ; // Disable 2FA
2023-09-06 22:15:22 +03:00
delete user . msghandle ; // Disable users 2fa messaging too
2023-09-30 14:31:25 +03:00
var config = getConfig ( false ) ;
2024-03-10 10:46:01 +03:00
if ( config . domains [ user . domain ] . auth || config . domains [ user . domain ] . authstrategies ) {
2023-09-30 14:20:39 +03:00
console . log ( 'This users domain has external authentication methods enabled so the password will not be changed if you set one' )
obj . db . Set ( user , function ( ) { console . log ( "Done." ) ; process . exit ( ) ; return ; } ) ;
2024-03-10 10:46:01 +03:00
} else {
2023-09-30 14:20:39 +03:00
if ( obj . args . hashpass && ( typeof obj . args . hashpass == 'string' ) ) {
// Reset an account using a pre-hashed password. Use --hashpassword to pre-hash a password.
var hashpasssplit = obj . args . hashpass . split ( ',' ) ;
if ( hashpasssplit . length != 2 ) { console . log ( "Invalid hashed password." ) ; process . exit ( ) ; return ; }
user . salt = hashpasssplit [ 0 ] ;
user . hash = hashpasssplit [ 1 ] ;
obj . db . Set ( user , function ( ) { console . log ( "Done. This command will only work if MeshCentral is stopped." ) ; process . exit ( ) ; return ; } ) ;
2024-03-10 10:46:01 +03:00
} else if ( obj . args . pass && ( typeof obj . args . pass == 'string' ) ) {
2023-09-30 14:20:39 +03:00
// Hash the password and reset the account.
require ( './pass' ) . hash ( String ( obj . args . pass ) , user . salt , function ( err , hash , tag ) {
if ( err ) { console . log ( "Unable to reset password: " + err ) ; process . exit ( ) ; return ; }
user . hash = hash ;
obj . db . Set ( user , function ( ) { console . log ( "Done." ) ; process . exit ( ) ; return ; } ) ;
} , 0 ) ;
2024-03-10 10:46:01 +03:00
} else {
2023-09-30 14:20:39 +03:00
console . log ( 'Not setting a users password' ) ;
obj . db . Set ( user , function ( ) { console . log ( "Done." ) ; process . exit ( ) ; return ; } ) ;
}
2021-02-17 11:24:49 +03:00
}
2020-03-22 21:57:52 +03:00
} ) ;
return ;
}
2020-04-21 02:59:59 +03:00
if ( obj . args . adminaccount ) { // Set a user account to server administrator
2022-01-12 04:30:50 +03:00
if ( ( typeof obj . args . adminaccount != 'string' ) || ( obj . args . adminaccount . indexOf ( ' ' ) >= 0 ) ) { console . log ( "Invalid userid, usage: --adminaccount [username] --domain (domain)" ) ; process . exit ( ) ; return ; }
2020-05-27 03:49:42 +03:00
var userid = 'user/' + ( obj . args . domain ? obj . args . domain : '' ) + '/' + obj . args . adminaccount . toLowerCase ( ) ;
if ( obj . args . adminaccount . startsWith ( 'user/' ) ) { userid = obj . args . adminaccount ; }
if ( userid . split ( '/' ) . length != 3 ) { console . log ( "Invalid userid." ) ; process . exit ( ) ; return ; }
2020-04-21 02:59:59 +03:00
obj . db . Get ( userid , function ( err , docs ) {
if ( err != null ) { console . log ( "Database error: " + err ) ; process . exit ( ) ; return ; }
2020-05-26 22:46:48 +03:00
if ( ( docs == null ) || ( docs . length == 0 ) ) { console . log ( "Unknown userid, usage: --adminaccount [userid] --domain (domain)." ) ; process . exit ( ) ; return ; }
2020-03-22 22:04:04 +03:00
docs [ 0 ] . siteadmin = 0xFFFFFFFF ; // Set user as site administrator
2022-08-31 00:41:21 +03:00
obj . db . Set ( docs [ 0 ] , function ( ) { console . log ( "Done. This command will only work if MeshCentral is stopped." ) ; process . exit ( ) ; return ; } ) ;
2020-03-22 22:04:04 +03:00
} ) ;
return ;
}
2022-01-12 04:30:50 +03:00
if ( obj . args . removesubdomain ) { // Remove all references to a sub domain from the database
if ( ( typeof obj . args . removesubdomain != 'string' ) || ( obj . args . removesubdomain . indexOf ( ' ' ) >= 0 ) ) { console . log ( "Invalid sub domain, usage: --removesubdomain [domain]" ) ; process . exit ( ) ; return ; }
obj . db . removeDomain ( obj . args . removesubdomain , function ( ) { console . log ( "Done." ) ; process . exit ( ) ; return ; } ) ;
return ;
}
2021-01-12 23:43:27 +03:00
if ( obj . args . removetestagents ) { // Remove all test agents from the database
db . GetAllType ( 'node' , function ( err , docs ) {
if ( ( err != null ) || ( docs . length == 0 ) ) {
console . log ( 'Unable to get any nodes from the database' ) ;
process . exit ( 0 ) ;
} else {
// Load all users
2022-04-14 23:07:05 +03:00
const allusers = { } , removeCount = 0 ;
2021-01-12 23:43:27 +03:00
obj . db . GetAllType ( 'user' , function ( err , docs ) {
obj . common . unEscapeAllLinksFieldName ( docs ) ;
for ( i in docs ) { allusers [ docs [ i ] . _id ] = docs [ i ] ; }
} ) ;
// Look at all devices
for ( var i in docs ) {
if ( ( docs [ i ] != null ) && ( docs [ i ] . agent != null ) && ( docs [ i ] . agent . id == 23 ) ) {
// Remove this test node
2022-04-14 23:07:05 +03:00
const node = docs [ i ] ;
2021-01-12 23:43:27 +03:00
// Delete this node including network interface information, events and timeline
removeCount ++ ;
db . Remove ( node . _id ) ; // Remove node with that id
db . Remove ( 'if' + node . _id ) ; // Remove interface information
db . Remove ( 'nt' + node . _id ) ; // Remove notes
db . Remove ( 'lc' + node . _id ) ; // Remove last connect time
db . Remove ( 'si' + node . _id ) ; // Remove system information
if ( db . RemoveSMBIOS ) { db . RemoveSMBIOS ( node . _id ) ; } // Remove SMBios data
db . RemoveAllNodeEvents ( node . _id ) ; // Remove all events for this node
db . removeAllPowerEventsForNode ( node . _id ) ; // Remove all power events for this node
2021-02-23 03:54:30 +03:00
if ( typeof node . pmt == 'string' ) { db . Remove ( 'pmt_' + node . pmt ) ; } // Remove Push Messaging Token
2021-01-12 23:43:27 +03:00
db . Get ( 'ra' + node . _id , function ( err , nodes ) {
if ( ( nodes != null ) && ( nodes . length == 1 ) ) { db . Remove ( 'da' + nodes [ 0 ] . daid ) ; } // Remove diagnostic agent to real agent link
db . Remove ( 'ra' + node . _id ) ; // Remove real agent to diagnostic agent link
} ) ;
// Remove any user node links
if ( node . links != null ) {
for ( var i in node . links ) {
if ( i . startsWith ( 'user/' ) ) {
var cuser = allusers [ i ] ;
if ( ( cuser != null ) && ( cuser . links != null ) && ( cuser . links [ node . _id ] != null ) ) {
// Remove the user link & save the user
delete cuser . links [ node . _id ] ;
if ( Object . keys ( cuser . links ) . length == 0 ) { delete cuser . links ; }
db . SetUser ( cuser ) ;
}
}
}
}
}
}
if ( removeCount == 0 ) {
console . log ( "Done, no devices removed." ) ;
process . exit ( 0 ) ;
} else {
console . log ( "Removed " + removeCount + " device(s), holding 10 seconds..." ) ;
setTimeout ( function ( ) { console . log ( "Done." ) ; process . exit ( 0 ) ; } , 10000 )
}
}
} ) ;
return ;
}
2019-05-09 04:14:30 +03:00
2021-01-05 03:26:25 +03:00
// Import NeDB data into database
if ( obj . args . nedbtodb ) {
if ( db . databaseType == 1 ) { console . log ( "NeDB is current database, can't perform transfer." ) ; process . exit ( ) ; return ; }
console . log ( "Transfering NeDB data into database..." ) ;
db . nedbtodb ( function ( msg ) { console . log ( msg ) ; process . exit ( ) ; } )
return ;
}
2019-05-09 04:14:30 +03:00
// Show a list of all configuration files in the database
if ( obj . args . dblistconfigfiles ) {
2019-10-30 02:17:29 +03:00
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-05-09 04:14:30 +03:00
}
2019-02-03 01:54:36 +03:00
2019-05-09 04:14:30 +03:00
// Display the content of a configuration file in the database
if ( obj . args . dbshowconfigfile ) {
2019-10-30 02:17:29 +03:00
if ( typeof obj . args . configkey != 'string' ) { console . log ( "Error, --configkey is required." ) ; process . exit ( ) ; return ; }
2019-05-09 04:14:30 +03:00
obj . db . getConfigFile ( obj . args . dbshowconfigfile , function ( err , docs ) {
if ( err == null ) {
2019-10-30 02:17:29 +03:00
if ( docs . length == 0 ) { console . log ( "File not found." ) ; } else {
2022-04-14 23:07:05 +03:00
const data = obj . db . decryptData ( obj . args . configkey , docs [ 0 ] . data ) ;
2019-10-30 02:17:29 +03:00
if ( data == null ) { console . log ( "Invalid config key." ) ; } else { console . log ( data ) ; }
2019-05-09 04:14:30 +03:00
}
2019-10-30 02:17:29 +03:00
} else { console . log ( "Unable to read from database." ) ; }
2019-05-09 04:14:30 +03:00
process . exit ( ) ;
} ) ; return ;
}
2019-02-03 01:54:36 +03:00
2019-05-09 04:14:30 +03:00
// Delete all configuration files from database
if ( obj . args . dbdeleteconfigfiles ) {
2019-10-30 02:17:29 +03:00
console . log ( "Deleting all configuration files from the database..." ) ; obj . db . RemoveAllOfType ( 'cfile' , function ( ) { console . log ( 'Done.' ) ; process . exit ( ) ; } ) ;
2019-05-09 04:14:30 +03:00
}
2019-02-02 02:16:46 +03:00
2019-05-09 04:14:30 +03:00
// Push all relevent files from meshcentral-data into the database
if ( obj . args . dbpushconfigfiles ) {
2019-10-30 02:17:29 +03:00
if ( typeof obj . args . configkey != 'string' ) { console . log ( "Error, --configkey is required." ) ; process . exit ( ) ; return ; }
2019-05-09 04:14:30 +03:00
if ( ( obj . args . dbpushconfigfiles !== true ) && ( typeof obj . args . dbpushconfigfiles != 'string' ) ) {
2019-10-30 02:17:29 +03:00
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." ) ;
2019-05-09 04:14:30 +03:00
process . exit ( ) ;
} else {
if ( ( obj . args . dbpushconfigfiles == '*' ) || ( obj . args . dbpushconfigfiles === true ) ) { obj . args . dbpushconfigfiles = obj . datapath ; }
2019-02-27 01:39:45 +03:00
obj . fs . readdir ( obj . args . dbpushconfigfiles , function ( err , files ) {
2019-05-09 04:14:30 +03: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-27 01:39:45 +03:00
} ) ;
2019-05-09 04:14:30 +03:00
}
return ;
}
2019-02-02 02:16:46 +03:00
2019-05-09 04:14:30 +03:00
// Pull all database files into meshcentral-data
if ( obj . args . dbpullconfigfiles ) {
2019-10-30 02:17:29 +03:00
if ( typeof obj . args . configkey != 'string' ) { console . log ( "Error, --configkey is required." ) ; process . exit ( ) ; return ; }
2019-05-09 04:14:30 +03:00
if ( typeof obj . args . dbpullconfigfiles != 'string' ) {
2019-10-30 02:17:29 +03:00
console . log ( "Usage: --dbpulldatafiles (path)" ) ;
2019-05-09 04:14:30 +03:00
process . exit ( ) ;
} else {
obj . db . GetAllType ( 'cfile' , function ( err , docs ) {
if ( err == null ) {
if ( docs . length == 0 ) {
2019-10-30 02:17:29 +03:00
console . log ( "File not found." ) ;
2019-02-03 01:54:36 +03:00
} else {
2019-05-09 04:14:30 +03: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 ) {
2019-10-30 02:17:29 +03:00
console . log ( "Invalid config key." ) ;
2019-05-09 04:14:30 +03:00
} else {
2022-04-14 23:07:05 +03:00
const fullFileName = obj . path . join ( obj . args . dbpullconfigfiles , file ) ;
2019-05-09 04:14:30 +03:00
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-03 01:54:36 +03:00
}
2019-05-09 04:14:30 +03:00
} else {
2019-10-30 02:17:29 +03:00
console . log ( "Unable to read from database." ) ;
2019-02-02 02:16:46 +03:00
}
2019-05-09 04:14:30 +03:00
process . exit ( ) ;
} ) ;
2019-02-02 02:16:46 +03:00
}
2019-05-09 04:14:30 +03:00
return ;
}
2019-02-02 02:16:46 +03:00
2019-05-09 04:14:30 +03: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 22:13:13 +03:00
} ) ;
2019-05-09 04:14:30 +03: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' ) ; }
2021-03-27 10:26:18 +03:00
var json = null , json2 = '' , badCharCount = 0 ;
2022-02-04 06:11:35 +03:00
try { json = obj . fs . readFileSync ( obj . args . dbimport , { encoding : 'utf8' } ) ; } catch ( ex ) { console . log ( 'Invalid JSON file: ' + obj . args . dbimport + ': ' + ex ) ; process . exit ( ) ; }
2019-05-09 04:14:30 +03:00
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.' ) ; }
2022-02-04 06:11:35 +03:00
try { json = JSON . parse ( json2 ) ; } catch ( ex ) { console . log ( 'Invalid JSON format: ' + obj . args . dbimport + ': ' + e ) ; process . exit ( ) ; }
2019-05-09 04:14:30 +03:00
if ( ( json == null ) || ( typeof json . length != 'number' ) || ( json . length < 1 ) ) { console . log ( 'Invalid JSON format: ' + obj . args . dbimport + '.' ) ; }
2020-12-29 10:14:15 +03:00
// Escape MongoDB invalid field chars
for ( i in json ) {
2022-04-14 23:07:05 +03:00
const doc = json [ i ] ;
2020-12-29 10:14:15 +03:00
for ( var j in doc ) { if ( j . indexOf ( '.' ) >= 0 ) { console . log ( "Invalid field name (" + j + ") in document: " + json [ i ] ) ; return ; } }
2021-03-27 10:26:18 +03:00
//if ((json[i].type == 'ifinfo') && (json[i].netif2 != null)) { for (var j in json[i].netif2) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].netif2[esc] = json[i].netif2[j]; delete json[i].netif2[j]; } } }
//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]; } } }
2024-03-10 10:46:01 +03:00
}
2019-05-09 04:14:30 +03:00
//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 ;
2022-02-04 06:11:35 +03:00
try { json = obj . fs . readFileSync ( obj . args . dbmerge , { encoding : 'utf8' } ) ; } catch ( ex ) { console . log ( 'Invalid JSON file: ' + obj . args . dbmerge + ': ' + ex ) ; process . exit ( ) ; }
2019-05-09 04:14:30 +03:00
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.' ) ; }
2022-02-04 06:11:35 +03:00
try { json = JSON . parse ( json2 ) ; } catch ( ex ) { console . log ( 'Invalid JSON format: ' + obj . args . dbmerge + ': ' + ex ) ; process . exit ( ) ; }
2019-05-09 04:14:30 +03:00
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 ) {
2022-04-14 23:07:05 +03:00
const users = { } , usersCount = 0 ;
2019-05-09 04:14:30 +03:00
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 ) ;
2022-04-14 23:07:05 +03:00
const meshes = { } , meshesCount = 0 ;
2019-05-09 04:14:30 +03:00
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
2022-04-14 23:07:05 +03:00
const objectToAdd = [ ] ;
2019-05-09 04:14:30 +03:00
for ( var i in json ) {
2022-04-14 23:07:05 +03:00
const newobj = json [ i ] ;
2019-05-09 04:14:30 +03:00
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-16 02:11:52 +03:00
}
2019-05-09 04:14:30 +03:00
if ( existingUser . name == 'admin' ) { existingUser . links = { } ; }
objectToAdd . push ( existingUser ) ; // Add this user
} else {
objectToAdd . push ( newobj ) ; // Add this user
2019-02-16 01:50:23 +03:00
}
2019-05-09 04:14:30 +03:00
} else if ( newobj . type == 'mesh' ) {
2020-03-27 05:33:13 +03:00
// Add this object
objectToAdd . push ( newobj ) ;
2019-05-09 04:14:30 +03:00
} // Don't add nodes.
2019-02-16 01:50:23 +03:00
}
2019-05-09 04:14:30 +03: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-17 03:49:55 +03:00
return ;
2019-02-03 01:54:36 +03:00
}
2018-07-17 03:49:55 +03:00
2020-12-29 06:25:21 +03:00
// Check if the database is capable of performing a backup
obj . db . checkBackupCapability ( function ( err , msg ) { if ( msg != null ) { obj . addServerWarning ( msg , true ) } } ) ;
2019-05-09 04:14:30 +03: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 ; }
2019-10-30 02:17:29 +03:00
if ( key == null ) { console . log ( "Error, --configkey is required." ) ; process . exit ( ) ; return ; }
2019-05-09 04:14:30 +03:00
obj . db . getAllConfigFiles ( key , function ( configFiles ) {
2019-10-30 02:17:29 +03:00
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 ; }
2022-10-25 02:58:05 +03:00
if ( typeof configFiles [ 'config.json' ] == 'object' ) { configFiles [ 'config.json' ] = configFiles [ 'config.json' ] . toString ( ) ; }
if ( configFiles [ 'config.json' ] . charCodeAt ( 0 ) == 65279 ) { configFiles [ 'config.json' ] = configFiles [ 'config.json' ] . substring ( 1 ) ; }
2019-05-09 04:14:30 +03:00
obj . configurationFiles = configFiles ;
// Parse the new configuration file
var config2 = null ;
2022-10-25 02:58:05 +03:00
try { config2 = JSON . parse ( configFiles [ 'config.json' ] ) ; } catch ( ex ) { console . log ( 'Error, unable to parse config.json from database.' , ex ) ; process . exit ( ) ; return ; }
2019-05-09 04:14:30 +03:00
// 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
2022-11-02 23:59:00 +03:00
common . objKeysToLower ( config2 , [ 'ldapoptions' , 'defaultuserwebstate' , 'forceduserwebstate' , 'httpheaders' , 'telegram/proxy' ] ) ;
2019-05-09 04:14:30 +03:00
2022-08-14 04:32:17 +03:00
// Grab some of the values from the original config.json file if present.
2020-02-04 05:58:58 +03:00
config2 [ 'mysql' ] = config [ 'mysql' ] ;
2020-02-02 23:37:27 +03:00
config2 [ 'mariadb' ] = config [ 'mariadb' ] ;
2019-05-09 04:14:30 +03:00
config2 [ 'mongodb' ] = config [ 'mongodb' ] ;
config2 [ 'mongodbcol' ] = config [ 'mongodbcol' ] ;
config2 [ 'dbencryptkey' ] = config [ 'dbencryptkey' ] ;
2022-08-14 04:32:17 +03:00
config2 [ 'acebase' ] = config [ 'acebase' ] ;
2022-08-15 10:33:50 +03:00
config2 [ 'sqlite3' ] = config [ 'sqlite3' ] ;
2019-02-03 01:54:36 +03:00
2019-05-09 04:14:30 +03:00
// We got a new config.json from the database, let's use it.
config = obj . config = config2 ;
obj . StartEx1b ( ) ;
} ) ;
} else {
2019-10-14 23:35:27 +03:00
config = obj . config = getConfig ( obj . args . vault == null ) ;
2019-05-09 04:14:30 +03:00
obj . StartEx1b ( ) ;
}
2018-07-17 03:49:55 +03:00
} ) ;
}
2019-05-09 04:14:30 +03:00
) ;
2019-02-03 01:54:36 +03:00
} ;
2018-07-17 03:49:55 +03:00
2019-07-18 01:57:42 +03:00
// Time to start the server of real.
2022-07-07 06:34:04 +03:00
obj . StartEx1b = async function ( ) {
2019-02-03 01:54:36 +03:00
var i ;
2017-08-28 19:27:45 +03:00
2023-09-24 21:40:06 +03:00
// Add NodeJS version warning if needed
if ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) < 16 ) { addServerWarning ( "MeshCentral will require Node v16 or above in the future, your current version is " + process . version + "." ) ; }
2021-01-20 20:54:51 +03:00
// Setup certificate operations
obj . certificateOperations = require ( './certoperations.js' ) . CertificateOperations ( obj ) ;
2020-02-17 21:24:32 +03:00
// Linux format /var/log/auth.log
if ( obj . config . settings . authlog != null ) {
obj . fs . open ( obj . config . settings . authlog , 'a' , function ( err , fd ) {
2022-09-02 08:06:08 +03:00
if ( err == null ) { obj . authlogfile = fd ; } else { console . log ( 'ERROR: Unable to open: ' + obj . config . settings . authlog ) ; }
2020-02-17 21:24:32 +03:00
} )
}
2022-07-07 06:34:04 +03:00
// Start CrowdSec bouncer if needed: https://www.crowdsec.net/
2022-07-08 07:51:09 +03:00
if ( typeof obj . args . crowdsec == 'object' ) { obj . crowdSecBounser = require ( './crowdsec.js' ) . CreateCrowdSecBouncer ( obj , obj . args . crowdsec ) ; }
2022-07-07 06:34:04 +03:00
2019-07-18 23:11:08 +03: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-03 01:54:36 +03:00
// If we are targetting a specific version, update now.
2019-07-18 23:11:08 +03:00
if ( ( obj . serverSelfWriteAllowed == true ) && ( typeof obj . args . selfupdate == 'string' ) ) {
2019-02-03 01:54:36 +03:00
obj . args . selfupdate = obj . args . selfupdate . toLowerCase ( ) ;
2021-05-03 11:58:49 +03:00
if ( getCurrentVersion ( ) !== obj . args . selfupdate ) { obj . performServerUpdate ( ) ; return ; } // We are targetting a specific version, run self update now.
2019-02-03 01:54:36 +03:00
}
2017-08-28 19:27:45 +03:00
2019-02-03 01:54:36 +03:00
// Write the server state
obj . updateServerState ( 'state' , 'starting' ) ;
2019-07-18 01:57:42 +03:00
if ( process . pid ) { obj . updateServerState ( 'server-pid' , process . pid ) ; }
2019-07-18 03:34:34 +03:00
if ( process . ppid ) { obj . updateServerState ( 'server-parent-pid' , process . ppid ) ; }
2019-02-03 01:54:36 +03:00
// Read environment variables. For a subset of arguments, we allow them to be read from environment variables.
2022-04-14 23:07:05 +03:00
const xenv = [ 'user' , 'port' , 'mpsport' , 'mpsaliasport' , 'redirport' , 'rediraliasport' , 'exactport' , 'debug' ] ;
2019-02-03 01:54:36 +03:00
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 ; }
2019-12-18 23:00:08 +03:00
var xdomains = { } ; for ( i in obj . config . domains ) { xdomains [ i . toLowerCase ( ) ] = obj . config . domains [ i ] ; } obj . config . domains = xdomains ;
2019-02-03 01:54:36 +03:00
var bannedDomains = [ 'public' , 'private' , 'images' , 'scripts' , 'styles' , 'views' ] ; // List of banned domains
2022-05-13 10:39:47 +03: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." ) ; delete obj . config . domains [ i ] ; } } }
for ( i in obj . config . domains ) { if ( ( i . length > 64 ) || ( Buffer . from ( i ) . length > 64 ) ) { console . log ( "ERROR: Domain '" + i + "' is longer that 64 bytes, this is not allowed." ) ; delete obj . config . domains [ i ] ; } }
2019-02-03 01:54:36 +03:00
for ( i in obj . config . domains ) {
2020-07-10 01:18:33 +03:00
// Remove any domains that start with underscore
if ( i . startsWith ( '_' ) ) { delete obj . config . domains [ i ] ; continue ; }
2020-04-30 12:02:23 +03:00
// Apply default domain settings if present
if ( typeof obj . config . domaindefaults == 'object' ) { for ( var j in obj . config . domaindefaults ) { if ( obj . config . domains [ i ] [ j ] == null ) { obj . config . domains [ i ] [ j ] = obj . config . domaindefaults [ j ] ; } } }
// Perform domain setup
2020-03-27 22:04:11 +03:00
if ( typeof obj . config . domains [ i ] != 'object' ) { console . log ( "ERROR: Invalid domain configuration in config.json." ) ; process . exit ( ) ; return ; }
2019-12-01 23:52:32 +03:00
if ( ( i . length > 0 ) && ( i [ 0 ] == '_' ) ) { delete obj . config . domains [ i ] ; continue ; } // Remove any domains with names that start with _
2019-04-11 23:41:51 +03:00
if ( typeof config . domains [ i ] . auth == 'string' ) { config . domains [ i ] . auth = config . domains [ i ] . auth . toLowerCase ( ) ; }
2019-02-12 01:41:15 +03:00
if ( obj . config . domains [ i ] . limits == null ) { obj . config . domains [ i ] . limits = { } ; }
2019-02-03 01:54:36 +03: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 ;
2022-08-31 21:54:21 +03:00
if ( ( typeof obj . config . domains [ i ] . maxdeviceview != 'number' ) || ( obj . config . domains [ i ] . maxdeviceview < 1 ) ) { delete obj . config . domains [ i ] . maxdeviceview ; }
2019-11-26 01:30:23 +03:00
if ( typeof obj . config . domains [ i ] . loginkey == 'string' ) { obj . config . domains [ i ] . loginkey = [ obj . config . domains [ i ] . loginkey ] ; }
2020-06-21 11:45:24 +03:00
if ( ( obj . config . domains [ i ] . loginkey != null ) && ( obj . common . validateAlphaNumericArray ( obj . config . domains [ i ] . loginkey , 1 , 128 ) == false ) ) { console . log ( "ERROR: Invalid login key, must be alpha-numeric string with no spaces." ) ; process . exit ( ) ; return ; }
2022-01-17 00:59:14 +03:00
if ( typeof obj . config . domains [ i ] . agentkey == 'string' ) { obj . config . domains [ i ] . agentkey = [ obj . config . domains [ i ] . agentkey ] ; }
if ( ( obj . config . domains [ i ] . agentkey != null ) && ( obj . common . validateAlphaNumericArray ( obj . config . domains [ i ] . agentkey , 1 , 128 ) == false ) ) { console . log ( "ERROR: Invalid agent key, must be alpha-numeric string with no spaces." ) ; process . exit ( ) ; return ; }
2024-06-21 00:34:08 +03:00
obj . config . domains [ i ] . userallowedip = obj . config . domains [ i ] . userallowedip = readIpListFromFile ( obj . config . domains [ i ] . userallowedip ) ;
obj . config . domains [ i ] . userblockedip = obj . config . domains [ i ] . userblockedip = readIpListFromFile ( obj . config . domains [ i ] . userblockedip ) ;
obj . config . domains [ i ] . agentallowedip = obj . config . domains [ i ] . agentallowedip = readIpListFromFile ( obj . config . domains [ i ] . agentallowedip ) ;
obj . config . domains [ i ] . agentblockedip = obj . config . domains [ i ] . agentblockedip = readIpListFromFile ( obj . config . domains [ i ] . agentblockedip ) ;
2024-02-17 22:42:47 +03:00
if ( typeof obj . config . domains [ i ] . userallowedip == 'string' ) { if ( obj . config . domains [ i ] . userallowedip == '' ) { delete obj . config . domains [ i ] . userallowedip ; } else { obj . config . domains [ i ] . userallowedip = obj . config . domains [ i ] . userallowedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . userblockedip == 'string' ) { if ( obj . config . domains [ i ] . userblockedip == '' ) { delete obj . config . domains [ i ] . userblockedip ; } else { obj . config . domains [ i ] . userblockedip = obj . config . domains [ i ] . userblockedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . agentallowedip == 'string' ) { if ( obj . config . domains [ i ] . agentallowedip == '' ) { delete obj . config . domains [ i ] . agentallowedip ; } else { obj . config . domains [ i ] . agentallowedip = obj . config . domains [ i ] . agentallowedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
if ( typeof obj . config . domains [ i ] . agentblockedip == 'string' ) { if ( obj . config . domains [ i ] . agentblockedip == '' ) { delete obj . config . domains [ i ] . agentblockedip ; } else { obj . config . domains [ i ] . agentblockedip = obj . config . domains [ i ] . agentblockedip . split ( ' ' ) . join ( '' ) . split ( ',' ) ; } }
2020-12-31 03:54:02 +03:00
if ( typeof obj . config . domains [ i ] . ignoreagenthashcheck == 'string' ) { if ( obj . config . domains [ i ] . ignoreagenthashcheck == '' ) { delete obj . config . domains [ i ] . ignoreagenthashcheck ; } else { obj . config . domains [ i ] . ignoreagenthashcheck = obj . config . domains [ i ] . ignoreagenthashcheck . split ( ',' ) ; } }
2024-02-17 22:22:38 +03:00
if ( typeof obj . config . domains [ i ] . allowedorigin == 'string' ) { if ( obj . config . domains [ i ] . allowedorigin == '' ) { delete obj . config . domains [ i ] . allowedorigin ; } else { obj . config . domains [ i ] . allowedorigin = obj . config . domains [ i ] . allowedorigin . split ( ',' ) ; } }
2019-11-29 07:57:34 +03:00
if ( ( obj . config . domains [ i ] . passwordrequirements != null ) && ( typeof obj . config . domains [ i ] . passwordrequirements == 'object' ) ) {
if ( typeof obj . config . domains [ i ] . passwordrequirements . skip2factor == 'string' ) {
obj . config . domains [ i ] . passwordrequirements . skip2factor = obj . config . domains [ i ] . passwordrequirements . skip2factor . split ( ',' ) ;
} else {
delete obj . config . domains [ i ] . passwordrequirements . skip2factor ;
}
2022-03-25 01:05:20 +03:00
// Fix the list of users to add "user/domain/" if needed
if ( Array . isArray ( obj . config . domains [ i ] . passwordrequirements . logintokens ) ) {
var newValues = [ ] ;
for ( var j in obj . config . domains [ i ] . passwordrequirements . logintokens ) {
var splitVal = obj . config . domains [ i ] . passwordrequirements . logintokens [ j ] . split ( '/' ) ; ;
if ( splitVal . length == 1 ) { newValues . push ( 'user/' + i + '/' + splitVal [ 0 ] ) ; }
if ( splitVal . length == 2 ) { newValues . push ( 'user/' + splitVal [ 0 ] + '/' + splitVal [ 1 ] ) ; }
if ( splitVal . length == 3 ) { newValues . push ( splitVal [ 0 ] + '/' + splitVal [ 1 ] + '/' + splitVal [ 2 ] ) ; }
}
obj . config . domains [ i ] . passwordrequirements . logintokens = newValues ;
}
2019-11-29 07:57:34 +03:00
}
2019-04-11 23:41:51 +03: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-12 00:52:23 +03: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.
2020-11-29 00:08:57 +03:00
if ( obj . config . domains [ i ] . sitestyle == null ) { obj . config . domains [ i ] . sitestyle = 2 ; } // Default to site style #2
2019-05-13 05:14:24 +03:00
// Convert newAccountsRights from a array of strings to flags number.
2020-05-29 04:04:30 +03:00
obj . config . domains [ i ] . newaccountsrights = obj . common . meshServerRightsArrayToNumber ( obj . config . domains [ i ] . newaccountsrights ) ;
if ( typeof ( obj . config . domains [ i ] . newaccountsrights ) != 'number' ) { delete obj . config . domains [ i ] . newaccountsrights ; }
2020-03-25 23:21:14 +03:00
// Check if there is a web views path and/or web public path for this domain
if ( ( _ _dirname . endsWith ( '/node_modules/meshcentral' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral' ) ) || ( _ _dirname . endsWith ( '/node_modules/meshcentral/' ) ) || ( _ _dirname . endsWith ( '\\node_modules\\meshcentral\\' ) ) ) {
if ( ( obj . config . domains [ i ] . webviewspath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/views' ) ) ) ) { obj . config . domains [ i ] . webviewspath = obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/views' ) ; }
if ( ( obj . config . domains [ i ] . webpublicpath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/public' ) ) ) ) { obj . config . domains [ i ] . webpublicpath = obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/public' ) ; }
2020-04-05 05:29:20 +03:00
if ( ( obj . config . domains [ i ] . webemailspath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/emails' ) ) ) ) { obj . config . domains [ i ] . webemailspath = obj . path . join ( _ _dirname , '../../meshcentral-web-' + i + '/emails' ) ; }
2020-03-25 23:21:14 +03:00
} else {
if ( ( obj . config . domains [ i ] . webviewspath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/views' ) ) ) ) { obj . config . domains [ i ] . webviewspath = obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/views' ) ; }
if ( ( obj . config . domains [ i ] . webpublicpath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/public' ) ) ) ) { obj . config . domains [ i ] . webpublicpath = obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/public' ) ; }
2020-04-05 05:29:20 +03:00
if ( ( obj . config . domains [ i ] . webemailspath == null ) && ( obj . fs . existsSync ( obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/emails' ) ) ) ) { obj . config . domains [ i ] . webemailspath = obj . path . join ( _ _dirname , '../meshcentral-web-' + i + '/emails' ) ; }
2020-03-25 23:21:14 +03:00
}
2020-11-12 03:28:56 +03:00
// Check agent customization if any
if ( typeof obj . config . domains [ i ] . agentcustomization == 'object' ) {
if ( typeof obj . config . domains [ i ] . agentcustomization . displayname != 'string' ) { delete obj . config . domains [ i ] . agentcustomization . displayname ; } else { obj . config . domains [ i ] . agentcustomization . displayname = obj . config . domains [ i ] . agentcustomization . displayname . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; }
if ( typeof obj . config . domains [ i ] . agentcustomization . description != 'string' ) { delete obj . config . domains [ i ] . agentcustomization . description ; } else { obj . config . domains [ i ] . agentcustomization . description = obj . config . domains [ i ] . agentcustomization . description . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; }
if ( typeof obj . config . domains [ i ] . agentcustomization . companyname != 'string' ) { delete obj . config . domains [ i ] . agentcustomization . companyname ; } else { obj . config . domains [ i ] . agentcustomization . companyname = obj . config . domains [ i ] . agentcustomization . companyname . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; }
if ( typeof obj . config . domains [ i ] . agentcustomization . servicename != 'string' ) { delete obj . config . domains [ i ] . agentcustomization . servicename ; } else { obj . config . domains [ i ] . agentcustomization . servicename = obj . config . domains [ i ] . agentcustomization . servicename . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) . split ( ' ' ) . join ( '' ) . split ( '"' ) . join ( '' ) . split ( '\'' ) . join ( '' ) . split ( '>' ) . join ( '' ) . split ( '<' ) . join ( '' ) . split ( '/' ) . join ( '' ) . split ( '\\' ) . join ( '' ) ; }
2022-01-19 00:34:10 +03:00
if ( typeof obj . config . domains [ i ] . agentcustomization . image != 'string' ) { delete obj . config . domains [ i ] . agentcustomization . image ; } else { try { obj . config . domains [ i ] . agentcustomization . image = 'data:image/png;base64,' + Buffer . from ( obj . fs . readFileSync ( obj . getConfigFilePath ( obj . config . domains [ i ] . agentcustomization . image ) ) , 'binary' ) . toString ( 'base64' ) ; } catch ( ex ) { console . log ( ex ) ; delete obj . config . domains [ i ] . agentcustomization . image ; } }
2020-11-12 03:28:56 +03:00
} else {
delete obj . config . domains [ i ] . agentcustomization ;
}
2020-11-27 20:23:03 +03:00
// Convert user consent flags
if ( typeof obj . config . domains [ i ] . userconsentflags == 'object' ) {
var flags = 0 ;
if ( obj . config . domains [ i ] . userconsentflags . desktopnotify == true ) { flags |= 1 ; }
if ( obj . config . domains [ i ] . userconsentflags . terminalnotify == true ) { flags |= 2 ; }
if ( obj . config . domains [ i ] . userconsentflags . filenotify == true ) { flags |= 4 ; }
if ( obj . config . domains [ i ] . userconsentflags . desktopprompt == true ) { flags |= 8 ; }
if ( obj . config . domains [ i ] . userconsentflags . terminalprompt == true ) { flags |= 16 ; }
if ( obj . config . domains [ i ] . userconsentflags . fileprompt == true ) { flags |= 32 ; }
if ( obj . config . domains [ i ] . userconsentflags . desktopprivacybar == true ) { flags |= 64 ; }
obj . config . domains [ i ] . userconsentflags = flags ;
}
2021-01-20 20:54:51 +03:00
// If we have Intel AMT manager settings, take a look at them here.
if ( typeof obj . config . domains [ i ] . amtmanager == 'object' ) {
if ( typeof obj . config . domains [ i ] . amtmanager . tlsrootcert == 'object' ) {
obj . config . domains [ i ] . amtmanager . tlsrootcert2 = obj . certificateOperations . loadGenericCertAndKey ( obj . config . domains [ i ] . amtmanager . tlsrootcert ) ;
if ( obj . config . domains [ i ] . amtmanager . tlsrootcert2 == null ) { // Show an error message if needed
if ( i == '' ) {
2021-09-02 06:06:14 +03:00
addServerWarning ( "Unable to load Intel AMT TLS root certificate for default domain." , 5 ) ;
2021-01-20 20:54:51 +03:00
} else {
2024-03-10 10:46:01 +03:00
addServerWarning ( "Unable to load Intel AMT TLS root certificate for domain " + i + "." , 6 , [ i ] ) ;
2021-01-20 20:54:51 +03:00
}
}
}
}
2022-08-04 11:47:36 +03:00
// Check agentfileinfo
if ( typeof obj . config . domains [ i ] . agentfileinfo == 'object' ) {
if ( ( obj . config . domains [ i ] . agentfileinfo . fileversionnumber != null ) && ( obj . common . parseVersion ( obj . config . domains [ i ] . agentfileinfo . fileversionnumber ) == null ) ) { delete obj . config . domains [ i ] . agentfileinfo . fileversionnumber ; }
if ( ( obj . config . domains [ i ] . agentfileinfo . productversionnumber != null ) && ( obj . common . parseVersion ( obj . config . domains [ i ] . agentfileinfo . productversionnumber ) == null ) ) { delete obj . config . domains [ i ] . agentfileinfo . productversionnumber ; }
if ( ( obj . config . domains [ i ] . agentfileinfo . fileversionnumber == null ) && ( typeof obj . config . domains [ i ] . agentfileinfo . fileversion == 'string' ) && ( obj . common . parseVersion ( obj . config . domains [ i ] . agentfileinfo . fileversion ) != null ) ) { obj . config . domains [ i ] . agentfileinfo . fileversionnumber = obj . config . domains [ i ] . agentfileinfo . fileversion ; }
2022-08-10 23:12:59 +03:00
if ( typeof obj . config . domains [ i ] . agentfileinfo . icon == 'string' ) {
// Load the agent .ico file
var icon = null ;
try { icon = require ( './authenticode.js' ) . loadIcon ( obj . path . join ( obj . datapath , obj . config . domains [ i ] . agentfileinfo . icon ) ) ; } catch ( ex ) { }
if ( icon != null ) {
// The icon file was correctly loaded
obj . config . domains [ i ] . agentfileinfo . icon = icon ;
} else {
// Failed to load the icon file, display a server warning
addServerWarning ( "Unable to load agent icon file: " + obj . config . domains [ i ] . agentfileinfo . icon + "." , 23 , [ obj . config . domains [ i ] . agentfileinfo . icon ] ) ;
delete obj . config . domains [ i ] . agentfileinfo . icon ;
}
} else {
// Invalid icon file path
delete obj . config . domains [ i ] . agentfileinfo . icon ;
}
2022-08-12 03:37:42 +03:00
if ( typeof obj . config . domains [ i ] . agentfileinfo . logo == 'string' ) {
// Load the agent .bmp file
var logo = null ;
try { logo = require ( './authenticode.js' ) . loadBitmap ( obj . path . join ( obj . datapath , obj . config . domains [ i ] . agentfileinfo . logo ) ) ; } catch ( ex ) { }
if ( logo != null ) {
// The logo file was correctly loaded
obj . config . domains [ i ] . agentfileinfo . logo = logo ;
} else {
// Failed to load the icon file, display a server warning
addServerWarning ( "Unable to load agent logo file: " + obj . config . domains [ i ] . agentfileinfo . logo + "." , 24 , [ obj . config . domains [ i ] . agentfileinfo . logo ] ) ;
delete obj . config . domains [ i ] . agentfileinfo . logo ;
}
} else {
// Invalid icon file path
delete obj . config . domains [ i ] . agentfileinfo . logo ;
}
2022-08-04 11:47:36 +03:00
}
2019-02-03 01:54:36 +03: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 ; }
2021-09-02 06:06:14 +03:00
if ( ( obj . args . ciralocalfqdn != null ) && ( ( obj . args . lanonly == true ) || ( obj . args . wanonly == true ) ) ) { addServerWarning ( "CIRA local FQDN's ignored when server in LAN-only or WAN-only mode." , 7 ) ; }
if ( ( obj . args . ciralocalfqdn != null ) && ( obj . args . ciralocalfqdn . split ( ',' ) . length > 4 ) ) { addServerWarning ( "Can't have more than 4 CIRA local FQDN's. Ignoring value." , 8 ) ; obj . args . ciralocalfqdn = null ; }
if ( obj . args . ignoreagenthashcheck === true ) { addServerWarning ( "Agent hash checking is being skipped, this is unsafe." , 9 ) ; }
2020-11-05 13:27:39 +03:00
if ( obj . args . port == null || typeof obj . args . port != 'number' ) { obj . args . port = 443 ; }
2019-02-03 01:54:36 +03:00
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 ;
2019-11-18 00:35:50 +03:00
if ( obj . args . rediraliasport != null && ( typeof obj . args . rediraliasport != 'number' ) ) obj . args . rediraliasport = null ;
2020-11-05 13:27:39 +03:00
if ( obj . args . redirport == null ) obj . args . redirport = 80 ;
2022-09-29 22:07:50 +03:00
if ( obj . args . minifycore == null ) obj . args . minifycore = false ;
2019-06-18 01:20:47 +03:00
if ( typeof args . agentidletimeout != 'number' ) { args . agentidletimeout = 150000 ; } else { args . agentidletimeout *= 1000 } // Default agent idle timeout is 2m, 30sec.
2020-11-28 21:36:25 +03:00
if ( ( obj . args . lanonly != true ) && ( obj . args . webrtconfig == null ) ) { obj . args . webrtconfig = { iceservers : [ { urls : 'stun:stun.l.google.com:19302' } , { urls : 'stun:stun.services.mozilla.com' } ] } ; } // Setup default WebRTC STUN servers
2020-12-31 03:54:02 +03:00
if ( typeof obj . args . ignoreagenthashcheck == 'string' ) { if ( obj . args . ignoreagenthashcheck == '' ) { delete obj . args . ignoreagenthashcheck ; } else { obj . args . ignoreagenthashcheck = obj . args . ignoreagenthashcheck . split ( ',' ) ; } }
2019-02-03 01:54:36 +03: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 ] ; }
2019-10-30 02:17:29 +03:00
else { console . log ( "Invalid administrator name." ) ; process . exit ( ) ; return ; }
2019-02-03 01:54:36 +03:00
obj . db . Get ( adminname , function ( err , user ) {
2019-10-30 02:17:29 +03:00
if ( user . length != 1 ) { console . log ( "Invalid user name." ) ; process . exit ( ) ; return ; }
2019-06-27 08:13:56 +03:00
user [ 0 ] . siteadmin = 4294967295 ; // 0xFFFFFFFF
2019-02-03 01:54:36 +03:00
obj . db . Set ( user [ 0 ] , function ( ) {
2019-12-01 23:52:32 +03:00
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." ) ; }
2019-02-03 01:54:36 +03:00
process . exit ( ) ;
2017-08-28 19:27:45 +03:00
return ;
2019-02-03 01:54:36 +03: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 ] ; }
2019-10-30 02:17:29 +03:00
else { console . log ( "Invalid administrator name." ) ; process . exit ( ) ; return ; }
2019-02-03 01:54:36 +03:00
obj . db . Get ( adminname , function ( err , user ) {
2019-10-30 02:17:29 +03:00
if ( user . length != 1 ) { console . log ( "Invalid user name." ) ; process . exit ( ) ; return ; }
2019-02-03 01:54:36 +03:00
if ( user [ 0 ] . siteadmin ) { delete user [ 0 ] . siteadmin ; }
obj . db . Set ( user [ 0 ] , function ( ) {
2019-12-01 23:52:32 +03:00
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." ) ; }
2019-02-03 01:54:36 +03:00
process . exit ( ) ;
return ;
} ) ;
} ) ;
return ;
}
2017-08-28 19:27:45 +03:00
2021-04-18 00:44:19 +03:00
// Setup agent error log
if ( ( obj . config ) && ( obj . config . settings ) && ( obj . config . settings . agentlogdump != null ) ) {
obj . fs . open ( obj . path . join ( obj . datapath , 'agenterrorlogs.txt' ) , 'a' , function ( err , fd ) { obj . agentErrorLog = fd ; } )
}
2019-02-03 01:54:36 +03:00
// Perform other database cleanup
obj . db . cleanup ( ) ;
// Set all nodes to power state of unknown (0)
2019-02-19 01:32:55 +03: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 19:27:45 +03:00
2019-02-03 01:54:36 +03:00
// Read or setup database configuration values
obj . db . Get ( 'dbconfig' , function ( err , dbconfig ) {
2020-11-03 13:58:29 +03:00
if ( ( dbconfig != null ) && ( dbconfig . length == 1 ) ) { obj . dbconfig = dbconfig [ 0 ] ; } else { obj . dbconfig = { _id : 'dbconfig' , version : 1 } ; }
2019-02-03 01:54:36 +03:00
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 ( ) ; }
2022-04-14 23:07:05 +03:00
const username = buf . toString ( 'hex' ) ;
const nodeid = obj . args . getwspass ;
const pass = obj . crypto . createHash ( 'sha384' ) . update ( username . toLowerCase ( ) + ':' + nodeid + ':' + obj . dbconfig . amtWsEventSecret ) . digest ( 'base64' ) . substring ( 0 , 12 ) . split ( '/' ) . join ( 'x' ) . split ( '\\' ) . join ( 'x' ) ;
2019-10-30 02:17:29 +03:00
console . log ( "--- Intel(r) AMT WSMAN eventing credentials ---" ) ;
console . log ( "Username: " + username ) ;
console . log ( "Password: " + pass ) ;
console . log ( "Argument: " + nodeid ) ;
2019-02-03 01:54:36 +03:00
process . exit ( ) ;
} ) ;
2018-01-15 08:01:06 +03:00
} else {
2019-10-30 02:17:29 +03:00
console . log ( "Invalid NodeID." ) ;
2019-02-03 01:54:36 +03:00
process . exit ( ) ;
2018-01-15 08:01:06 +03:00
}
2019-02-03 01:54:36 +03:00
return ;
}
2022-08-19 04:03:03 +03:00
// Setup the task manager
if ( ( obj . config ) && ( obj . config . settings ) && ( obj . config . settings . taskmanager == true ) ) {
obj . taskManager = require ( './taskmanager' ) . createTaskManager ( obj ) ;
}
2019-10-10 21:13:25 +03:00
// Start plugin manager if configuration allows this.
2020-03-27 09:01:49 +03:00
if ( ( obj . config ) && ( obj . config . settings ) && ( obj . config . settings . plugins != null ) && ( obj . config . settings . plugins != false ) && ( ( typeof obj . config . settings . plugins != 'object' ) || ( obj . config . settings . plugins . enabled != false ) ) ) {
2021-05-28 01:25:54 +03:00
obj . pluginHandler = require ( './pluginHandler.js' ) . pluginHandler ( obj ) ;
2019-10-10 21:13:25 +03:00
}
2019-02-03 01:54:36 +03:00
// 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 08:01:06 +03:00
} ) ;
2019-02-03 01:54:36 +03:00
}
2018-01-15 08:01:06 +03:00
// Done starting the redirection server, go on to load the server certificates
obj . StartEx2 = function ( ) {
// Load server certificates
2019-02-03 01:54:36 +03:00
obj . certificateOperations . GetMeshServerCertificate ( obj . args , obj . config , function ( certs ) {
2019-11-16 04:55:05 +03:00
// Get the current node version
2021-05-28 01:25:54 +03:00
if ( ( obj . config . letsencrypt == null ) || ( obj . redirserver == null ) ) {
2018-01-15 08:01:06 +03:00
obj . StartEx3 ( certs ) ; // Just use the configured certificates
2020-02-07 05:04:55 +03:00
} else if ( ( obj . config . letsencrypt != null ) && ( obj . config . letsencrypt . nochecks == true ) ) {
// Use Let's Encrypt with no checking
2020-03-12 02:53:09 +03:00
obj . letsencrypt = require ( './letsencrypt.js' ) . CreateLetsEncrypt ( obj ) ;
2020-02-07 05:04:55 +03:00
obj . letsencrypt . getCertificate ( certs , obj . StartEx3 ) ; // Use Let's Encrypt with no checking, use at your own risk.
2018-01-15 08:01:06 +03:00
} else {
2019-12-05 00:45:29 +03:00
// Check Let's Encrypt settings
var leok = true ;
2020-04-25 00:58:21 +03:00
if ( ( typeof obj . config . letsencrypt . names != 'string' ) && ( typeof obj . config . settings . cert == 'string' ) ) { obj . config . letsencrypt . names = obj . config . settings . cert ; }
2021-09-02 06:06:14 +03:00
if ( typeof obj . config . letsencrypt . email != 'string' ) { leok = false ; addServerWarning ( "Missing Let's Encrypt email address." , 10 ) ; }
else if ( typeof obj . config . letsencrypt . names != 'string' ) { leok = false ; addServerWarning ( "Invalid Let's Encrypt host names." , 11 ) ; }
else if ( obj . config . letsencrypt . names . indexOf ( '*' ) >= 0 ) { leok = false ; addServerWarning ( "Invalid Let's Encrypt names, can't contain a *." , 12 ) ; }
else if ( obj . config . letsencrypt . email . split ( '@' ) . length != 2 ) { leok = false ; addServerWarning ( "Invalid Let's Encrypt email address." , 10 ) ; }
else if ( obj . config . letsencrypt . email . trim ( ) !== obj . config . letsencrypt . email ) { leok = false ; addServerWarning ( "Invalid Let's Encrypt email address." , 10 ) ; }
2019-12-05 00:45:29 +03:00
else {
2022-04-14 23:07:05 +03:00
const le = require ( './letsencrypt.js' ) ;
2020-03-12 02:53:09 +03:00
try { obj . letsencrypt = le . CreateLetsEncrypt ( obj ) ; } catch ( ex ) { console . log ( ex ) ; }
2021-09-02 06:06:14 +03:00
if ( obj . letsencrypt == null ) { addServerWarning ( "Unable to setup Let's Encrypt module." , 13 ) ; leok = false ; }
2019-12-05 00:45:29 +03:00
}
if ( leok == true ) {
2019-12-09 07:46:25 +03:00
// Check that the email address domain MX resolves.
require ( 'dns' ) . resolveMx ( obj . config . letsencrypt . email . split ( '@' ) [ 1 ] , function ( err , addresses ) {
if ( err == null ) {
// Check that all names resolve
checkResolveAll ( obj . config . letsencrypt . names . split ( ',' ) , function ( err ) {
if ( err == null ) {
obj . letsencrypt . getCertificate ( certs , obj . StartEx3 ) ; // Use Let's Encrypt
} else {
2021-09-02 06:06:14 +03:00
for ( var i in err ) { addServerWarning ( "Invalid Let's Encrypt names, unable to resolve: " + err [ i ] , 14 , [ err [ i ] ] ) ; }
2019-12-09 07:46:25 +03:00
obj . StartEx3 ( certs ) ; // Let's Encrypt did not load, just use the configured certificates
}
} ) ;
} else {
2021-09-02 06:06:14 +03:00
addServerWarning ( "Invalid Let's Encrypt email address, unable to resolve: " + obj . config . letsencrypt . email . split ( '@' ) [ 1 ] , 15 , [ obj . config . letsencrypt . email . split ( '@' ) [ 1 ] ] ) ;
2019-12-09 07:46:25 +03:00
obj . StartEx3 ( certs ) ; // Let's Encrypt did not load, just use the configured certificates
}
} ) ;
2018-01-15 08:01:06 +03:00
} else {
obj . StartEx3 ( certs ) ; // Let's Encrypt did not load, just use the configured certificates
}
}
} ) ;
2018-08-30 22:05:23 +03:00
} ;
2018-01-15 08:01:06 +03:00
2018-11-01 02:03:09 +03:00
// Start the server with the given certificates, but check if we have web certificates to load
2018-01-15 08:01:06 +03:00
obj . StartEx3 = function ( certs ) {
obj . certificates = certs ;
obj . certificateOperations . acceleratorStart ( certs ) ; // Set the state of the accelerators
2017-08-28 19:27:45 +03:00
2018-11-01 02:03:09 +03:00
// Load any domain web certificates
2019-10-30 02:17:29 +03:00
for ( var i in obj . config . domains ) {
2019-06-13 05:40:27 +03:00
// Load any Intel AMT ACM activation certificates
2020-10-27 11:42:47 +03:00
if ( obj . config . domains [ i ] . amtacmactivation == null ) { obj . config . domains [ i ] . amtacmactivation = { } ; }
2019-06-20 03:16:50 +03:00
obj . certificateOperations . loadIntelAmtAcmCerts ( obj . config . domains [ i ] . amtacmactivation ) ;
2022-02-24 22:00:40 +03:00
if ( obj . config . domains [ i ] . amtacmactivation . acmCertErrors != null ) { for ( var j in obj . config . domains [ i ] . amtacmactivation . acmCertErrors ) { obj . addServerWarning ( obj . config . domains [ i ] . amtacmactivation . acmCertErrors [ j ] ) ; } }
2019-10-30 02:17:29 +03:00
if ( typeof obj . config . domains [ i ] . certurl == 'string' ) {
obj . supportsProxyCertificatesRequest = true ; // If a certurl is set, enable proxy cert requests
// Then, fix the URL and add 'https://' if needed
2018-11-03 04:13:32 +03:00
if ( obj . config . domains [ i ] . certurl . indexOf ( '://' ) < 0 ) { obj . config . domains [ i ] . certurl = 'https://' + obj . config . domains [ i ] . certurl ; }
2018-11-01 02:03:09 +03:00
}
}
2020-12-11 00:56:15 +03:00
// Load CloudFlare trusted proxies list if needed
2020-12-11 02:16:02 +03:00
if ( ( obj . config . settings . trustedproxy != null ) && ( typeof obj . config . settings . trustedproxy == 'string' ) && ( obj . config . settings . trustedproxy . toLowerCase ( ) == 'cloudflare' ) ) {
2021-01-07 01:23:07 +03:00
obj . config . settings . extrascriptsrc = 'ajax.cloudflare.com' ; // Add CloudFlare as a trusted script source. This allows for CloudFlare's RocketLoader feature.
2020-12-11 00:56:15 +03:00
delete obj . args . trustedproxy ;
delete obj . config . settings . trustedproxy ;
obj . certificateOperations . loadTextFile ( 'https://www.cloudflare.com/ips-v4' , null , function ( url , data , tag ) {
if ( data != null ) {
if ( Array . isArray ( obj . args . trustedproxy ) == false ) { obj . args . trustedproxy = [ ] ; }
2022-04-14 23:07:05 +03:00
const ipranges = data . split ( '\n' ) ;
2020-12-11 00:56:15 +03:00
for ( var i in ipranges ) { if ( ipranges [ i ] != '' ) { obj . args . trustedproxy . push ( ipranges [ i ] ) ; } }
obj . certificateOperations . loadTextFile ( 'https://www.cloudflare.com/ips-v6' , null , function ( url , data , tag ) {
if ( data != null ) {
var ipranges = data . split ( '\n' ) ;
for ( var i in ipranges ) { if ( ipranges [ i ] != '' ) { obj . args . trustedproxy . push ( ipranges [ i ] ) ; } }
obj . config . settings . trustedproxy = obj . args . trustedproxy ;
} else {
2021-09-02 06:06:14 +03:00
addServerWarning ( "Unable to load CloudFlare trusted proxy IPv6 address list." , 16 ) ;
2020-12-11 00:56:15 +03:00
}
obj . StartEx4 ( ) ; // Keep going
} ) ;
} else {
2021-09-02 06:06:14 +03:00
addServerWarning ( "Unable to load CloudFlare trusted proxy IPv4 address list." , 16 ) ;
2020-12-11 00:56:15 +03:00
obj . StartEx4 ( ) ; // Keep going
}
} ) ;
} else {
obj . StartEx4 ( ) ; // Keep going
}
2018-11-01 02:03:09 +03:00
}
// Start the server with the given certificates
obj . StartEx4 = function ( ) {
var i ;
2018-01-15 08:01:06 +03:00
// If the certificate is un-configured, force LAN-only mode
2019-03-05 10:48:45 +03:00
if ( obj . certificates . CommonName . indexOf ( '.' ) == - 1 ) { /*console.log('Server name not configured, running in LAN-only mode.');*/ obj . args . lanonly = true ; }
2018-11-13 05:36:20 +03:00
// Write server version and run mode
2022-04-14 23:07:05 +03:00
const productionMode = ( process . env . NODE _ENV && ( process . env . NODE _ENV == 'production' ) ) ;
const runmode = ( obj . args . lanonly ? 2 : ( obj . args . wanonly ? 1 : 0 ) ) ;
2021-05-03 11:58:49 +03:00
console . log ( "MeshCentral v" + getCurrentVersion ( ) + ', ' + ( [ "Hybrid (LAN + WAN) mode" , "WAN mode" , "LAN mode" ] [ runmode ] ) + ( productionMode ? ", Production mode." : '.' ) ) ;
2017-08-28 19:27:45 +03:00
2018-01-15 08:01:06 +03:00
// Check that no sub-domains have the same DNS as the parent
2018-08-30 22:05:23 +03:00
for ( i in obj . config . domains ) {
2018-01-15 08:01:06 +03: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-03 03:52:49 +03:00
2020-11-03 08:41:58 +03:00
// Load the list of MeshCentral tools
obj . updateMeshTools ( ) ;
2020-12-16 02:47:29 +03:00
// Load MeshAgent translation strings
2020-12-16 22:33:24 +03:00
try {
2022-01-19 21:59:23 +03:00
var translationpath = obj . path . join ( _ _dirname , 'agents' , 'agent-translations.json' ) ;
2022-04-14 23:07:05 +03:00
const translationpath2 = obj . path . join ( obj . datapath , 'agents' , 'agent-translations.json' ) ;
2022-01-19 21:59:23 +03:00
if ( obj . fs . existsSync ( translationpath2 ) ) { translationpath = translationpath2 ; } // If the agent is present in "meshcentral-data/agents", use that one instead.
var translations = JSON . parse ( obj . fs . readFileSync ( translationpath ) . toString ( ) ) ;
2020-12-16 22:33:24 +03:00
if ( translations [ 'zh-chs' ] ) { translations [ 'zh-hans' ] = translations [ 'zh-chs' ] ; delete translations [ 'zh-chs' ] ; }
if ( translations [ 'zh-cht' ] ) { translations [ 'zh-hant' ] = translations [ 'zh-cht' ] ; delete translations [ 'zh-cht' ] ; }
2022-02-05 01:02:21 +03:00
// If there is domain customizations to the agent strings, do this here.
for ( var i in obj . config . domains ) {
var domainTranslations = translations ;
if ( ( typeof obj . config . domains [ i ] . agentcustomization == 'object' ) && ( typeof obj . config . domains [ i ] . agentcustomization . installtext == 'string' ) ) {
domainTranslations = Object . assign ( { } , domainTranslations ) ; // Shallow clone
for ( var j in domainTranslations ) { delete domainTranslations [ j ] . description ; }
domainTranslations . en . description = obj . config . domains [ i ] . agentcustomization . installtext ;
}
2022-02-05 02:02:09 +03:00
obj . config . domains [ i ] . agentTranslations = JSON . stringify ( domainTranslations ) ;
2022-02-05 01:02:21 +03:00
}
2020-12-16 22:33:24 +03:00
} catch ( ex ) { }
2020-12-16 02:47:29 +03:00
2022-03-05 20:35:04 +03:00
// Load any domain specific agents
2022-05-29 08:34:48 +03:00
for ( var i in obj . config . domains ) { if ( ( i != '' ) && ( obj . config . domains [ i ] . share == null ) ) { obj . updateMeshAgentsTable ( obj . config . domains [ i ] , function ( ) { } ) ; } }
2022-03-05 20:35:04 +03:00
2018-01-15 08:01:06 +03:00
// Load the list of mesh agents and install scripts
2021-02-17 10:58:40 +03:00
if ( ( obj . args . noagentupdate == 1 ) || ( obj . args . noagentupdate == true ) ) { for ( i in obj . meshAgentsArchitectureNumbers ) { obj . meshAgentsArchitectureNumbers [ i ] . update = false ; } }
2022-06-18 10:07:45 +03:00
obj . signMeshAgents ( obj . config . domains [ '' ] , function ( ) {
obj . updateMeshAgentsTable ( obj . config . domains [ '' ] , function ( ) {
obj . updateMeshAgentInstallScripts ( ) ;
// Setup and start the web server
obj . crypto . randomBytes ( 48 , function ( err , buf ) {
// Setup Mesh Multi-Server if needed
obj . multiServer = require ( './multiserver.js' ) . CreateMultiServer ( obj , obj . args ) ;
if ( obj . multiServer != null ) {
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 ; }
if ( typeof obj . args . sessionkey != 'string' ) { console . log ( "ERROR: Multi-server support requires \"SessionKey\" be set in the settings section of config.json, same key for all servers." ) ; process . exit ( 0 ) ; return ; }
obj . serverId = obj . multiServer . serverid ;
for ( var serverid in obj . config . peers . servers ) { obj . peerConnectivityByNode [ serverid ] = { } ; }
}
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// 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-23 02:18:01 +03:00
2022-06-18 10:07:45 +03:00
// Set the session length to 60 minutes if not set and set a random key if needed
if ( ( obj . args . sessiontime != null ) && ( ( typeof obj . args . sessiontime != 'number' ) || ( obj . args . sessiontime < 1 ) ) ) { delete obj . args . sessiontime ; }
if ( typeof obj . args . sessionkey != 'string' ) { obj . args . sessionkey = buf . toString ( 'hex' ) . toUpperCase ( ) ; }
2019-10-03 03:57:18 +03:00
2022-06-18 10:07:45 +03:00
// Create MQTT Broker to hook into webserver and mpsserver
if ( ( typeof obj . config . settings . mqtt == 'object' ) && ( typeof obj . config . settings . mqtt . auth == 'object' ) && ( typeof obj . config . settings . mqtt . auth . keyid == 'string' ) && ( typeof obj . config . settings . mqtt . auth . key == 'string' ) ) { obj . mqttbroker = require ( "./mqttbroker.js" ) . CreateMQTTBroker ( obj , obj . db , obj . args ) ; }
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Start the web server and if needed, the redirection web server.
obj . webserver = require ( './webserver.js' ) . CreateWebServer ( obj , obj . db , obj . args , obj . certificates , obj . StartEx5 ) ;
if ( obj . redirserver != null ) { obj . redirserver . hookMainWebServer ( obj . certificates ) ; }
2021-04-24 00:17:13 +03:00
2022-07-10 11:32:11 +03:00
// Change RelayDNS to a array of strings
if ( typeof obj . args . relaydns == 'string' ) { obj . args . relaydns = [ obj . args . relaydns ] ; }
if ( obj . common . validateStrArray ( obj . args . relaydns , 1 ) == false ) { delete obj . args . relaydns ; }
2022-06-25 00:34:27 +03:00
// Start the HTTP relay web server if needed
2022-07-12 09:26:54 +03:00
if ( ( obj . args . relaydns == null ) && ( typeof obj . args . relayport == 'number' ) && ( obj . args . relayport != 0 ) ) {
2022-06-25 00:34:27 +03:00
obj . webrelayserver = require ( './webrelayserver.js' ) . CreateWebRelayServer ( obj , obj . db , obj . args , obj . certificates , function ( ) { } ) ;
}
2022-06-18 10:07:45 +03:00
// Update proxy certificates
if ( obj . supportsProxyCertificatesRequest == true ) { obj . updateProxyCertificates ( true ) ; }
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup the Intel AMT event handler
obj . amtEventHandler = require ( './amtevents.js' ) . CreateAmtEventsHandler ( obj ) ;
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup the Intel AMT local network scanner
if ( obj . args . wanonly != true ) {
if ( obj . args . amtscanner != false ) { obj . amtScanner = require ( './amtscanner.js' ) . CreateAmtScanner ( obj ) . start ( ) ; }
if ( obj . args . meshscanner != false ) { obj . meshScanner = require ( './meshscanner.js' ) . CreateMeshScanner ( obj ) . start ( ) ; }
}
2020-10-16 21:57:29 +03:00
2022-06-18 10:07:45 +03:00
// Setup and start the MPS server
obj . mpsserver = require ( './mpsserver.js' ) . CreateMpsServer ( obj , obj . db , obj . args , obj . certificates ) ;
2020-10-07 05:47:28 +03:00
2022-06-18 10:07:45 +03:00
// Setup the Intel AMT manager
if ( obj . args . amtmanager !== false ) {
obj . amtManager = require ( './amtmanager.js' ) . CreateAmtManager ( obj ) ;
}
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup and start the legacy swarm server
if ( ( obj . certificates . swarmserver != null ) && ( obj . args . swarmport != null ) && ( obj . args . swarmport !== 0 ) ) {
obj . swarmserver = require ( './swarmserver.js' ) . CreateSwarmServer ( obj , obj . db , obj . args , obj . certificates ) ;
}
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup the main email server
if ( obj . config . sendgrid != null ) {
2021-02-10 22:28:21 +03:00
// Sendgrid server
2022-06-18 10:07:45 +03:00
obj . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj ) ;
obj . mailserver . verify ( ) ;
2021-09-02 06:06:14 +03:00
if ( obj . args . lanonly == true ) { addServerWarning ( "SendGrid server has limited use in LAN mode." , 17 ) ; }
2022-06-18 10:07:45 +03:00
} else if ( obj . config . smtp != null ) {
2021-02-10 22:28:21 +03:00
// SMTP server
2022-06-18 10:07:45 +03:00
obj . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj ) ;
obj . mailserver . verify ( ) ;
2021-09-02 06:06:14 +03:00
if ( obj . args . lanonly == true ) { addServerWarning ( "SMTP server has limited use in LAN mode." , 18 ) ; }
2022-06-18 10:07:45 +03:00
} else if ( obj . config . sendmail != null ) {
2021-11-27 22:37:30 +03:00
// Sendmail server
2022-06-18 10:07:45 +03:00
obj . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj ) ;
obj . mailserver . verify ( ) ;
2021-11-27 22:37:30 +03:00
if ( obj . args . lanonly == true ) { addServerWarning ( "SMTP server has limited use in LAN mode." , 18 ) ; }
2021-02-10 22:28:21 +03:00
}
2022-06-18 10:07:45 +03:00
// Setup the email server for each domain
for ( i in obj . config . domains ) {
if ( obj . config . domains [ i ] . sendgrid != null ) {
// Sendgrid server
obj . config . domains [ i ] . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj , obj . config . domains [ i ] ) ;
obj . config . domains [ i ] . mailserver . verify ( ) ;
if ( obj . args . lanonly == true ) { addServerWarning ( "SendGrid server has limited use in LAN mode." , 17 ) ; }
} else if ( ( obj . config . domains [ i ] . smtp != null ) && ( obj . config . domains [ i ] . smtp . host != null ) && ( obj . config . domains [ i ] . smtp . from != null ) ) {
// SMTP server
obj . config . domains [ i ] . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj , obj . config . domains [ i ] ) ;
obj . config . domains [ i ] . mailserver . verify ( ) ;
if ( obj . args . lanonly == true ) { addServerWarning ( "SMTP server has limited use in LAN mode." , 18 ) ; }
} else if ( obj . config . domains [ i ] . sendmail != null ) {
// Sendmail server
obj . config . domains [ i ] . mailserver = require ( './meshmail.js' ) . CreateMeshMail ( obj , obj . config . domains [ i ] ) ;
obj . config . domains [ i ] . mailserver . verify ( ) ;
if ( obj . args . lanonly == true ) { addServerWarning ( "SMTP server has limited use in LAN mode." , 18 ) ; }
} else {
// Setup the parent mail server for this domain
if ( obj . mailserver != null ) { obj . config . domains [ i ] . mailserver = obj . mailserver ; }
}
}
2020-04-22 11:33:27 +03:00
2022-06-18 10:07:45 +03:00
// Setup SMS gateway
if ( config . sms != null ) {
obj . smsserver = require ( './meshsms.js' ) . CreateMeshSMS ( obj ) ;
if ( ( obj . smsserver != null ) && ( obj . args . lanonly == true ) ) { addServerWarning ( "SMS gateway has limited use in LAN mode." , 19 ) ; }
2021-02-09 06:31:29 +03:00
}
2022-10-22 17:23:55 +03:00
// Setup user messaging
if ( config . messaging != null ) {
obj . msgserver = require ( './meshmessaging.js' ) . CreateServer ( obj ) ;
}
2022-06-18 10:07:45 +03:00
// Setup web based push notifications
if ( ( typeof config . settings . webpush == 'object' ) && ( typeof config . settings . webpush . email == 'string' ) ) {
obj . webpush = require ( 'web-push' ) ;
var vapidKeys = null ;
try { vapidKeys = JSON . parse ( obj . fs . readFileSync ( obj . path . join ( obj . datapath , 'vapid.json' ) ) . toString ( ) ) ; } catch ( ex ) { }
if ( ( vapidKeys == null ) || ( typeof vapidKeys . publicKey != 'string' ) || ( typeof vapidKeys . privateKey != 'string' ) ) {
console . log ( "Generating web push VAPID keys..." ) ;
vapidKeys = obj . webpush . generateVAPIDKeys ( ) ;
2022-08-27 00:19:28 +03:00
obj . common . moveOldFiles ( [ obj . path . join ( obj . datapath , 'vapid.json' ) ] ) ;
2022-06-18 10:07:45 +03:00
obj . fs . writeFileSync ( obj . path . join ( obj . datapath , 'vapid.json' ) , JSON . stringify ( vapidKeys ) ) ;
}
obj . webpush . vapidPublicKey = vapidKeys . publicKey ;
obj . webpush . setVapidDetails ( 'mailto:' + config . settings . webpush . email , vapidKeys . publicKey , vapidKeys . privateKey ) ;
if ( typeof config . settings . webpush . gcmapi == 'string' ) { webpush . setGCMAPIKey ( config . settings . webpush . gcmapi ) ; }
}
2021-01-31 03:15:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup Firebase
if ( ( config . firebase != null ) && ( typeof config . firebase . senderid == 'string' ) && ( typeof config . firebase . serverkey == 'string' ) ) {
obj . firebase = require ( './firebase' ) . CreateFirebase ( obj , config . firebase . senderid , config . firebase . serverkey ) ;
} else if ( ( typeof config . firebaserelay == 'object' ) && ( typeof config . firebaserelay . url == 'string' ) ) {
// Setup the push messaging relay
obj . firebase = require ( './firebase' ) . CreateFirebaseRelay ( obj , config . firebaserelay . url , config . firebaserelay . key ) ;
} else if ( obj . config . settings . publicpushnotifications === true ) {
2024-01-17 05:57:57 +03:00
// Setup the Firebase push messaging relay using https://alt.meshcentral.com, this is the public push notification server.
obj . firebase = require ( './firebase' ) . CreateFirebaseRelay ( obj , 'https://alt.meshcentral.com/firebaserelay.aspx' ) ;
2022-06-18 10:07:45 +03:00
}
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Start periodic maintenance
obj . maintenanceTimer = setInterval ( obj . maintenanceActions , 1000 * 60 * 60 ) ; // Run this every hour
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Dispatch an event that the server is now running
obj . DispatchEvent ( [ '*' ] , obj , { etype : 'server' , action : 'started' , msg : 'Server started' } ) ;
// Plugin hook. Need to run something at server startup? This is the place.
if ( obj . pluginHandler ) { obj . pluginHandler . callHook ( 'server_startup' ) ; }
// Setup the login cookie encryption key
if ( ( obj . config ) && ( obj . config . settings ) && ( typeof obj . config . settings . logincookieencryptionkey == 'string' ) ) {
// We have a string, hash it and use that as a key
try { obj . loginCookieEncryptionKey = Buffer . from ( obj . config . settings . logincookieencryptionkey , 'hex' ) ; } catch ( ex ) { }
if ( ( obj . loginCookieEncryptionKey == null ) || ( obj . loginCookieEncryptionKey . length != 80 ) ) { addServerWarning ( "Invalid \"LoginCookieEncryptionKey\" in config.json." , 20 ) ; obj . loginCookieEncryptionKey = null ; }
}
2020-02-18 21:57:39 +03:00
2022-06-18 10:07:45 +03:00
// Login cookie encryption key not set, use one from the database
if ( obj . loginCookieEncryptionKey == null ) {
obj . db . Get ( 'LoginCookieEncryptionKey' , function ( err , docs ) {
if ( ( docs != null ) && ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
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 ( ) } ) ;
}
} ) ;
}
// Load the invitation link encryption key from the database
obj . db . Get ( 'InvitationLinkEncryptionKey' , function ( err , docs ) {
if ( ( docs != null ) && ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
obj . invitationLinkEncryptionKey = Buffer . from ( docs [ 0 ] . key , 'hex' ) ;
2018-01-15 08:01:06 +03:00
} else {
2022-06-18 10:07:45 +03:00
obj . invitationLinkEncryptionKey = obj . generateCookieKey ( ) ; obj . db . Set ( { _id : 'InvitationLinkEncryptionKey' , key : obj . invitationLinkEncryptionKey . toString ( 'hex' ) , time : Date . now ( ) } ) ;
2018-01-15 08:01:06 +03:00
}
2017-08-28 19:27:45 +03:00
} ) ;
2018-01-15 08:01:06 +03:00
2022-06-18 10:07:45 +03:00
// Setup Intel AMT hello server
if ( ( typeof config . settings . amtprovisioningserver == 'object' ) && ( typeof config . settings . amtprovisioningserver . devicegroup == 'string' ) && ( typeof config . settings . amtprovisioningserver . newmebxpassword == 'string' ) && ( typeof config . settings . amtprovisioningserver . trustedfqdn == 'string' ) && ( typeof config . settings . amtprovisioningserver . ip == 'string' ) ) {
obj . amtProvisioningServer = require ( './amtprovisioningserver' ) . CreateAmtProvisioningServer ( obj , config . settings . amtprovisioningserver ) ;
2019-06-08 03:11:56 +03:00
}
2021-03-11 06:01:10 +03:00
2022-06-18 10:07:45 +03:00
// Start collecting server stats every 5 minutes
obj . trafficStats = obj . webserver . getTrafficStats ( ) ;
setInterval ( function ( ) {
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.
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.
const expire = new Date ( ) ;
expire . setTime ( expire . getTime ( ) + ( 60 * 60 * 1000 * hours ) ) ;
// Get traffic data
var trafficStats = obj . webserver . getTrafficDelta ( obj . trafficStats ) ;
obj . trafficStats = trafficStats . current ;
var data = {
time : new Date ( ) ,
expire : expire ,
mem : process . memoryUsage ( ) ,
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
} ,
traffic : trafficStats . delta
} ;
try { data . cpu = require ( 'os' ) . loadavg ( ) ; } catch ( ex ) { }
if ( obj . mpsserver != null ) {
data . conn . am = 0 ;
for ( var i in obj . mpsserver . ciraConnections ) { data . conn . am += obj . mpsserver . ciraConnections [ i ] . length ; }
}
if ( obj . firstStats === true ) { delete obj . firstStats ; data . first = true ; }
if ( obj . multiServer != null ) { data . s = obj . multiServer . serverid ; }
obj . db . SetServerStats ( data ) ; // Save the stats to the database
obj . DispatchEvent ( [ '*' ] , obj , { action : 'servertimelinestats' , data : data } ) ; // Event the server stats
} , 300000 ) ;
obj . debug ( 'main' , "Server started" ) ;
if ( obj . args . nousers == true ) { obj . updateServerState ( 'nousers' , '1' ) ; }
obj . updateServerState ( 'state' , "running" ) ;
// Setup auto-backup defaults
if ( obj . config . settings . autobackup == null ) { obj . config . settings . autobackup = { backupintervalhours : 24 , keeplastdaysbackup : 10 } ; }
else if ( obj . config . settings . autobackup === false ) { delete obj . config . settings . autobackup ; }
// Check that autobackup path is not within the "meshcentral-data" folder.
if ( ( typeof obj . config . settings . autobackup == 'object' ) && ( typeof obj . config . settings . autobackup . backuppath == 'string' ) && ( obj . path . normalize ( obj . config . settings . autobackup . backuppath ) . startsWith ( obj . path . normalize ( obj . datapath ) ) ) ) {
addServerWarning ( "Backup path can't be set within meshcentral-data folder, backup settings ignored." , 21 ) ;
delete obj . config . settings . autobackup ;
2020-10-10 01:44:09 +03:00
}
2021-11-09 11:03:39 +03:00
2022-06-18 10:07:45 +03:00
// Load Intel AMT passwords from the "amtactivation.log" file
obj . loadAmtActivationLogPasswords ( function ( amtPasswords ) {
obj . amtPasswords = amtPasswords ;
} ) ;
2021-04-04 04:29:02 +03:00
2022-06-18 10:07:45 +03:00
// Setup users that can see all device groups
if ( typeof obj . config . settings . managealldevicegroups == 'string' ) { obj . config . settings . managealldevicegroups = obj . config . settings . managealldevicegroups . split ( ',' ) ; }
else if ( Array . isArray ( obj . config . settings . managealldevicegroups ) == false ) { obj . config . settings . managealldevicegroups = [ ] ; }
for ( i in obj . config . domains ) {
if ( Array . isArray ( obj . config . domains [ i ] . managealldevicegroups ) ) {
for ( var j in obj . config . domains [ i ] . managealldevicegroups ) {
if ( typeof obj . config . domains [ i ] . managealldevicegroups [ j ] == 'string' ) {
const u = 'user/' + i + '/' + obj . config . domains [ i ] . managealldevicegroups [ j ] ;
if ( obj . config . settings . managealldevicegroups . indexOf ( u ) == - 1 ) { obj . config . settings . managealldevicegroups . push ( u ) ; }
}
2020-05-06 21:47:23 +03:00
}
}
}
2022-06-18 10:07:45 +03:00
obj . config . settings . managealldevicegroups . sort ( ) ;
// Start watchdog timer if needed
// This is used to monitor if NodeJS is servicing IO correctly or getting held up a lot. Add this line to the settings section of config.json
// "watchDog": { "interval": 100, "timeout": 150 }
// This will check every 100ms, if the timer is more than 150ms late, it will warn.
if ( ( typeof config . settings . watchdog == 'object' ) && ( typeof config . settings . watchdog . interval == 'number' ) && ( typeof config . settings . watchdog . timeout == 'number' ) && ( config . settings . watchdog . interval >= 50 ) && ( config . settings . watchdog . timeout >= 50 ) ) {
obj . watchdogtime = Date . now ( ) ;
obj . watchdogmax = 0 ;
obj . watchdogmaxtime = null ;
obj . watchdogtable = [ ] ;
obj . watchdog = setInterval ( function ( ) {
const now = Date . now ( ) , delta = now - obj . watchdogtime - config . settings . watchdog . interval ;
if ( delta > obj . watchdogmax ) { obj . watchdogmax = delta ; obj . watchdogmaxtime = new Date ( ) . toLocaleString ( ) ; }
if ( delta > config . settings . watchdog . timeout ) {
const msg = obj . common . format ( "Watchdog timer timeout, {0}ms." , delta ) ;
obj . watchdogtable . push ( new Date ( ) . toLocaleString ( ) + ', ' + delta + 'ms' ) ;
while ( obj . watchdogtable . length > 10 ) { obj . watchdogtable . shift ( ) ; }
obj . debug ( 'main' , msg ) ;
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 ( errlogpath , new Date ( ) . toLocaleString ( ) + ': ' + msg + '\r\n' ) ;
} catch ( ex ) { console . log ( 'ERROR: Unable to write to mesherrors.txt.' ) ; }
}
obj . watchdogtime = now ;
} , config . settings . watchdog . interval ) ;
obj . debug ( 'main' , "Started watchdog timer." ) ;
}
2020-08-21 11:57:03 +03:00
2022-06-18 10:07:45 +03:00
} ) ;
2017-08-28 19:27:45 +03:00
} ) ;
} ) ;
2018-08-30 22:05:23 +03:00
} ;
2017-09-07 04:10:24 +03:00
2021-12-03 07:20:37 +03:00
// Called when the web server finished loading
obj . StartEx5 = function ( ) {
// Setup the email server for each domain
var ipKvmSupport = false ;
for ( var i in obj . config . domains ) { if ( obj . config . domains [ i ] . ipkvm == true ) { ipKvmSupport = true ; } }
if ( ipKvmSupport ) { obj . ipKvmManager = require ( './meshipkvm' ) . CreateIPKVMManager ( obj ) ; }
2022-03-17 21:19:18 +03:00
// Run the server start script if present
if ( typeof obj . config . settings . runonserverstarted == 'string' ) {
const child _process = require ( 'child_process' ) ;
var parentpath = _ _dirname ;
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 , '../..' ) ; }
child _process . exec ( obj . config . settings . runonserverstarted + ' ' + getCurrentVersion ( ) , { maxBuffer : 512000 , timeout : 120000 , cwd : parentpath } , function ( error , stdout , stderr ) { } ) ;
}
2021-12-03 07:20:37 +03:00
}
2019-10-30 02:17:29 +03:00
// Refresh any certificate hashs from the reverse proxy
obj . pendingProxyCertificatesRequests = 0 ;
obj . lastProxyCertificatesRequest = null ;
obj . supportsProxyCertificatesRequest = false ;
2019-10-30 02:31:03 +03:00
obj . updateProxyCertificates = function ( force ) {
if ( force !== true ) {
if ( ( obj . pendingProxyCertificatesRequests > 0 ) || ( obj . supportsProxyCertificatesRequest == false ) ) return ;
if ( ( obj . lastProxyCertificatesRequest != null ) && ( ( Date . now ( ) - obj . lastProxyCertificatesRequest ) < 120000 ) ) return ; // Don't allow this call more than every 2 minutes.
obj . lastProxyCertificatesRequest = Date . now ( ) ;
}
2019-10-30 02:17:29 +03:00
// Load any domain web certificates
2019-10-30 02:31:03 +03:00
for ( var i in obj . config . domains ) {
2019-10-30 02:17:29 +03:00
if ( obj . config . domains [ i ] . certurl != null ) {
// Load web certs
obj . pendingProxyCertificatesRequests ++ ;
var dnsname = obj . config . domains [ i ] . dns ;
if ( ( dnsname == null ) && ( obj . config . settings . cert != null ) ) { dnsname = obj . config . settings . cert ; }
obj . certificateOperations . loadCertificate ( obj . config . domains [ i ] . certurl , dnsname , obj . config . domains [ i ] , function ( url , cert , xhostname , xdomain ) {
obj . pendingProxyCertificatesRequests -- ;
if ( cert != null ) {
// Hash the entire cert
2022-04-14 23:07:05 +03:00
const hash = obj . crypto . createHash ( 'sha384' ) . update ( Buffer . from ( cert , 'binary' ) ) . digest ( 'hex' ) ;
2019-10-30 02:17:29 +03:00
if ( xdomain . certhash != hash ) { // The certificate has changed.
xdomain . certkeyhash = hash ;
xdomain . certhash = hash ;
try {
// Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
2022-04-14 23:07:05 +03:00
const forgeCert = obj . certificateOperations . forge . pki . certificateFromAsn1 ( obj . certificateOperations . forge . asn1 . fromDer ( cert ) ) ;
2019-10-30 02:17:29 +03:00
xdomain . certkeyhash = obj . certificateOperations . forge . pki . getPublicKeyFingerprint ( forgeCert . publicKey , { md : obj . certificateOperations . forge . md . sha384 . create ( ) , encoding : 'hex' } ) ;
2021-04-24 00:17:13 +03:00
obj . webserver . webCertificateExpire [ xdomain . id ] = Date . parse ( forgeCert . validity . notAfter ) ; // Update certificate expire time
2019-10-30 02:17:29 +03:00
//console.log('V1: ' + xdomain.certkeyhash);
} catch ( ex ) {
2021-04-24 00:17:13 +03:00
delete obj . webserver . webCertificateExpire [ xdomain . id ] ; // Remove certificate expire time
2019-10-30 02:17:29 +03:00
delete xdomain . certkeyhash ;
}
if ( obj . webserver ) {
obj . webserver . webCertificateHashs [ xdomain . id ] = obj . webserver . webCertificateFullHashs [ xdomain . id ] = Buffer . from ( hash , 'hex' ) . toString ( 'binary' ) ;
if ( xdomain . certkeyhash != null ) { obj . webserver . webCertificateHashs [ xdomain . id ] = Buffer . from ( xdomain . certkeyhash , 'hex' ) . toString ( 'binary' ) ; }
// Disconnect all agents with bad web certificates
for ( var i in obj . webserver . wsagentsWithBadWebCerts ) { obj . webserver . wsagentsWithBadWebCerts [ i ] . close ( 1 ) ; }
}
console . log ( obj . common . format ( "Loaded web certificate from \"{0}\", host: \"{1}\"" , url , xhostname ) ) ;
console . log ( obj . common . format ( " SHA384 cert hash: {0}" , xdomain . certhash ) ) ;
if ( ( xdomain . certkeyhash != null ) && ( xdomain . certhash != xdomain . certkeyhash ) ) { console . log ( obj . common . format ( " SHA384 key hash: {0}" , xdomain . certkeyhash ) ) ; }
}
} else {
console . log ( obj . common . format ( "Failed to load web certificate at: \"{0}\", host: \"{1}\"" , url , xhostname ) ) ;
}
} ) ;
}
}
}
2017-09-07 04:10:24 +03:00
// Perform maintenance operations (called every hour)
obj . maintenanceActions = function ( ) {
2020-08-24 23:26:27 +03:00
// Perform database maintenance
obj . db . maintenance ( ) ;
2020-11-08 12:13:23 +03:00
// Clean up any temporary files
2022-04-14 23:07:05 +03:00
const removeTime = new Date ( Date . now ( ) ) . getTime ( ) - ( 30 * 60 * 1000 ) ; // 30 minutes
const dir = obj . fs . readdir ( obj . path . join ( obj . filespath , 'tmp' ) , function ( err , files ) {
2020-11-08 12:13:23 +03:00
if ( err != null ) return ;
for ( var i in files ) { try { const filepath = obj . path . join ( obj . filespath , 'tmp' , files [ i ] ) ; if ( obj . fs . statSync ( filepath ) . mtime . getTime ( ) < removeTime ) { obj . fs . unlink ( filepath , function ( ) { } ) ; } } catch ( ex ) { } }
} ) ;
2019-01-27 23:23:38 +03:00
// Check for self-update that targets a specific version
2021-05-03 11:58:49 +03:00
if ( ( typeof obj . args . selfupdate == 'string' ) && ( getCurrentVersion ( ) === obj . args . selfupdate ) ) { obj . args . selfupdate = false ; }
2019-01-27 23:23:38 +03:00
2017-09-07 04:10:24 +03:00
// Check if we need to perform server self-update
2019-01-27 23:23:38 +03:00
if ( ( obj . args . selfupdate ) && ( obj . serverSelfWriteAllowed == true ) ) {
2017-09-07 04:10:24 +03: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 ; } } ) ;
2020-04-17 02:56:04 +03:00
} else {
checkAutobackup ( ) ;
2017-09-07 04:10:24 +03:00
}
} ) ;
2020-04-17 02:56:04 +03:00
} else {
checkAutobackup ( ) ;
2017-09-07 04:10:24 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2017-09-07 04:10:24 +03:00
2020-04-17 02:56:04 +03:00
// Check if we need to perform an automatic backup
function checkAutobackup ( ) {
if ( obj . config . settings . autobackup && ( typeof obj . config . settings . autobackup . backupintervalhours == 'number' ) ) {
obj . db . Get ( 'LastAutoBackupTime' , function ( err , docs ) {
if ( err != null ) return ;
2022-04-14 23:07:05 +03:00
var lastBackup = 0 ;
const now = new Date ( ) . getTime ( ) ;
2020-04-17 02:56:04 +03:00
if ( docs . length == 1 ) { lastBackup = docs [ 0 ] . value ; }
2022-04-14 23:07:05 +03:00
const delta = now - lastBackup ;
2020-04-17 02:56:04 +03:00
if ( delta > ( obj . config . settings . autobackup . backupintervalhours * 60 * 60 * 1000 ) ) {
// A new auto-backup is required.
obj . db . Set ( { _id : 'LastAutoBackupTime' , value : now } ) ; // Save the current time in the database
obj . db . performBackup ( ) ; // Perform the backup
}
} ) ;
}
}
2017-08-28 19:27:45 +03: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
2019-10-30 02:17:29 +03:00
obj . DispatchEvent ( [ '*' ] , obj , { etype : 'server' , action : 'stopped' , msg : "Server stopped" } ) ;
2017-08-28 19:27:45 +03:00
// Set all nodes to power state of unknown (0)
2019-02-19 01:32:55 +03: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 19:27:45 +03:00
if ( restoreFile ) {
2019-10-30 02:17:29 +03:00
obj . debug ( 'main' , obj . common . format ( "Server stopped, updating settings: {0}" , restoreFile ) ) ;
console . log ( "Updating settings folder..." ) ;
2018-05-03 02:19:45 +03:00
2022-04-14 23:07:05 +03:00
const yauzl = require ( 'yauzl' ) ;
2018-05-03 02:19:45 +03:00
yauzl . open ( restoreFile , { lazyEntries : true } , function ( err , zipfile ) {
if ( err ) throw err ;
zipfile . readEntry ( ) ;
2019-10-30 02:17:29 +03:00
zipfile . on ( 'entry' , function ( entry ) {
2018-05-03 02:19:45 +03:00
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 {
2020-09-28 20:34:44 +03:00
// File entry
2018-05-03 02:19:45 +03:00
zipfile . openReadStream ( entry , function ( err , readStream ) {
if ( err ) throw err ;
2019-10-30 02:17:29 +03:00
readStream . on ( 'end' , function ( ) { zipfile . readEntry ( ) ; } ) ;
2022-06-07 20:02:38 +03:00
var directory = obj . path . dirname ( entry . fileName ) ;
2020-09-28 20:34:44 +03:00
if ( directory != '.' ) {
directory = obj . getConfigFilePath ( directory )
if ( obj . fs . existsSync ( directory ) == false ) { obj . fs . mkdirSync ( directory ) ; }
}
//console.log('Extracting:', obj.getConfigFilePath(entry.fileName));
2018-07-14 05:18:43 +03:00
readStream . pipe ( obj . fs . createWriteStream ( obj . getConfigFilePath ( entry . fileName ) ) ) ;
2018-05-03 02:19:45 +03:00
} ) ;
}
} ) ;
2019-10-30 02:17:29 +03:00
zipfile . on ( 'end' , function ( ) { setTimeout ( function ( ) { obj . fs . unlinkSync ( restoreFile ) ; process . exit ( 123 ) ; } ) ; } ) ;
2018-05-03 02:19:45 +03:00
} ) ;
2017-08-28 19:27:45 +03:00
} else {
2019-10-30 02:17:29 +03:00
obj . debug ( 'main' , "Server stopped" ) ;
2017-08-28 19:27:45 +03:00
process . exit ( 0 ) ;
}
} ) ;
2018-03-14 22:10:13 +03:00
// Update the server state
2019-10-30 02:17:29 +03:00
obj . updateServerState ( 'state' , "stopped" ) ;
2018-08-30 22:05:23 +03:00
} ;
2024-03-10 10:46:01 +03:00
2017-08-28 19:27:45 +03:00
// Event Dispatch
obj . AddEventDispatch = function ( ids , target ) {
2019-08-23 01:31:39 +03:00
obj . debug ( 'dispatch' , 'AddEventDispatch' , ids ) ;
2017-08-28 19:27:45 +03: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 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
obj . RemoveEventDispatch = function ( ids , target ) {
2021-06-23 02:54:37 +03:00
obj . debug ( 'dispatch' , 'RemoveEventDispatch' , ids ) ;
2021-11-11 04:56:12 +03:00
for ( var i in ids ) {
const 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 {
const newList = [ ] ; // We create a new list so not to modify the original list. Allows this function to be called during an event dispatch.
for ( var k in obj . eventsDispatch [ i ] ) { if ( obj . eventsDispatch [ i ] [ k ] != target ) { newList . push ( obj . eventsDispatch [ i ] [ k ] ) ; } }
obj . eventsDispatch [ i ] = newList ;
}
}
}
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
obj . RemoveEventDispatchId = function ( id ) {
2019-08-23 01:31:39 +03:00
obj . debug ( 'dispatch' , 'RemoveEventDispatchId' , id ) ;
2017-09-15 21:45:06 +03:00
if ( obj . eventsDispatch [ id ] != null ) { delete obj . eventsDispatch [ id ] ; }
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
obj . RemoveAllEventDispatch = function ( target ) {
2019-08-23 01:31:39 +03:00
obj . debug ( 'dispatch' , 'RemoveAllEventDispatch' ) ;
2021-11-11 04:56:12 +03:00
for ( var i in obj . eventsDispatch ) {
const j = obj . eventsDispatch [ i ] . indexOf ( target ) ;
if ( j >= 0 ) {
if ( obj . eventsDispatch [ i ] . length == 1 ) {
delete obj . eventsDispatch [ i ] ;
} else {
const newList = [ ] ; // We create a new list so not to modify the original list. Allows this function to be called during an event dispatch.
for ( var k in obj . eventsDispatch [ i ] ) { if ( obj . eventsDispatch [ i ] [ k ] != target ) { newList . push ( obj . eventsDispatch [ i ] [ k ] ) ; } }
obj . eventsDispatch [ i ] = newList ;
}
}
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
obj . DispatchEvent = function ( ids , source , event , fromPeerServer ) {
// If the database is not setup, exit now.
if ( ! obj . db ) return ;
2020-09-09 04:59:58 +03:00
// Send event to syslog if needed
2020-04-07 22:02:29 +03:00
if ( obj . syslog && event . msg ) { obj . syslog . log ( obj . syslog . LOG _INFO , event . msg ) ; }
2020-04-08 21:02:51 +03:00
if ( obj . syslogjson ) { obj . syslogjson . log ( obj . syslogjson . LOG _INFO , JSON . stringify ( event ) ) ; }
2021-05-19 09:54:36 +03:00
if ( obj . syslogtcp && event . msg ) { obj . syslogtcp . log ( event . msg , obj . syslogtcp . LOG _INFO ) ; }
2020-04-07 22:02:29 +03:00
2019-08-23 01:31:39 +03:00
obj . debug ( 'dispatch' , 'DispatchEvent' , ids ) ;
2019-02-19 01:32:55 +03: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.
2021-11-11 04:56:12 +03:00
const storeEvent = Object . assign ( { } , event ) ;
2019-10-12 01:14:38 +03:00
if ( storeEvent . node ) { delete storeEvent . node ; } // Skip the "node" field. May skip more in the future.
if ( storeEvent . links ) {
// Escape "links" names that may have "." and/or "$"
storeEvent . links = Object . assign ( { } , storeEvent . links ) ;
for ( var i in storeEvent . links ) { var ue = obj . common . escapeFieldName ( i ) ; if ( ue !== i ) { storeEvent . links [ ue ] = storeEvent . links [ i ] ; delete storeEvent . links [ i ] ; } }
}
2019-02-19 01:32:55 +03:00
storeEvent . ids = ids ;
obj . db . StoreEvent ( storeEvent ) ;
2017-12-15 01:57:52 +03:00
}
2021-11-11 04:56:12 +03:00
const targets = [ ] ; // List of targets we dispatched the event to, we don't want to dispatch to the same target twice.
2017-08-28 19:27:45 +03:00
for ( var j in ids ) {
2021-11-11 04:56:12 +03:00
const id = ids [ j ] ;
const eventsDispatch = obj . eventsDispatch [ id ] ;
if ( eventsDispatch ) {
for ( var i in eventsDispatch ) {
if ( targets . indexOf ( eventsDispatch [ i ] ) == - 1 ) { // Check if we already displatched to this target
targets . push ( eventsDispatch [ i ] ) ;
try { eventsDispatch [ i ] . HandleEvent ( source , event , ids , id ) ; } catch ( ex ) { console . log ( ex , eventsDispatch [ i ] ) ; }
2017-08-28 19:27:45 +03:00
}
}
}
}
2017-12-15 01:57:52 +03:00
if ( ( fromPeerServer == null ) && ( obj . multiServer != null ) && ( ( typeof event != 'object' ) || ( event . nopeers != 1 ) ) ) { obj . multiServer . DispatchEvent ( ids , source , event ) ; }
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
// Get the connection state of a node
2018-08-30 22:05:23 +03:00
obj . GetConnectivityState = function ( nodeid ) { return obj . connectivityByNode [ nodeid ] ; } ;
2017-09-13 21:25:57 +03:00
2017-09-15 21:45:06 +03:00
// Get the routing server id for a given node and connection type, can never be self.
2020-11-13 09:16:10 +03:00
obj . GetRoutingServerIdNotSelf = function ( nodeid , connectType ) {
if ( obj . multiServer == null ) return null ;
for ( var serverid in obj . peerConnectivityByNode ) {
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 ;
} ;
// Get the routing server id for a given node and connection type, self first
2017-09-15 21:45:06 +03:00
obj . GetRoutingServerId = function ( nodeid , connectType ) {
if ( obj . multiServer == null ) return null ;
2020-11-13 09:16:10 +03:00
// Look at our own server first
var connections = obj . peerConnectivityByNode [ obj . serverId ] ;
if ( connections != null ) {
var state = connections [ nodeid ] ;
if ( ( state != null ) && ( ( state . connectivity & connectType ) != 0 ) ) { return { serverid : obj . serverId , meshid : state . meshid } ; }
}
// Look at other servers
2018-08-30 22:05:23 +03:00
for ( var serverid in obj . peerConnectivityByNode ) {
2017-09-15 21:45:06 +03: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 22:05:23 +03:00
} ;
2017-09-15 21:45:06 +03:00
2017-09-13 21:25:57 +03: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 22:05:23 +03:00
if ( oldState != null ) { meshid = oldState . meshid ; oldConnectivity = oldState . connectivity ; oldPowerState = oldState . powerState ; }
for ( var serverid in obj . peerConnectivityByNode ) {
2017-09-13 21:25:57 +03: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 ) ) {
2024-05-21 19:47:39 +03:00
obj . DispatchEvent ( obj . webserver . CreateNodeDispatchTargets ( meshid , nodeid ) , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , domain : nodeid . split ( '/' ) [ 1 ] , conn : newConnectivity , pwr : newPowerState , nolog : 1 , nopeers : 1 , id : Math . random ( ) } ) ;
2017-09-13 21:25:57 +03:00
}
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2021-10-10 22:43:20 +03:00
// See if we need to notifiy any user of device state change
obj . NotifyUserOfDeviceStateChange = function ( meshid , nodeid , connectTime , connectType , powerState , serverid , stateSet , extraInfo ) {
// Check if there is a email server for this domain
const meshSplit = meshid . split ( '/' ) ;
if ( meshSplit . length != 3 ) return ;
const domainId = meshSplit [ 1 ] ;
2022-02-16 01:50:58 +03:00
if ( obj . config . domains [ domainId ] == null ) return ;
const mailserver = obj . config . domains [ domainId ] . mailserver ;
2022-11-02 08:39:59 +03:00
if ( ( mailserver == null ) && ( obj . msgserver == null ) ) return ;
2021-10-10 22:43:20 +03:00
// Get the device group for this device
const mesh = obj . webserver . meshes [ meshid ] ;
if ( ( mesh == null ) || ( mesh . links == null ) ) return ;
2021-10-15 08:13:35 +03:00
// Get the list of users that have visibility to this device
// This includes users that are part of user groups
2022-04-14 23:07:05 +03:00
const users = [ ] ;
2021-10-10 22:43:20 +03:00
for ( var i in mesh . links ) {
2021-10-15 08:13:35 +03:00
if ( i . startsWith ( 'user/' ) && ( users . indexOf ( i ) < 0 ) ) { users . push ( i ) ; }
if ( i . startsWith ( 'ugrp/' ) ) {
var usergrp = obj . webserver . userGroups [ i ] ;
if ( usergrp . links != null ) { for ( var j in usergrp . links ) { if ( j . startsWith ( 'user/' ) && ( users . indexOf ( j ) < 0 ) ) { users . push ( j ) ; } } }
}
}
2021-10-15 04:21:43 +03:00
2021-10-15 08:13:35 +03:00
// Check if any user needs email notification
for ( var i in users ) {
2021-10-19 03:52:36 +03:00
const user = obj . webserver . users [ users [ i ] ] ;
2022-11-02 09:48:21 +03:00
if ( user != null ) {
2021-10-15 08:13:35 +03:00
var notify = 0 ;
// Device group notifications
const meshLinks = user . links [ meshid ] ;
if ( ( meshLinks != null ) && ( meshLinks . notify != null ) ) { notify |= meshLinks . notify ; }
// User notifications
if ( user . notify != null ) {
if ( user . notify [ meshid ] != null ) { notify |= user . notify [ meshid ] ; }
if ( user . notify [ nodeid ] != null ) { notify |= user . notify [ nodeid ] ; }
}
2022-11-02 08:39:59 +03:00
// Email notifications
2022-11-02 09:48:21 +03:00
if ( ( user . email != null ) && ( user . emailVerified == true ) && ( mailserver != null ) && ( ( notify & 48 ) != 0 ) ) {
2021-10-15 08:13:35 +03:00
if ( stateSet == true ) {
if ( ( notify & 16 ) != 0 ) {
mailserver . notifyDeviceConnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
} else {
mailserver . cancelNotifyDeviceDisconnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
2021-10-10 22:43:20 +03:00
}
2021-10-15 08:13:35 +03:00
}
else if ( stateSet == false ) {
if ( ( notify & 32 ) != 0 ) {
mailserver . notifyDeviceDisconnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
} else {
mailserver . cancelNotifyDeviceConnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
2021-10-10 22:43:20 +03:00
}
}
}
2022-11-02 08:39:59 +03:00
// Messaging notifications
if ( ( obj . msgserver != null ) && ( ( notify & 384 ) != 0 ) ) {
if ( stateSet == true ) {
if ( ( notify & 128 ) != 0 ) {
obj . msgserver . notifyDeviceConnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
} else {
obj . msgserver . cancelNotifyDeviceDisconnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
}
}
else if ( stateSet == false ) {
if ( ( notify & 256 ) != 0 ) {
obj . msgserver . notifyDeviceDisconnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
} else {
obj . msgserver . cancelNotifyDeviceConnect ( user , meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) ;
}
}
}
2021-10-10 22:43:20 +03:00
}
}
}
2022-09-03 03:03:38 +03:00
// See if we need to notifiy any user of device requested help
//if (typeof device.name == 'string') { parent.parent.NotifyUserOfDeviceHelpRequest(domain, device._id, device.meshid, device.name, command.msgArgs[0], command.msgArgs[1]); }
obj . NotifyUserOfDeviceHelpRequest = function ( domain , meshid , nodeid , devicename , helpusername , helprequest ) {
// Check if there is a email server for this domain
const meshSplit = meshid . split ( '/' ) ;
if ( meshSplit . length != 3 ) return ;
const domainId = meshSplit [ 1 ] ;
if ( obj . config . domains [ domainId ] == null ) return ;
const mailserver = obj . config . domains [ domainId ] . mailserver ;
2022-11-02 09:48:21 +03:00
if ( ( mailserver == null ) && ( obj . msgserver == null ) ) return ;
2022-09-03 03:03:38 +03:00
// Get the device group for this device
const mesh = obj . webserver . meshes [ meshid ] ;
if ( ( mesh == null ) || ( mesh . links == null ) ) return ;
// Get the list of users that have visibility to this device
// This includes users that are part of user groups
const users = [ ] ;
for ( var i in mesh . links ) {
if ( i . startsWith ( 'user/' ) && ( users . indexOf ( i ) < 0 ) ) { users . push ( i ) ; }
if ( i . startsWith ( 'ugrp/' ) ) {
var usergrp = obj . webserver . userGroups [ i ] ;
if ( usergrp . links != null ) { for ( var j in usergrp . links ) { if ( j . startsWith ( 'user/' ) && ( users . indexOf ( j ) < 0 ) ) { users . push ( j ) ; } } }
}
}
// Check if any user needs email notification
for ( var i in users ) {
const user = obj . webserver . users [ users [ i ] ] ;
2022-11-02 09:48:21 +03:00
if ( user != null ) {
2022-09-03 03:03:38 +03:00
var notify = 0 ;
// Device group notifications
const meshLinks = user . links [ meshid ] ;
if ( ( meshLinks != null ) && ( meshLinks . notify != null ) ) { notify |= meshLinks . notify ; }
// User notifications
if ( user . notify != null ) {
if ( user . notify [ meshid ] != null ) { notify |= user . notify [ meshid ] ; }
if ( user . notify [ nodeid ] != null ) { notify |= user . notify [ nodeid ] ; }
}
2022-11-02 09:48:21 +03:00
// Mail help request
if ( ( user . email != null ) && ( user . emailVerified == true ) && ( ( notify & 64 ) != 0 ) ) { mailserver . sendDeviceHelpMail ( domain , user . name , user . email , devicename , nodeid , helpusername , helprequest , user . llang ) ; }
// Message help request
if ( ( user . msghandle != null ) && ( ( notify & 512 ) != 0 ) ) { obj . msgserver . sendDeviceHelpRequest ( domain , user . name , user . msghandle , devicename , nodeid , helpusername , helprequest , user . llang ) ; }
2022-09-03 03:03:38 +03:00
}
}
}
2017-08-28 19:27:45 +03: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.
2019-10-05 03:24:30 +03:00
// connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT
2021-12-15 01:42:45 +03:00
// 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, 8 = Off
2019-10-05 03:24:30 +03:00
//var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local', '', '', '', 'Intel AMT Relay', '', '', '', '', '', '', '', 'MQTT'];
2021-12-15 01:42:45 +03:00
//var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present', 'Off'];
2021-08-01 05:31:47 +03:00
obj . SetConnectivityState = function ( meshid , nodeid , connectTime , connectType , powerState , serverid , extraInfo ) {
2017-09-13 21:25:57 +03:00
//console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid)));
2021-10-10 22:43:20 +03:00
if ( ( serverid == null ) && ( obj . multiServer != null ) ) { obj . multiServer . DispatchMessage ( { action : 'SetConnectivityState' , meshid : meshid , nodeid : nodeid , connectTime : connectTime , connectType : connectType , powerState : powerState , extraInfo : extraInfo } ) ; }
2017-09-13 21:25:57 +03:00
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 ; }
2024-03-10 10:46:01 +03:00
if ( ( state . powerState == null ) || ( state . powerState == undefined ) || ( state . powerState != powerState ) ) {
2017-09-13 21:25:57 +03:00
state . powerState = powerState ;
2017-08-28 19:27:45 +03:00
eventConnectChange = 1 ;
2017-09-13 21:25:57 +03:00
// Set new power state in database
2022-04-14 23:07:05 +03:00
const record = { time : new Date ( connectTime ) , nodeid : nodeid , power : powerState } ;
2019-02-19 01:32:55 +03:00
if ( oldPowerState != null ) { record . oldPower = oldPowerState ; }
obj . db . storePowerEvent ( record , obj . multiServer ) ;
2017-08-28 19:27:45 +03:00
}
2017-09-13 21:25:57 +03:00
// Event the node connection change
2020-04-13 21:24:56 +03:00
if ( eventConnectChange == 1 ) {
2024-05-21 19:47:39 +03:00
obj . DispatchEvent ( obj . webserver . CreateNodeDispatchTargets ( meshid , nodeid ) , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , domain : nodeid . split ( '/' ) [ 1 ] , conn : state . connectivity , pwr : state . powerState , ct : connectTime , nolog : 1 , nopeers : 1 , id : Math . random ( ) } ) ;
2021-08-01 05:31:47 +03:00
// Save indication of node connection change
2021-08-04 20:55:33 +03:00
const lc = { _id : 'lc' + nodeid , type : 'lastconnect' , domain : nodeid . split ( '/' ) [ 1 ] , meshid : meshid , time : Date . now ( ) , cause : 1 , connectType : connectType } ;
2021-08-01 05:31:47 +03:00
if ( extraInfo && extraInfo . remoteaddrport ) { lc . addr = extraInfo . remoteaddrport ; }
obj . db . Set ( lc ) ;
2021-10-10 22:43:20 +03:00
// Notify any users of device connection
obj . NotifyUserOfDeviceStateChange ( meshid , nodeid , connectTime , connectType , powerState , serverid , true , extraInfo ) ;
2020-04-13 21:24:56 +03:00
}
2020-04-01 01:12:47 +03:00
} else {
2017-09-13 21:25:57 +03: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
2021-08-01 05:31:47 +03:00
var eventConnectChange = 0 ;
2022-09-07 18:12:52 +03:00
var state = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
2017-09-13 21:25:57 +03:00
if ( state ) {
// Change the connection in the node and mesh state lists
2021-08-01 05:31:47 +03:00
if ( ( state . connectivity & connectType ) == 0 ) { state . connectivity |= connectType ; eventConnectChange = 1 ; }
2017-09-13 21:25:57 +03:00
state . meshid = meshid ;
} else {
// Add the connection to the node and mesh state list
obj . peerConnectivityByNode [ serverid ] [ nodeid ] = state = { connectivity : connectType , meshid : meshid } ;
2021-08-01 05:31:47 +03:00
eventConnectChange = 1 ;
2017-09-13 21:25:57 +03:00
}
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03: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 22:05:23 +03:00
var powerState = 0 , oldPowerState = state . powerState ;
2017-09-13 21:25:57 +03: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 ; }
2024-03-10 10:46:01 +03:00
if ( ( state . powerState == null ) || ( state . powerState == undefined ) || ( state . powerState != powerState ) ) {
2018-01-03 03:52:49 +03:00
state . powerState = powerState ;
2021-08-01 05:31:47 +03:00
eventConnectChange = 1 ;
2018-01-03 03:52:49 +03:00
// Set new power state in database
2019-02-19 01:32:55 +03: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-03 03:52:49 +03:00
}
2017-08-28 19:27:45 +03:00
2021-08-01 05:31:47 +03:00
if ( eventConnectChange == 1 ) {
// Update the combined node state
var x = { } ; x [ nodeid ] = 1 ;
obj . UpdateConnectivityState ( x ) ;
// Save indication of node connection change
if ( serverid == obj . serverId ) {
2021-08-04 05:28:48 +03:00
const lc = { _id : 'lc' + nodeid , type : 'lastconnect' , domain : nodeid . split ( '/' ) [ 1 ] , meshid : meshid , time : Date . now ( ) , cause : 1 , connectType : connectType , serverid : obj . serverId } ;
2021-08-01 05:31:47 +03:00
if ( extraInfo && extraInfo . remoteaddrport ) { lc . addr = extraInfo . remoteaddrport ; }
obj . db . Set ( lc ) ;
}
2021-10-10 22:43:20 +03:00
// Notify any users of device connection
obj . NotifyUserOfDeviceStateChange ( meshid , nodeid , connectTime , connectType , powerState , serverid , true , extraInfo ) ;
2021-08-01 05:31:47 +03:00
}
2017-08-28 19:27:45 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03: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.
2021-08-01 05:31:47 +03:00
obj . ClearConnectivityState = function ( meshid , nodeid , connectType , serverid , extraInfo ) {
2017-09-13 21:25:57 +03:00
//console.log('ClearConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + (serverid == null?(''):(', ServerId: ' + serverid)));
2021-10-10 22:43:20 +03:00
if ( ( serverid == null ) && ( obj . multiServer != null ) ) { obj . multiServer . DispatchMessage ( { action : 'ClearConnectivityState' , meshid : meshid , nodeid : nodeid , connectType : connectType , extraInfo : extraInfo } ) ; }
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
if ( obj . multiServer == null ) {
// Single server mode
2018-08-27 22:24:15 +03:00
var eventConnectChange = 0 ;
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
// Remove the agent connection from the nodes connection list
2022-04-14 23:07:05 +03:00
const state = obj . connectivityByNode [ nodeid ] ;
2017-09-15 21:45:06 +03:00
if ( state == null ) return ;
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
if ( ( state . connectivity & connectType ) != 0 ) {
state . connectivity -= connectType ;
2021-08-01 05:31:47 +03:00
// Save indication of node connection change
2021-08-04 20:55:33 +03:00
const lc = { _id : 'lc' + nodeid , type : 'lastconnect' , domain : nodeid . split ( '/' ) [ 1 ] , meshid : meshid , time : Date . now ( ) , cause : 0 , connectType : connectType } ;
2021-08-01 05:31:47 +03:00
if ( extraInfo && extraInfo . remoteaddrport ) { lc . addr = extraInfo . remoteaddrport ; }
obj . db . Set ( lc ) ;
2017-09-13 21:25:57 +03:00
// If the node is completely disconnected, clean it up completely
2020-04-01 01:12:47 +03:00
if ( state . connectivity == 0 ) { delete obj . connectivityByNode [ nodeid ] ; }
2017-09-13 21:25:57 +03:00
eventConnectChange = 1 ;
2017-08-28 19:27:45 +03:00
}
2017-09-13 21:25:57 +03:00
// Clear node power state
2022-04-14 23:07:05 +03:00
var powerState = 0 ;
const oldPowerState = state . powerState ;
2017-09-13 21:25:57 +03:00
if ( connectType == 1 ) { state . agentPower = 0 ; } else if ( connectType == 2 ) { state . ciraPower = 0 ; } else if ( connectType == 4 ) { state . amtPower = 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 21:45:06 +03:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) {
2017-09-13 21:25:57 +03:00
state . powerState = powerState ;
eventConnectChange = 1 ;
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
// Set new power state in database
2019-02-19 01:32:55 +03:00
obj . db . storePowerEvent ( { time : new Date ( ) , nodeid : nodeid , power : powerState , oldPower : oldPowerState } , obj . multiServer ) ;
2017-09-13 21:25:57 +03:00
}
2017-08-28 19:27:45 +03:00
2017-09-13 21:25:57 +03:00
// Event the node connection change
2021-08-01 05:31:47 +03:00
if ( eventConnectChange == 1 ) {
2024-05-21 19:47:39 +03:00
obj . DispatchEvent ( obj . webserver . CreateNodeDispatchTargets ( meshid , nodeid ) , obj , { action : 'nodeconnect' , meshid : meshid , nodeid : nodeid , domain : nodeid . split ( '/' ) [ 1 ] , conn : state . connectivity , pwr : state . powerState , nolog : 1 , nopeers : 1 , id : Math . random ( ) } ) ;
2021-10-10 22:43:20 +03:00
// Notify any users of device disconnection
obj . NotifyUserOfDeviceStateChange ( meshid , nodeid , Date . now ( ) , connectType , - 1 , serverid , false , extraInfo ) ;
2021-08-01 05:31:47 +03:00
}
2017-09-13 21:25:57 +03:00
} 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
2022-09-07 18:12:52 +03:00
var state = obj . peerConnectivityByNode [ serverid ] [ nodeid ] ;
2017-09-15 21:45:06 +03:00
if ( state == null ) return ;
2017-09-13 21:25:57 +03:00
// If existing state exist, remove this connection
if ( ( state . connectivity & connectType ) != 0 ) {
state . connectivity -= connectType ; // Remove one connectivity mode
2021-08-01 05:31:47 +03:00
// Save indication of node connection change
if ( serverid == obj . serverId ) {
2021-08-04 05:28:48 +03:00
const lc = { _id : 'lc' + nodeid , type : 'lastconnect' , domain : nodeid . split ( '/' ) [ 1 ] , meshid : meshid , time : Date . now ( ) , cause : 0 , connectType : connectType , serverid : obj . serverId } ;
2021-08-01 05:31:47 +03:00
if ( extraInfo && extraInfo . remoteaddrport ) { lc . addr = extraInfo . remoteaddrport ; }
obj . db . Set ( lc ) ;
}
2017-09-13 21:25:57 +03:00
// If the node is completely disconnected, clean it up completely
if ( state . connectivity == 0 ) { delete obj . peerConnectivityByNode [ serverid ] [ nodeid ] ; state . powerState = 0 ; }
2021-10-10 22:43:20 +03:00
// Notify any users of device disconnection
obj . NotifyUserOfDeviceStateChange ( meshid , nodeid , Date . now ( ) , connectType , - 1 , serverid , false , extraInfo ) ;
2017-09-13 21:25:57 +03: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 ;
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 21:45:06 +03:00
if ( ( state . powerState == null ) || ( state . powerState != powerState ) ) { state . powerState = powerState ; }
2017-09-13 21:25:57 +03:00
// Update the combined node state
var x = { } ; x [ nodeid ] = 1 ;
obj . UpdateConnectivityState ( x ) ;
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2018-12-30 02:24:33 +03:00
// Escape a code string
2022-01-16 23:14:14 +03:00
obj . escapeCodeString = function ( str , keepUtf8 ) {
2018-12-30 02:24:33 +03:00
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 ;
2022-01-16 23:14:14 +03:00
} else if ( keepUtf8 === true ) {
r += c ;
2018-12-30 02:24:33 +03:00
} else {
cr = c . charCodeAt ( 0 ) ;
if ( ( cr >= 32 ) && ( cr <= 127 ) ) { r += c ; }
}
}
return r ;
}
2017-08-28 19:27:45 +03:00
// Update the default mesh core
2022-02-04 06:11:35 +03:00
obj . updateMeshCore = function ( func , dumpToFile ) {
2017-10-04 04:31:20 +03: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 ) {
2024-03-10 10:46:01 +03:00
obj . defaultMeshCores = obj . defaultMeshCoresHash = { } ; if ( func != null ) { func ( false ) ; } // meshcore.js not found
2017-10-04 04:31:20 +03:00
}
}
// Read meshcore.js and all .js files in the modules folder.
2018-12-30 02:24:33 +03:00
var meshCore = null , modulesDir = null ;
const modulesAdd = {
2019-03-10 01:28:08 +03:00
'windows-amt' : [ 'var addedModules = [];\r\n' ] ,
'linux-amt' : [ 'var addedModules = [];\r\n' ] ,
'linux-noamt' : [ 'var addedModules = [];\r\n' ]
2018-12-30 02:24:33 +03:00
} ;
2019-01-22 01:05:50 +03: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-10 01:28:08 +03:00
modulesAdd [ 'windows-recovery' ] = [ 'var addedModules = [];\r\n' ] ;
modulesAdd [ 'linux-recovery' ] = [ 'var addedModules = [];\r\n' ] ;
2019-01-22 01:05:50 +03:00
}
}
2019-02-26 01:35:08 +03:00
// Read the agent recovery core if present
var meshAgentRecoveryCore = null ;
2019-04-13 03:43:20 +03: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-26 01:35:08 +03:00
if ( meshAgentRecoveryCore != null ) {
2019-03-10 01:28:08 +03:00
modulesAdd [ 'windows-agentrecovery' ] = [ 'var addedModules = [];\r\n' ] ;
modulesAdd [ 'linux-agentrecovery' ] = [ 'var addedModules = [];\r\n' ] ;
2019-02-26 01:35:08 +03:00
}
}
2021-01-25 05:43:15 +03:00
// Read the tiny core if present
var meshTinyCore = null ;
if ( obj . fs . existsSync ( obj . path . join ( _ _dirname , 'agents' , 'tinycore.js' ) ) == true ) {
try { meshTinyCore = obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'agents' , 'tinycore.js' ) ) . toString ( ) ; } catch ( ex ) { }
if ( meshTinyCore != null ) {
modulesAdd [ 'windows-tiny' ] = [ 'var addedModules = [];\r\n' ] ;
modulesAdd [ 'linux-tiny' ] = [ 'var addedModules = [];\r\n' ] ;
}
}
2022-02-04 06:11:35 +03:00
if ( obj . args . minifycore !== false ) { try { meshCore = obj . fs . readFileSync ( obj . path . join ( meshcorePath , 'meshcore.min.js' ) ) . toString ( ) ; } catch ( ex ) { } } // Favor minified meshcore if present.
if ( meshCore == null ) { try { meshCore = obj . fs . readFileSync ( obj . path . join ( meshcorePath , 'meshcore.js' ) ) . toString ( ) ; } catch ( ex ) { } } // Use non-minified meshcore.
2018-04-12 21:15:01 +03:00
if ( meshCore != null ) {
2018-12-30 02:24:33 +03:00
var moduleDirPath = null ;
2022-02-04 06:11:35 +03:00
if ( obj . args . minifycore !== false ) { try { moduleDirPath = obj . path . join ( meshcorePath , 'modules_meshcore_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } // Favor minified modules if present.
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( meshcorePath , 'modules_meshcore' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } // Use non-minified mofules.
2018-04-12 21:15:01 +03:00
if ( modulesDir != null ) {
for ( var i in modulesDir ) {
2022-01-13 02:12:30 +03:00
if ( modulesDir [ i ] . toLowerCase ( ) . endsWith ( '.json' ) ) {
2022-01-16 23:14:14 +03:00
// We are adding a JSON file to the meshcores
2022-01-13 02:12:30 +03:00
var moduleName = modulesDir [ i ] . substring ( 0 , modulesDir [ i ] . length - 5 ) ;
if ( moduleName . endsWith ( '.min' ) ) { moduleName = moduleName . substring ( 0 , moduleName . length - 6 ) ; } // Remove the ".min" for ".min.json" files.
2022-01-16 23:14:14 +03:00
const jsonData = obj . escapeCodeString ( obj . fs . readFileSync ( obj . path . join ( moduleDirPath , modulesDir [ i ] ) ) . toString ( 'utf8' ) , true ) ;
const moduleData = [ 'var ' , moduleName , ' = JSON.parse(\'' , jsonData , '\');\r\n' ] ;
2022-01-13 02:12:30 +03:00
2022-01-16 23:14:14 +03:00
// Add to all major cores
2022-01-13 02:12:30 +03:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-noamt' ] . push ( ... moduleData ) ;
}
2018-04-12 21:15:01 +03:00
if ( modulesDir [ i ] . toLowerCase ( ) . endsWith ( '.js' ) ) {
2022-01-16 23:14:14 +03:00
// We are adding a JS file to the meshcores
2018-04-12 21:15:01 +03:00
var moduleName = modulesDir [ i ] . substring ( 0 , modulesDir [ i ] . length - 3 ) ;
2018-12-30 02:24:33 +03:00
if ( moduleName . endsWith ( '.min' ) ) { moduleName = moduleName . substring ( 0 , moduleName . length - 4 ) ; } // Remove the ".min" for ".min.js" files.
2024-03-10 10:46:01 +03:00
const moduleData = [ 'try { addModule("' , moduleName , '", "' , obj . escapeCodeString ( obj . fs . readFileSync ( obj . path . join ( moduleDirPath , modulesDir [ i ] ) ) . toString ( 'binary' ) ) , '"); addedModules.push("' , moduleName , '"); } catch (ex) { }\r\n' ] ;
2018-12-30 02:24:33 +03:00
// Merge this module
2018-12-31 00:32:40 +03: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-10 01:28:08 +03:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
2018-12-30 02:24:33 +03:00
} else if ( moduleName . startsWith ( 'win-' ) ) {
// Add to Windows cores only
2019-03-10 01:28:08 +03:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
2018-12-30 02:24:33 +03:00
} else if ( moduleName . startsWith ( 'linux-' ) ) {
// Add to Linux cores only
2019-03-10 01:28:08 +03:00
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-noamt' ] . push ( ... moduleData ) ;
2018-12-30 02:24:33 +03:00
} else {
// Add to all cores
2019-03-10 01:28:08 +03:00
modulesAdd [ 'windows-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-amt' ] . push ( ... moduleData ) ;
modulesAdd [ 'linux-noamt' ] . push ( ... moduleData ) ;
2018-04-12 21:15:01 +03:00
}
2019-01-22 01:05:50 +03:00
// Merge this module to recovery modules if needed
2022-01-16 23:14:14 +03:00
if ( modulesAdd [ 'windows-recovery' ] != null ) {
if ( ( moduleName == 'win-console' ) || ( moduleName == 'win-message-pump' ) || ( moduleName == 'win-terminal' ) || ( moduleName == 'win-virtual-terminal' ) ) {
2019-03-10 01:28:08 +03:00
modulesAdd [ 'windows-recovery' ] . push ( ... moduleData ) ;
2019-01-22 01:05:50 +03:00
}
}
2019-02-26 01:35:08 +03:00
// Merge this module to agent recovery modules if needed
2022-01-16 23:14:14 +03:00
if ( modulesAdd [ 'windows-agentrecovery' ] != null ) {
if ( ( moduleName == 'win-console' ) || ( moduleName == 'win-message-pump' ) || ( moduleName == 'win-terminal' ) || ( moduleName == 'win-virtual-terminal' ) ) {
2019-03-10 01:28:08 +03:00
modulesAdd [ 'windows-agentrecovery' ] . push ( ... moduleData ) ;
2019-02-26 01:35:08 +03:00
}
}
2017-12-13 03:04:54 +03:00
}
}
}
2019-10-10 21:13:25 +03:00
// Add plugins to cores
if ( obj . pluginHandler ) { obj . pluginHandler . addMeshCoreModules ( modulesAdd ) ; }
2022-02-04 06:11:35 +03:00
// If we need to dump modules to file, create a meshcores folder
if ( dumpToFile ) { try { obj . fs . mkdirSync ( 'meshcores' ) ; } catch ( ex ) { } }
2018-12-30 02:24:33 +03:00
// Merge the cores and compute the hashes
for ( var i in modulesAdd ) {
2019-01-22 01:05:50 +03:00
if ( ( i == 'windows-recovery' ) || ( i == 'linux-recovery' ) ) {
2019-03-10 01:28:08 +03:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshRecoveryCore ] . join ( '' ) ;
2019-02-26 01:35:08 +03:00
} else if ( ( i == 'windows-agentrecovery' ) || ( i == 'linux-agentrecovery' ) ) {
2019-03-10 01:28:08 +03:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshAgentRecoveryCore ] . join ( '' ) ;
2021-01-25 05:43:15 +03:00
} else if ( ( i == 'windows-tiny' ) || ( i == 'linux-tiny' ) ) {
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshTinyCore ] . join ( '' ) ;
2019-01-22 01:05:50 +03:00
} else {
2019-03-10 01:28:08 +03:00
obj . defaultMeshCores [ i ] = [ obj . common . IntToStr ( 0 ) , ... modulesAdd [ i ] , meshCore ] . join ( '' ) ;
2019-01-22 01:05:50 +03:00
}
2022-01-14 01:34:39 +03:00
obj . defaultMeshCores [ i ] = Buffer . from ( obj . defaultMeshCores [ i ] , 'utf8' ) ;
2020-12-29 02:21:50 +03:00
obj . defaultMeshCoresHash [ i ] = obj . crypto . createHash ( 'sha384' ) . update ( obj . defaultMeshCores [ i ] ) . digest ( 'binary' ) ;
2019-08-23 01:31:39 +03:00
obj . debug ( 'main' , 'Core module ' + i + ' is ' + obj . defaultMeshCores [ i ] . length + ' bytes.' ) ;
2022-01-14 01:34:39 +03:00
2022-02-04 06:11:35 +03:00
// Write all modules to files. Great for debugging.
if ( dumpToFile ) {
console . log ( 'Core module ' + i + ' is ' + obj . defaultMeshCores [ i ] . length + ' bytes, saving to meshcores/' + i + '.js.' ) ; // Print the core size and filename
obj . fs . writeFile ( 'meshcores/' + i + '.js' , obj . defaultMeshCores [ i ] . slice ( 4 ) , function ( ) { } ) ; // Write the core to file
}
2020-07-17 21:14:24 +03:00
// Compress the mesh cores with DEFLATE
2022-01-16 23:14:14 +03:00
const callback = function MeshCoreDeflateCb ( err , buffer ) { if ( err == null ) { obj . defaultMeshCoresDeflate [ MeshCoreDeflateCb . i ] = buffer ; } }
2020-07-17 21:14:24 +03:00
callback . i = i ;
2022-01-14 01:34:39 +03:00
require ( 'zlib' ) . deflate ( obj . defaultMeshCores [ i ] , { level : require ( 'zlib' ) . Z _BEST _COMPRESSION } , callback ) ;
2018-12-30 02:24:33 +03:00
}
}
// We are done creating all the mesh cores.
2017-12-13 03:04:54 +03:00
if ( func != null ) { func ( true ) ; }
2018-08-30 22:05:23 +03:00
} ;
2017-12-13 03:04:54 +03:00
// Update the default meshcmd
2018-01-05 02:59:57 +03:00
obj . updateMeshCmdTimer = 'notset' ;
2017-12-13 03:04:54 +03:00
obj . updateMeshCmd = function ( func ) {
2019-01-01 05:09:19 +03:00
// Figure out where meshcmd.js is and read it.
2019-03-10 01:28:08 +03:00
var meshCmd = null , meshcmdPath , moduleAdditions = [ 'var addedModules = [];\r\n' ] , moduleDirPath , modulesDir = null ;
2019-01-04 01:46:52 +03: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 ( ) ; }
2019-01-01 05:09:19 +03: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-04 01:46:52 +03: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 ( ) ; }
2019-01-01 05:09:19 +03: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 ( ) ; }
2020-06-24 22:39:11 +03:00
else { obj . defaultMeshCmd = null ; if ( func != null ) { func ( false ) ; } return ; } // meshcmd.js not found
2021-05-03 11:58:49 +03:00
meshCmd = meshCmd . replace ( "'***Mesh*Cmd*Version***'" , '\'' + getCurrentVersion ( ) + '\'' ) ;
2019-01-01 05:09:19 +03:00
// Figure out where the modules_meshcmd folder is.
2022-02-04 06:11:35 +03:00
if ( obj . args . minifycore !== false ) { try { moduleDirPath = obj . path . join ( meshcmdPath , 'modules_meshcmd_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } // Favor minified modules if present.
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( meshcmdPath , 'modules_meshcmd' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } // Use non-minified mofules.
if ( obj . args . minifycore !== false ) { if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( _ _dirname , 'agents' , 'modules_meshcmd_min' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } } // Favor minified modules if present.
if ( modulesDir == null ) { try { moduleDirPath = obj . path . join ( _ _dirname , 'agents' , 'modules_meshcmd' ) ; modulesDir = obj . fs . readdirSync ( moduleDirPath ) ; } catch ( ex ) { } } // Use non-minified mofules.
2019-01-01 05:09:19 +03:00
// Read all .js files in the meshcmd modules folder.
2017-10-17 06:11:03 +03: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 ) ;
2019-01-01 05:09:19 +03:00
if ( moduleName . endsWith ( '.min' ) ) { moduleName = moduleName . substring ( 0 , moduleName . length - 4 ) ; } // Remove the ".min" for ".min.js" files.
2022-02-04 06:11:35 +03:00
moduleAdditions . push ( 'try { addModule("' , moduleName , '", "' , obj . escapeCodeString ( obj . fs . readFileSync ( obj . path . join ( moduleDirPath , modulesDir [ i ] ) ) . toString ( 'binary' ) ) , '"); addedModules.push("' , moduleName , '"); } catch (ex) { }\r\n' ) ;
2017-10-17 06:11:03 +03:00
}
2017-10-04 04:31:20 +03:00
}
}
2017-12-13 03:04:54 +03:00
// Set the new default meshcmd.js
2019-03-10 01:28:08 +03:00
moduleAdditions . push ( meshCmd ) ;
obj . defaultMeshCmd = moduleAdditions . join ( '' ) ;
2019-01-01 05:09:19 +03: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-04 04:31:20 +03:00
if ( func != null ) { func ( true ) ; }
2018-01-05 02:59:57 +03:00
2019-01-01 05:09:19 +03:00
// Monitor for changes in meshcmd.js
if ( obj . updateMeshCmdTimer === 'notset' ) {
2018-01-05 02:59:57 +03:00
obj . updateMeshCmdTimer = null ;
2019-01-01 05:09:19 +03:00
obj . fs . watch ( meshcmdPath , function ( eventType , filename ) {
2018-01-05 02:59:57 +03:00
if ( obj . updateMeshCmdTimer != null ) { clearTimeout ( obj . updateMeshCmdTimer ) ; obj . updateMeshCmdTimer = null ; }
2019-03-10 01:28:08 +03:00
obj . updateMeshCmdTimer = setTimeout ( function ( ) { obj . updateMeshCmd ( ) ; } , 5000 ) ;
2018-08-30 22:05:23 +03:00
} ) ;
2018-01-05 02:59:57 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2017-10-04 04:31:20 +03:00
2020-11-03 08:41:58 +03:00
// List of possible mesh agent install scripts
2022-04-14 23:07:05 +03:00
const meshToolsList = {
2020-11-03 08:41:58 +03:00
'MeshCentralRouter' : { localname : 'MeshCentralRouter.exe' , dlname : 'winrouter' } ,
2022-05-18 02:17:49 +03:00
'MeshCentralAssistant' : { localname : 'MeshCentralAssistant.exe' , dlname : 'winassistant' , winhash : true }
//'MeshCentralRouterMacOS': { localname: 'MeshCentralRouter.dmg', dlname: 'MeshCentralRouter.dmg' }
2020-11-03 08:41:58 +03:00
} ;
// Update the list of available mesh agents
obj . updateMeshTools = function ( ) {
for ( var toolname in meshToolsList ) {
2021-05-19 01:44:53 +03:00
if ( meshToolsList [ toolname ] . winhash === true ) {
var toolpath = obj . path . join ( _ _dirname , 'agents' , meshToolsList [ toolname ] . localname ) ;
2022-04-14 23:07:05 +03:00
const toolpath2 = obj . path . join ( obj . datapath , 'agents' , meshToolsList [ toolname ] . localname ) ;
2022-01-19 21:59:23 +03:00
if ( obj . fs . existsSync ( toolpath2 ) ) { toolpath = toolpath2 ; } // If the tool is present in "meshcentral-data/agents", use that one instead.
2021-05-19 01:44:53 +03:00
var hashStream = obj . crypto . createHash ( 'sha384' ) ;
hashStream . toolname = toolname ;
hashStream . toolpath = toolpath ;
hashStream . dlname = meshToolsList [ toolname ] . dlname ;
hashStream . hashx = 0 ;
hashStream . on ( 'data' , function ( data ) {
obj . meshToolsBinaries [ this . toolname ] = { hash : data . toString ( 'hex' ) , hashx : this . hashx , path : this . toolpath , dlname : this . dlname , url : this . url } ;
2020-11-05 13:29:13 +03:00
obj . meshToolsBinaries [ this . toolname ] . url = 'https://' + obj . certificates . CommonName + ':' + ( ( typeof obj . args . aliasport == 'number' ) ? obj . args . aliasport : obj . args . port ) + '/meshagents?meshaction=' + this . dlname ;
2020-11-03 08:41:58 +03:00
var stats = null ;
2022-02-04 06:11:35 +03:00
try { stats = obj . fs . statSync ( this . toolpath ) ; } catch ( ex ) { }
2020-11-03 08:41:58 +03:00
if ( stats != null ) { obj . meshToolsBinaries [ this . toolname ] . size = stats . size ; }
} ) ;
2022-04-14 23:07:05 +03:00
const options = { sourcePath : toolpath , targetStream : hashStream } ;
2021-05-19 01:44:53 +03:00
obj . exeHandler . hashExecutableFile ( options ) ;
} else {
var toolpath = obj . path . join ( _ _dirname , 'agents' , meshToolsList [ toolname ] . localname ) ;
2022-04-14 23:07:05 +03:00
const toolpath2 = obj . path . join ( obj . datapath , 'agents' , meshToolsList [ toolname ] . localname ) ;
2022-01-19 21:59:23 +03:00
if ( obj . fs . existsSync ( toolpath2 ) ) { toolpath = toolpath2 ; } // If the tool is present in "meshcentral-data/agents", use that one instead.
2021-05-19 01:44:53 +03:00
var stream = null ;
try {
stream = obj . fs . createReadStream ( toolpath ) ;
stream . on ( 'data' , function ( data ) { this . hash . update ( data , 'binary' ) ; this . hashx += data . length ; } ) ;
stream . on ( 'error' , function ( data ) {
// If there is an error reading this file, make sure this agent is not in the agent table
if ( obj . meshToolsBinaries [ this . toolname ] != null ) { delete obj . meshToolsBinaries [ this . toolname ] ; }
} ) ;
stream . on ( 'end' , function ( ) {
// Add the agent to the agent table with all information and the hash
obj . meshToolsBinaries [ this . toolname ] = { } ;
obj . meshToolsBinaries [ this . toolname ] . hash = this . hash . digest ( 'hex' ) ;
obj . meshToolsBinaries [ this . toolname ] . hashx = this . hashx ;
obj . meshToolsBinaries [ this . toolname ] . path = this . agentpath ;
obj . meshToolsBinaries [ this . toolname ] . dlname = this . dlname ;
obj . meshToolsBinaries [ this . toolname ] . url = 'https://' + obj . certificates . CommonName + ':' + ( ( typeof obj . args . aliasport == 'number' ) ? obj . args . aliasport : obj . args . port ) + '/meshagents?meshaction=' + this . dlname ;
var stats = null ;
2022-02-04 06:11:35 +03:00
try { stats = obj . fs . statSync ( this . agentpath ) ; } catch ( ex ) { }
2021-05-19 01:44:53 +03:00
if ( stats != null ) { obj . meshToolsBinaries [ this . toolname ] . size = stats . size ; }
} ) ;
stream . toolname = toolname ;
stream . agentpath = toolpath ;
stream . dlname = meshToolsList [ toolname ] . dlname ;
stream . hash = obj . crypto . createHash ( 'sha384' , stream ) ;
stream . hashx = 0 ;
2022-02-04 06:11:35 +03:00
} catch ( ex ) { }
2021-05-19 01:44:53 +03:00
}
2020-11-03 08:41:58 +03:00
}
} ;
2017-08-28 19:27:45 +03:00
// List of possible mesh agent install scripts
2022-04-14 23:07:05 +03:00
const meshAgentsInstallScriptList = {
2019-03-02 05:07:38 +03:00
1 : { id : 1 , localname : 'meshinstall-linux.sh' , rname : 'meshinstall.sh' , linux : true } ,
2019-07-23 03:26:26 +03:00
2 : { id : 2 , localname : 'meshinstall-initd.sh' , rname : 'meshagent' , linux : true } ,
2020-08-02 07:12:07 +03:00
5 : { id : 5 , localname : 'meshinstall-bsd-rcd.sh' , rname : 'meshagent' , linux : true } ,
6 : { id : 6 , localname : 'meshinstall-linux.js' , rname : 'meshinstall.js' , linux : true }
2017-08-28 19:27:45 +03: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-31 00:43:42 +03:00
stream . xdata = '' ;
stream . on ( 'data' , function ( data ) { this . hash . update ( data , 'binary' ) ; this . xdata += data ; } ) ;
2017-08-28 19:27:45 +03: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 21:45:06 +03:00
if ( obj . meshAgentInstallScripts [ this . info . id ] != null ) { delete obj . meshAgentInstallScripts [ this . info . id ] ; }
2017-08-28 19:27:45 +03:00
} ) ;
stream . on ( 'end' , function ( ) {
// Add the agent to the agent table with all information and the hash
2019-10-11 21:16:36 +03:00
obj . meshAgentInstallScripts [ this . info . id ] = Object . assign ( { } , this . info ) ;
2017-08-28 19:27:45 +03:00
obj . meshAgentInstallScripts [ this . info . id ] . hash = this . hash . digest ( 'hex' ) ;
obj . meshAgentInstallScripts [ this . info . id ] . path = this . agentpath ;
2019-01-31 00:43:42 +03:00
obj . meshAgentInstallScripts [ this . info . id ] . data = this . xdata ;
2020-11-05 13:29:13 +03:00
obj . meshAgentInstallScripts [ this . info . id ] . url = 'https://' + obj . certificates . CommonName + ':' + ( ( typeof obj . args . aliasport == 'number' ) ? obj . args . aliasport : obj . args . port ) + '/meshagents?script=' + this . info . id ;
2017-08-28 19:27:45 +03:00
var stats = null ;
2022-02-04 06:11:35 +03:00
try { stats = obj . fs . statSync ( this . agentpath ) ; } catch ( ex ) { }
2017-08-28 19:27:45 +03:00
if ( stats != null ) { obj . meshAgentInstallScripts [ this . info . id ] . size = stats . size ; }
2019-03-02 05:07:38 +03: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 19:27:45 +03:00
} ) ;
stream . info = meshAgentsInstallScriptList [ scriptid ] ;
stream . agentpath = scriptpath ;
2017-10-15 09:22:19 +03:00
stream . hash = obj . crypto . createHash ( 'sha384' , stream ) ;
2022-02-04 06:11:35 +03:00
} catch ( ex ) { }
2017-08-28 19:27:45 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2024-03-10 10:46:01 +03:00
2017-08-28 19:27:45 +03:00
// List of possible mesh agents
2018-01-03 03:52:49 +03:00
obj . meshAgentsArchitectureNumbers = {
2021-01-25 05:43:15 +03: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' , tcore : 'linux-tiny' } ,
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' , tcore : 'windows-tiny' } ,
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' , tcore : 'windows-tiny' } ,
2022-05-29 08:34:48 +03:00
3 : { id : 3 , localname : 'MeshService.exe' , rname : 'meshagent32.exe' , desc : 'Windows x86-32 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' , codesign : true } ,
4 : { id : 4 , localname : 'MeshService64.exe' , rname : 'meshagent64.exe' , desc : 'Windows x86-64 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' , codesign : true } ,
2021-01-25 05:43:15 +03: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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
11 : { id : 11 , localname : 'meshagent_osx-x86-32' , rname : 'meshosx' , desc : 'Apple macOS x86-32' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Apple x86-32 binary, no longer supported.
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
2023-10-21 03:29:48 +03:00
14 : { id : 14 , localname : 'meshagent_android.apk' , rname : 'meshandroid.apk' , desc : 'Android' , update : false , amt : false , platform : 'android' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Get this one from Google Play
2021-01-25 05:43:15 +03:00
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' , tcore : 'linux-tiny' } ,
16 : { id : 16 , localname : 'meshagent_osx-x86-64' , rname : 'meshagent' , desc : 'Apple macOS x86-64' , update : true , amt : false , platform : 'osx' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Apple x86-64 binary
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' , tcore : 'linux-tiny' } , // 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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'windows-tiny' } ,
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' , tcore : 'windows-tiny' } ,
23 : { id : 23 , localname : 'MeshAgent-NodeJS' , rname : 'meshagent' , desc : 'NodeJS' , update : false , amt : false , platform : 'node' , core : 'nodejs' , rcore : 'nodejs' , arcore : 'nodejs' , tcore : 'nodejs' } , // NodeJS based agent
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' , tcore : 'linux-tiny' } ,
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' , tcore : 'linux-tiny' } , // "armv6l" and "armv7l"
2021-08-31 02:03:23 +03:00
26 : { id : 26 , localname : 'meshagent_aarch64' , rname : 'meshagent' , desc : 'Linux ARM 64 bit (glibc/2.24)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // This is replaced by ARCHID 32
2021-01-25 05:43:15 +03: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' , tcore : 'linux-tiny' } , // Raspbian 7 2015-02-02 for old Raspberry Pi.
28 : { id : 28 , localname : 'meshagent_mips24kc' , rname : 'meshagent' , desc : 'Linux MIPS24KC/MUSL (OpenWRT)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // MIPS Router with OpenWRT
29 : { id : 29 , localname : 'meshagent_osx-arm-64' , rname : 'meshagent' , desc : 'Apple macOS ARM-64' , update : true , amt : false , platform : 'osx' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Apple Silicon ARM 64bit
30 : { id : 30 , localname : 'meshagent_freebsd_x86-64' , rname : 'meshagent' , desc : 'FreeBSD x86-64' , update : true , amt : false , platform : 'freebsd' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // FreeBSD x64
2021-08-31 02:03:23 +03:00
32 : { id : 32 , localname : 'meshagent_aarch64' , rname : 'meshagent' , desc : 'Linux ARM 64 bit (glibc/2.24)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } ,
33 : { id : 33 , localname : 'meshagent_openwrt_x86_64' , rname : 'meshagent' , desc : 'OpenWRT x86-64' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // This is replaced with ARCHID 36.
2021-05-11 08:23:03 +03:00
34 : { id : 34 , localname : 'assistant_windows' , rname : 'meshassistant' , desc : 'MeshCentral Assistant (Windows)' , update : false , amt : false , platform : 'assistant' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // MeshCentral Assistant for Windows
2021-08-05 05:01:02 +03:00
35 : { id : 35 , localname : 'meshagent_linux-armada370-hf' , rname : 'meshagent' , desc : 'Armada370 - ARM32/HF (libc/2.26)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Armada370
2021-08-06 09:19:35 +03:00
36 : { id : 36 , localname : 'meshagent_openwrt_x86_64' , rname : 'meshagent' , desc : 'OpenWRT x86-64' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // OpenWRT x86-64
2021-08-30 22:33:36 +03:00
37 : { id : 37 , localname : 'meshagent_openbsd_x86-64' , rname : 'meshagent' , desc : 'OpenBSD x86-64' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // OpenBSD x86-64
2021-01-25 05:43:15 +03:00
40 : { id : 40 , localname : 'meshagent_mipsel24kc' , rname : 'meshagent' , desc : 'Linux MIPSEL24KC (OpenWRT)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // MIPS Router with OpenWRT
41 : { id : 41 , localname : 'meshagent_aarch64-cortex-a53' , rname : 'meshagent' , desc : 'ARMADA/CORTEX-A53/MUSL (OpenWRT)' , update : true , amt : false , platform : 'linux' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // OpenWRT Routers
2023-04-14 23:36:34 +03:00
42 : { id : 42 , localname : 'MeshConsoleARM64.exe' , rname : 'meshconsolearm64.exe' , desc : 'Windows ARM-64 console' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' } ,
43 : { id : 43 , localname : 'MeshServiceARM64.exe' , rname : 'meshagentarm64.exe' , desc : 'Windows ARM-64 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' , codesign : true } ,
2022-08-31 11:36:23 +03:00
10003 : { id : 10003 , localname : 'MeshService.exe' , rname : 'meshagent32.exe' , desc : 'Windows x86-32 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' , unsigned : true } ,
10004 : { id : 10004 , localname : 'MeshService64.exe' , rname : 'meshagent64.exe' , desc : 'Windows x86-64 service' , update : true , amt : true , platform : 'win32' , core : 'windows-amt' , rcore : 'windows-recovery' , arcore : 'windows-agentrecovery' , tcore : 'windows-tiny' , unsigned : true } ,
2021-05-18 07:41:20 +03:00
10005 : { id : 10005 , localname : 'meshagent_osx-universal-64' , rname : 'meshagent' , desc : 'Apple macOS Universal Binary' , update : true , amt : false , platform : 'osx' , core : 'linux-noamt' , rcore : 'linux-recovery' , arcore : 'linux-agentrecovery' , tcore : 'linux-tiny' } , // Apple Silicon + x86 universal binary
2022-08-31 11:36:23 +03:00
10006 : { id : 10006 , localname : 'MeshCentralAssistant.exe' , rname : 'MeshCentralAssistant.exe' , desc : 'MeshCentral Assistant for Windows' , update : false , amt : false , platform : 'win32' } , // MeshCentral Assistant
2023-04-14 23:36:34 +03:00
11000 : { id : 11000 , localname : 'MeshCmd.exe' , rname : 'MeshCmd.exe' , desc : 'Windows x86-32 meshcmd' , update : false , amt : true , platform : 'win32' , codesign : true } , // MeshCMD for Windows x86 32-bit
11001 : { id : 11001 , localname : 'MeshCmd64.exe' , rname : 'MeshCmd64.exe' , desc : 'Windows x86-64 meshcmd' , update : false , amt : true , platform : 'win32' , codesign : true } , // MeshCMD for Windows x86 64-bit
11002 : { id : 11002 , localname : 'MeshCmdARM64.exe' , rname : 'MeshCmdARM64.exe' , desc : 'Windows ARM-64 meshcmd' , update : false , amt : true , platform : 'win32' , codesign : true } // MeshCMD for Windows ARM 64-bit
2017-08-28 19:27:45 +03:00
} ;
2022-06-18 10:07:45 +03:00
// Sign windows agents
obj . signMeshAgents = function ( domain , func ) {
2022-03-05 20:35:04 +03:00
// Setup the domain is specified
var objx = domain , suffix = '' ;
2022-05-29 08:34:48 +03:00
if ( domain . id == '' ) { objx = obj ; } else { suffix = '-' + domain . id ; objx . meshAgentBinaries = { } ; }
2022-05-29 08:59:21 +03:00
// Check if a custom agent signing certificate is available
2022-06-18 10:07:45 +03:00
var agentSignCertInfo = require ( './authenticode.js' ) . loadCertificates ( [ obj . path . join ( obj . datapath , 'agentsigningcert.pem' ) ] ) ;
2022-05-29 08:59:21 +03:00
// If not using a custom signing cert, get agent code signature certificate ready with the full cert chain
if ( ( agentSignCertInfo == null ) && ( obj . certificates . codesign != null ) ) {
2022-05-29 08:34:48 +03:00
agentSignCertInfo = {
cert : obj . certificateOperations . forge . pki . certificateFromPem ( obj . certificates . codesign . cert ) ,
key : obj . certificateOperations . forge . pki . privateKeyFromPem ( obj . certificates . codesign . key ) ,
2022-06-18 10:07:45 +03:00
extraCerts : [ obj . certificateOperations . forge . pki . certificateFromPem ( obj . certificates . root . cert ) ]
2022-05-29 08:34:48 +03:00
}
}
2022-06-18 20:54:13 +03:00
if ( agentSignCertInfo == null ) { func ( ) ; return ; } // No code signing certificate, nothing to do.
2022-05-29 08:34:48 +03:00
2022-06-18 10:07:45 +03:00
// Setup the domain is specified
var objx = domain , suffix = '' ;
if ( domain . id == '' ) { objx = obj ; } else { suffix = '-' + domain . id ; objx . meshAgentBinaries = { } ; }
2022-05-29 08:34:48 +03:00
// Generate the agent signature description and URL
2022-06-19 07:47:44 +03:00
const serverSignedAgentsPath = obj . path . join ( obj . datapath , 'signedagents' + suffix ) ;
const signDesc = ( domain . title ? domain . title : agentSignCertInfo . cert . subject . hash ) ;
const httpsPort = ( ( obj . args . aliasport == null ) ? obj . args . port : obj . args . aliasport ) ; // Use HTTPS alias port is specified
var httpsHost = ( ( domain . dns != null ) ? domain . dns : obj . certificates . CommonName ) ;
if ( obj . args . agentaliasdns != null ) { httpsHost = obj . args . agentaliasdns ; }
var signUrl = 'https://' + httpsHost ;
if ( httpsPort != 443 ) { signUrl += ':' + httpsPort ; }
var xdomain = ( domain . dns == null ) ? domain . id : '' ;
if ( xdomain != '' ) xdomain += '/' ;
signUrl += '/' + xdomain ;
// If requested, lock the agent to this server
if ( obj . config . settings . agentsignlock ) { signUrl += '?ServerID=' + obj . certificateOperations . getPublicKeyHash ( obj . certificates . agent . cert ) . toUpperCase ( ) ; }
2022-05-29 08:34:48 +03:00
2022-06-18 10:07:45 +03:00
// Setup the time server
var timeStampUrl = 'http://timestamp.comodoca.com/authenticode' ;
if ( args . agenttimestampserver === false ) { timeStampUrl = null ; }
else if ( typeof args . agenttimestampserver == 'string' ) { timeStampUrl = args . agenttimestampserver ; }
2022-06-24 01:39:40 +03:00
// Setup the time server proxy
var timeStampProxy = null ;
if ( typeof args . agenttimestampproxy == 'string' ) { timeStampProxy = args . agenttimestampproxy ; }
else if ( ( args . agenttimestampproxy !== false ) && ( typeof args . npmproxy == 'string' ) ) { timeStampProxy = args . npmproxy ; }
2022-06-18 10:07:45 +03:00
// Setup the pending operations counter
var pendingOperations = 1 ;
2021-11-19 05:43:27 +03:00
2018-01-03 03:52:49 +03:00
for ( var archid in obj . meshAgentsArchitectureNumbers ) {
2022-06-18 10:07:45 +03:00
if ( obj . meshAgentsArchitectureNumbers [ archid ] . codesign !== true ) continue ;
2022-03-05 20:35:04 +03:00
var agentpath ;
2022-05-29 08:34:48 +03:00
if ( domain . id == '' ) {
// Load all agents when processing the default domain
2022-03-05 20:35:04 +03:00
agentpath = obj . path . join ( _ _dirname , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
var agentpath2 = obj . path . join ( obj . datapath , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2022-05-29 08:34:48 +03:00
if ( obj . fs . existsSync ( agentpath2 ) ) { agentpath = agentpath2 ; delete obj . meshAgentsArchitectureNumbers [ archid ] . codesign ; } // If the agent is present in "meshcentral-data/agents", use that one instead.
2022-03-05 20:35:04 +03:00
} else {
2022-05-29 08:34:48 +03:00
// When processing an extra domain, only load agents that are specific to that domain
2022-08-31 11:36:23 +03:00
agentpath = obj . path . join ( obj . datapath , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2022-05-29 08:34:48 +03:00
if ( obj . fs . existsSync ( agentpath ) ) { delete obj . meshAgentsArchitectureNumbers [ archid ] . codesign ; } else { continue ; } // If the agent is not present in "meshcentral-data/agents" skip.
2022-03-05 20:35:04 +03:00
}
2018-08-30 22:05:23 +03:00
2022-06-18 10:07:45 +03:00
// Open the original agent with authenticode
2022-06-20 22:03:23 +03:00
const signeedagentpath = obj . path . join ( serverSignedAgentsPath , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2022-06-18 10:07:45 +03:00
const originalAgent = require ( './authenticode.js' ) . createAuthenticodeHandler ( agentpath ) ;
if ( originalAgent != null ) {
// Check if the agent is already signed correctly
const destinationAgent = require ( './authenticode.js' ) . createAuthenticodeHandler ( signeedagentpath ) ;
var destinationAgentOk = (
( destinationAgent != null ) &&
( destinationAgent . fileHashSigned != null ) &&
( Buffer . compare ( destinationAgent . fileHashSigned , destinationAgent . fileHashActual ) == 0 ) &&
( destinationAgent . signingAttribs . indexOf ( signUrl ) >= 0 ) &&
( destinationAgent . signingAttribs . indexOf ( signDesc ) >= 0 )
) ;
2022-06-21 00:31:28 +03:00
if ( destinationAgent != null ) {
// If the agent is signed correctly, look to see if the resources in the destination agent are correct
var orgVersionStrings = originalAgent . getVersionInfo ( ) ;
if ( destinationAgentOk == true ) {
2022-08-04 11:47:36 +03:00
const versionStrings = destinationAgent . getVersionInfo ( ) ;
const versionProperties = [ 'FileDescription' , 'FileVersion' , 'InternalName' , 'LegalCopyright' , 'OriginalFilename' , 'ProductName' , 'ProductVersion' ] ;
2022-06-21 00:31:28 +03:00
for ( var i in versionProperties ) {
const prop = versionProperties [ i ] , propl = prop . toLowerCase ( ) ;
if ( ( domain . agentfileinfo != null ) && ( typeof domain . agentfileinfo == 'object' ) && ( typeof domain . agentfileinfo [ propl ] == 'string' ) ) {
2022-08-10 23:12:59 +03:00
if ( domain . agentfileinfo [ propl ] != versionStrings [ prop ] ) { destinationAgentOk = false ; break ; } // If the resource we want is not the same as the destination executable, we need to re-sign the agent.
2022-06-21 00:31:28 +03:00
} else {
2022-08-10 23:12:59 +03:00
if ( orgVersionStrings [ prop ] != versionStrings [ prop ] ) { destinationAgentOk = false ; break ; } // if the resource of the orginal agent not the same as the destination executable, we need to re-sign the agent.
2022-06-21 00:31:28 +03:00
}
}
2022-08-04 11:47:36 +03:00
// Check file version number
2022-08-10 23:12:59 +03:00
if ( destinationAgentOk == true ) {
if ( ( domain . agentfileinfo != null ) && ( typeof domain . agentfileinfo == 'object' ) && ( typeof domain . agentfileinfo [ 'fileversionnumber' ] == 'string' ) ) {
if ( domain . agentfileinfo [ 'fileversionnumber' ] != versionStrings [ '~FileVersion' ] ) { destinationAgentOk = false ; } // If the resource we want is not the same as the destination executable, we need to re-sign the agent.
} else {
if ( orgVersionStrings [ '~FileVersion' ] != versionStrings [ '~FileVersion' ] ) { destinationAgentOk = false ; } // if the resource of the orginal agent not the same as the destination executable, we need to re-sign the agent.
}
2022-08-04 11:47:36 +03:00
}
// Check product version number
2022-08-10 23:12:59 +03:00
if ( destinationAgentOk == true ) {
if ( ( domain . agentfileinfo != null ) && ( typeof domain . agentfileinfo == 'object' ) && ( typeof domain . agentfileinfo [ 'productversionnumber' ] == 'string' ) ) {
if ( domain . agentfileinfo [ 'productversionnumber' ] != versionStrings [ '~ProductVersion' ] ) { destinationAgentOk = false ; } // If the resource we want is not the same as the destination executable, we need to re-sign the agent.
} else {
if ( orgVersionStrings [ '~ProductVersion' ] != versionStrings [ '~ProductVersion' ] ) { destinationAgentOk = false ; } // if the resource of the orginal agent not the same as the destination executable, we need to re-sign the agent.
}
}
// Check the agent icon
2022-08-11 23:34:06 +03:00
if ( destinationAgentOk == true ) {
if ( ( domain . agentfileinfo != null ) && ( domain . agentfileinfo . icon != null ) ) {
// Check if the destination agent matches the icon we want
const agentIconGroups = destinationAgent . getIconInfo ( ) ;
if ( agentIconGroups != null ) {
const agentIconGroupNames = Object . keys ( agentIconGroups ) ;
if ( agentIconGroupNames . length > 0 ) {
const agentMainIconGroup = agentIconGroups [ agentIconGroupNames [ 0 ] ] ;
if ( agentMainIconGroup . resCount != domain . agentfileinfo . icon . resCount ) {
destinationAgentOk = false ; // The icon image count is different, don't bother hashing to see if the icons are different.
} else {
const agentMainIconGroupHash = require ( './authenticode.js' ) . hashObject ( agentMainIconGroup ) ;
const iconHash = require ( './authenticode.js' ) . hashObject ( domain . agentfileinfo . icon ) ;
if ( agentMainIconGroupHash != iconHash ) { destinationAgentOk = false ; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
}
}
}
} else {
// Check if the destination agent has the default icon
const agentIconGroups1 = destinationAgent . getIconInfo ( ) ;
const agentIconGroups2 = originalAgent . getIconInfo ( ) ;
if ( agentIconGroups1 . resCount != agentIconGroups2 . resCount ) {
destinationAgentOk = false ; // The icon image count is different, don't bother hashing to see if the icons are different.
} else {
const iconHash1 = require ( './authenticode.js' ) . hashObject ( agentIconGroups1 ) ;
const iconHash2 = require ( './authenticode.js' ) . hashObject ( agentIconGroups2 ) ;
if ( iconHash1 != iconHash2 ) { destinationAgentOk = false ; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
2022-08-10 23:12:59 +03:00
}
}
2022-08-04 11:47:36 +03:00
}
2022-08-12 03:37:42 +03:00
// Check the agent logo
if ( destinationAgentOk == true ) {
if ( ( domain . agentfileinfo != null ) && ( domain . agentfileinfo . logo != null ) ) {
// Check if the destination agent matches the logo we want
const agentBitmaps = destinationAgent . getBitmapInfo ( ) ;
if ( agentBitmaps != null ) {
const agentBitmapNames = Object . keys ( agentBitmaps ) ;
if ( agentBitmapNames . length > 0 ) {
const agentMainBitmap = agentBitmaps [ agentBitmapNames [ 0 ] ] ;
const agentMainBitmapHash = require ( './authenticode.js' ) . hashObject ( agentMainBitmap ) ;
const bitmapHash = require ( './authenticode.js' ) . hashObject ( domain . agentfileinfo . logo ) ;
if ( agentMainBitmapHash != bitmapHash ) { destinationAgentOk = false ; } // If the existing agent logo does not match the desired logo, we need to re-sign the agent.
}
}
} else {
// Check if the destination agent has the default icon
const agentBitmaps1 = destinationAgent . getBitmapInfo ( ) ;
const agentBitmaps2 = originalAgent . getBitmapInfo ( ) ;
const agentBitmapNames = Object . keys ( agentBitmaps1 ) ;
if ( agentBitmapNames . length == 0 ) {
destinationAgentOk = false ;
} else {
const iconHash1 = require ( './authenticode.js' ) . hashObject ( agentBitmaps1 [ agentBitmapNames [ 0 ] ] ) ;
const iconHash2 = require ( './authenticode.js' ) . hashObject ( agentBitmaps2 [ agentBitmapNames [ 0 ] ] ) ;
if ( iconHash1 != iconHash2 ) { destinationAgentOk = false ; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
}
}
}
2022-06-21 00:31:28 +03:00
}
2022-08-11 23:34:06 +03:00
// If everything looks ok, runs a hash of the original and destination agent .text, .data and .rdata sections. If different, sign the agent again.
if ( ( destinationAgentOk == true ) && ( originalAgent . getHashOfSection ( 'sha384' , '.text' ) . compare ( destinationAgent . getHashOfSection ( 'sha384' , '.text' ) ) != 0 ) ) { destinationAgentOk = false ; }
if ( ( destinationAgentOk == true ) && ( originalAgent . getHashOfSection ( 'sha384' , '.data' ) . compare ( destinationAgent . getHashOfSection ( 'sha384' , '.data' ) ) != 0 ) ) { destinationAgentOk = false ; }
if ( ( destinationAgentOk == true ) && ( originalAgent . getHashOfSection ( 'sha384' , '.rdata' ) . compare ( destinationAgent . getHashOfSection ( 'sha384' , '.rdata' ) ) != 0 ) ) { destinationAgentOk = false ; }
2022-06-21 00:31:28 +03:00
// We are done comparing the destination agent, close it.
destinationAgent . close ( ) ;
}
2022-06-18 10:07:45 +03:00
if ( destinationAgentOk == false ) {
// If not signed correctly, sign it. First, create the server signed agent folder if needed
try { obj . fs . mkdirSync ( serverSignedAgentsPath ) ; } catch ( ex ) { }
const xagentSignedFunc = function agentSignedFunc ( err , size ) {
if ( err == null ) {
2022-06-02 02:29:18 +03:00
// Agent was signed succesfuly
2022-08-31 11:36:23 +03:00
console . log ( obj . common . format ( 'Code signed {0}.' , agentSignedFunc . objx . meshAgentsArchitectureNumbers [ agentSignedFunc . archid ] . localname ) ) ;
2022-06-02 02:29:18 +03:00
} else {
2022-06-19 07:47:44 +03:00
// Failed to sign agent
2024-03-10 10:46:01 +03:00
addServerWarning ( 'Failed to sign \"' + agentSignedFunc . objx . meshAgentsArchitectureNumbers [ agentSignedFunc . archid ] . localname + '\": ' + err , 22 , [ agentSignedFunc . objx . meshAgentsArchitectureNumbers [ agentSignedFunc . archid ] . localname , err ] ) ;
2022-06-02 02:29:18 +03:00
}
2022-06-18 10:07:45 +03:00
if ( -- pendingOperations === 0 ) { agentSignedFunc . func ( ) ; }
2022-05-29 08:34:48 +03:00
}
2022-06-18 10:07:45 +03:00
pendingOperations ++ ;
xagentSignedFunc . func = func ;
xagentSignedFunc . objx = objx ;
xagentSignedFunc . archid = archid ;
xagentSignedFunc . signeedagentpath = signeedagentpath ;
2022-06-20 22:03:23 +03:00
2022-06-21 00:31:28 +03:00
// Parse the resources in the executable and make any required changes
var resChanges = false , versionStrings = null ;
if ( ( domain . agentfileinfo != null ) && ( typeof domain . agentfileinfo == 'object' ) ) {
versionStrings = originalAgent . getVersionInfo ( ) ;
var versionProperties = [ 'FileDescription' , 'FileVersion' , 'InternalName' , 'LegalCopyright' , 'OriginalFilename' , 'ProductName' , 'ProductVersion' ] ;
2022-08-10 23:12:59 +03:00
// Change the agent string properties
2022-06-21 00:31:28 +03:00
for ( var i in versionProperties ) {
const prop = versionProperties [ i ] , propl = prop . toLowerCase ( ) ;
if ( domain . agentfileinfo [ propl ] && ( domain . agentfileinfo [ propl ] != versionStrings [ prop ] ) ) { versionStrings [ prop ] = domain . agentfileinfo [ propl ] ; resChanges = true ; }
}
2022-08-10 23:12:59 +03:00
// Change the agent file version
2022-08-04 11:47:36 +03:00
if ( domain . agentfileinfo [ 'fileversionnumber' ] && ( domain . agentfileinfo [ 'fileversionnumber' ] != versionStrings [ '~FileVersion' ] ) ) {
versionStrings [ '~FileVersion' ] = domain . agentfileinfo [ 'fileversionnumber' ] ; resChanges = true ;
}
2022-08-10 23:12:59 +03:00
// Change the agent product version
2022-08-04 11:47:36 +03:00
if ( domain . agentfileinfo [ 'productversionnumber' ] && ( domain . agentfileinfo [ 'productversionnumber' ] != versionStrings [ '~ProductVersion' ] ) ) {
versionStrings [ '~ProductVersion' ] = domain . agentfileinfo [ 'productversionnumber' ] ; resChanges = true ;
}
2022-06-21 00:31:28 +03:00
if ( resChanges == true ) { originalAgent . setVersionInfo ( versionStrings ) ; }
2022-08-10 23:12:59 +03:00
// Change the agent icon
if ( domain . agentfileinfo . icon != null ) {
const agentIconGroups = originalAgent . getIconInfo ( ) ;
if ( agentIconGroups != null ) {
const agentIconGroupNames = Object . keys ( agentIconGroups ) ;
if ( agentIconGroupNames . length > 0 ) {
const agentMainIconGroupName = agentIconGroupNames [ 0 ] ;
agentIconGroups [ agentIconGroupNames [ 0 ] ] = domain . agentfileinfo . icon ;
originalAgent . setIconInfo ( agentIconGroups ) ;
}
}
}
2022-08-12 03:37:42 +03:00
// Change the agent logo
if ( domain . agentfileinfo . logo != null ) {
const agentBitmaps = originalAgent . getBitmapInfo ( ) ;
if ( agentBitmaps != null ) {
const agentBitmapNames = Object . keys ( agentBitmaps ) ;
if ( agentBitmapNames . length > 0 ) {
agentBitmaps [ agentBitmapNames [ 0 ] ] = domain . agentfileinfo . logo ;
originalAgent . setBitmapInfo ( agentBitmaps ) ;
}
}
}
2022-06-21 00:31:28 +03:00
}
2022-06-24 01:39:40 +03:00
const signingArguments = { out : signeedagentpath , desc : signDesc , url : signUrl , time : timeStampUrl , proxy : timeStampProxy } ; // Shallow clone
2022-08-31 11:36:23 +03:00
obj . debug ( 'main' , "Code signing with arguments: " + JSON . stringify ( signingArguments ) ) ;
2022-06-20 22:03:23 +03:00
if ( resChanges == false ) {
// Sign the agent the simple way, without changing any resources.
2022-06-21 00:31:28 +03:00
originalAgent . sign ( agentSignCertInfo , signingArguments , xagentSignedFunc ) ;
2022-06-20 22:03:23 +03:00
} else {
// Change the agent resources and sign the agent, this is a much more involved process.
// NOTE: This is experimental and could corupt the agent.
2022-06-21 00:31:28 +03:00
originalAgent . writeExecutable ( signingArguments , agentSignCertInfo , xagentSignedFunc ) ;
2022-06-20 22:03:23 +03:00
}
2022-06-18 10:07:45 +03:00
} else {
// Signed agent is already ok, use it.
2022-05-29 08:34:48 +03:00
originalAgent . close ( ) ;
2018-01-20 05:04:54 +03:00
}
2022-05-29 08:34:48 +03:00
}
2022-06-18 10:07:45 +03:00
}
if ( -- pendingOperations === 0 ) { func ( ) ; }
}
// Update the list of available mesh agents
obj . updateMeshAgentsTable = function ( domain , func ) {
// Check if a custom agent signing certificate is available
var agentSignCertInfo = require ( './authenticode.js' ) . loadCertificates ( [ obj . path . join ( obj . datapath , 'agentsigningcert.pem' ) ] ) ;
2022-06-18 20:54:13 +03:00
// If not using a custom signing cert, get agent code signature certificate ready with the full cert chain
if ( ( agentSignCertInfo == null ) && ( obj . certificates . codesign != null ) ) {
agentSignCertInfo = {
cert : obj . certificateOperations . forge . pki . certificateFromPem ( obj . certificates . codesign . cert ) ,
key : obj . certificateOperations . forge . pki . privateKeyFromPem ( obj . certificates . codesign . key ) ,
extraCerts : [ obj . certificateOperations . forge . pki . certificateFromPem ( obj . certificates . root . cert ) ]
}
}
2022-06-18 10:07:45 +03:00
// Setup the domain is specified
var objx = domain , suffix = '' ;
if ( domain . id == '' ) { objx = obj ; } else { suffix = '-' + domain . id ; objx . meshAgentBinaries = { } ; }
// Load agent information file. This includes the data & time of the agent.
const agentInfo = [ ] ;
try { agentInfo = JSON . parse ( obj . fs . readFileSync ( obj . path . join ( _ _dirname , 'agents' , 'hashagents.json' ) , 'utf8' ) ) ; } catch ( ex ) { }
var archcount = 0 ;
for ( var archid in obj . meshAgentsArchitectureNumbers ) {
var agentpath ;
if ( domain . id == '' ) {
// Load all agents when processing the default domain
agentpath = obj . path . join ( _ _dirname , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2022-08-31 11:36:23 +03:00
if ( obj . meshAgentsArchitectureNumbers [ archid ] . unsigned !== true ) {
const agentpath2 = obj . path . join ( obj . datapath , 'signedagents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
if ( obj . fs . existsSync ( agentpath2 ) ) { agentpath = agentpath2 ; } // If the agent is present in "meshcentral-data/signedagents", use that one instead.
const agentpath3 = obj . path . join ( obj . datapath , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
if ( obj . fs . existsSync ( agentpath3 ) ) { agentpath = agentpath3 ; } // If the agent is present in "meshcentral-data/agents", use that one instead.
}
2022-06-18 10:07:45 +03:00
} else {
// When processing an extra domain, only load agents that are specific to that domain
2022-08-31 11:36:23 +03:00
agentpath = obj . path . join ( obj . datapath , 'agents' + suffix , obj . meshAgentsArchitectureNumbers [ archid ] . localname ) ;
2022-06-18 10:07:45 +03:00
if ( obj . fs . existsSync ( agentpath ) ) { delete obj . meshAgentsArchitectureNumbers [ archid ] . codesign ; } else { continue ; } // If the agent is not present in "meshcentral-data/agents" skip.
}
// Fetch agent binary information
var stats = null ;
try { stats = obj . fs . statSync ( agentpath ) ; } catch ( ex ) { }
if ( ( stats == null ) ) continue ; // If this agent does not exist, skip it.
2019-02-15 09:16:15 +03:00
2022-05-29 08:34:48 +03:00
// Setup agent information
archcount ++ ;
objx . meshAgentBinaries [ archid ] = Object . assign ( { } , obj . meshAgentsArchitectureNumbers [ archid ] ) ;
objx . meshAgentBinaries [ archid ] . path = agentpath ;
objx . meshAgentBinaries [ archid ] . url = 'http://' + obj . certificates . CommonName + ':' + ( ( typeof obj . args . aliasport == 'number' ) ? obj . args . aliasport : obj . args . port ) + '/meshagents?id=' + archid ;
objx . meshAgentBinaries [ archid ] . size = stats . size ;
if ( ( agentInfo [ archid ] != null ) && ( agentInfo [ archid ] . mtime != null ) ) { objx . meshAgentBinaries [ archid ] . mtime = new Date ( agentInfo [ archid ] . mtime ) ; } // Set agent time if available
// If this is a windows binary, pull binary information
if ( obj . meshAgentsArchitectureNumbers [ archid ] . platform == 'win32' ) {
try { objx . meshAgentBinaries [ archid ] . pe = obj . exeHandler . parseWindowsExecutable ( agentpath ) ; } catch ( ex ) { }
}
// If agents must be stored in RAM or if this is a Windows 32/64 agent, load the agent in RAM.
if ( ( obj . args . agentsinram === true ) || ( ( ( archid == 3 ) || ( archid == 4 ) ) && ( obj . args . agentsinram !== false ) ) ) {
if ( ( archid == 3 ) || ( archid == 4 ) ) {
// Load the agent with a random msh added to it.
const outStream = new require ( 'stream' ) . Duplex ( ) ;
outStream . meshAgentBinary = objx . meshAgentBinaries [ archid ] ;
2022-06-18 20:43:03 +03:00
if ( agentSignCertInfo ) { outStream . meshAgentBinary . randomMsh = agentSignCertInfo . cert . subject . hash ; } else { outStream . meshAgentBinary . randomMsh = obj . crypto . randomBytes ( 16 ) . toString ( 'hex' ) ; }
2022-05-29 08:34:48 +03:00
outStream . bufferList = [ ] ;
outStream . _write = function ( chunk , encoding , callback ) { this . bufferList . push ( chunk ) ; if ( callback ) callback ( ) ; } ; // Append the chuck.
outStream . _read = function ( size ) { } ; // Do nothing, this is not going to be called.
outStream . on ( 'finish' , function ( ) {
// Merge all chunks
this . meshAgentBinary . data = Buffer . concat ( this . bufferList ) ;
this . meshAgentBinary . size = this . meshAgentBinary . data . length ;
delete this . bufferList ;
// Hash the uncompressed binary
const hash = obj . crypto . createHash ( 'sha384' ) . update ( this . meshAgentBinary . data ) ;
this . meshAgentBinary . fileHash = hash . digest ( 'binary' ) ;
this . meshAgentBinary . fileHashHex = Buffer . from ( this . meshAgentBinary . fileHash , 'binary' ) . toString ( 'hex' ) ;
2020-07-22 02:20:17 +03:00
// Compress the agent using ZIP
2022-04-14 23:07:05 +03:00
const archive = require ( 'archiver' ) ( 'zip' , { level : 9 } ) ; // Sets the compression method.
2020-07-22 02:20:17 +03:00
const onZipData = function onZipData ( buffer ) { onZipData . x . zacc . push ( buffer ) ; }
const onZipEnd = function onZipEnd ( ) {
// Concat all the buffer for create compressed zip agent
2022-04-14 23:07:05 +03:00
const concatData = Buffer . concat ( onZipData . x . zacc ) ;
2020-07-22 02:20:17 +03:00
delete onZipData . x . zacc ;
// Hash the compressed binary
2022-04-14 23:07:05 +03:00
const hash = obj . crypto . createHash ( 'sha384' ) . update ( concatData ) ;
2020-07-22 02:20:17 +03:00
onZipData . x . zhash = hash . digest ( 'binary' ) ;
onZipData . x . zhashhex = Buffer . from ( onZipData . x . zhash , 'binary' ) . toString ( 'hex' ) ;
// Set the agent
onZipData . x . zdata = concatData ;
onZipData . x . zsize = concatData . length ;
}
const onZipError = function onZipError ( ) { delete onZipData . x . zacc ; }
2022-05-29 08:34:48 +03:00
this . meshAgentBinary . zacc = [ ] ;
onZipData . x = this . meshAgentBinary ;
onZipEnd . x = this . meshAgentBinary ;
onZipError . x = this . meshAgentBinary ;
2020-07-22 02:20:17 +03:00
archive . on ( 'data' , onZipData ) ;
archive . on ( 'end' , onZipEnd ) ;
archive . on ( 'error' , onZipError ) ;
2022-05-29 08:34:48 +03:00
// Starting with NodeJS v16, passing in a buffer at archive.append() will result a compressed file with zero byte length. To fix this, we pass in the buffer as a stream.
// archive.append(this.meshAgentBinary.data, { name: 'meshagent' }); // This is the version that does not work on NodeJS v16.
const ReadableStream = require ( 'stream' ) . Readable ;
const zipInputStream = new ReadableStream ( ) ;
zipInputStream . push ( this . meshAgentBinary . data ) ;
zipInputStream . push ( null ) ;
archive . append ( zipInputStream , { name : 'meshagent' } ) ;
2020-07-22 02:20:17 +03:00
archive . finalize ( ) ;
2022-05-29 08:34:48 +03:00
} )
obj . exeHandler . streamExeWithMeshPolicy (
{
platform : 'win32' ,
sourceFileName : agentpath ,
destinationStream : outStream ,
randomPolicy : true , // Indicates that the msh policy is random data.
msh : outStream . meshAgentBinary . randomMsh ,
peinfo : objx . meshAgentBinaries [ archid ] . pe
} ) ;
} else {
// Load the agent as-is
objx . meshAgentBinaries [ archid ] . data = obj . fs . readFileSync ( agentpath ) ;
2019-10-23 23:12:30 +03:00
2022-05-29 08:34:48 +03:00
// Compress the agent using ZIP
const archive = require ( 'archiver' ) ( 'zip' , { level : 9 } ) ; // Sets the compression method.
2021-02-15 09:22:53 +03:00
2022-05-29 08:34:48 +03:00
const onZipData = function onZipData ( buffer ) { onZipData . x . zacc . push ( buffer ) ; }
const onZipEnd = function onZipEnd ( ) {
// Concat all the buffer for create compressed zip agent
const concatData = Buffer . concat ( onZipData . x . zacc ) ;
delete onZipData . x . zacc ;
// Hash the compressed binary
const hash = obj . crypto . createHash ( 'sha384' ) . update ( concatData ) ;
onZipData . x . zhash = hash . digest ( 'binary' ) ;
onZipData . x . zhashhex = Buffer . from ( onZipData . x . zhash , 'binary' ) . toString ( 'hex' ) ;
// Set the agent
onZipData . x . zdata = concatData ;
onZipData . x . zsize = concatData . length ;
//console.log('Packed', onZipData.x.size, onZipData.x.zsize);
}
const onZipError = function onZipError ( ) { delete onZipData . x . zacc ; }
objx . meshAgentBinaries [ archid ] . zacc = [ ] ;
onZipData . x = objx . meshAgentBinaries [ archid ] ;
onZipEnd . x = objx . meshAgentBinaries [ archid ] ;
onZipError . x = objx . meshAgentBinaries [ archid ] ;
archive . on ( 'data' , onZipData ) ;
archive . on ( 'end' , onZipEnd ) ;
archive . on ( 'error' , onZipError ) ;
archive . append ( objx . meshAgentBinaries [ archid ] . data , { name : 'meshagent' } ) ;
archive . finalize ( ) ;
2021-02-15 09:22:53 +03:00
}
2018-01-20 05:04:54 +03:00
}
2022-05-29 08:34:48 +03:00
// Hash the binary
const hashStream = obj . crypto . createHash ( 'sha384' ) ;
hashStream . archid = archid ;
hashStream . on ( 'data' , function ( data ) {
objx . meshAgentBinaries [ this . archid ] . hash = data . toString ( 'binary' ) ;
objx . meshAgentBinaries [ this . archid ] . hashhex = data . toString ( 'hex' ) ;
if ( ( -- archcount == 0 ) && ( func != null ) ) { func ( ) ; }
} ) ;
const options = { sourcePath : agentpath , targetStream : hashStream , platform : obj . meshAgentsArchitectureNumbers [ archid ] . platform } ;
if ( objx . meshAgentBinaries [ archid ] . pe != null ) { options . peinfo = objx . meshAgentBinaries [ archid ] . pe ; }
obj . exeHandler . hashExecutableFile ( options ) ;
// If we are not loading Windows binaries to RAM, compute the RAW file hash of the signed binaries here.
if ( ( obj . args . agentsinram === false ) && ( ( archid == 3 ) || ( archid == 4 ) ) ) {
const hash = obj . crypto . createHash ( 'sha384' ) . update ( obj . fs . readFileSync ( agentpath ) ) ;
objx . meshAgentBinaries [ archid ] . fileHash = hash . digest ( 'binary' ) ;
objx . meshAgentBinaries [ archid ] . fileHashHex = Buffer . from ( objx . meshAgentBinaries [ archid ] . fileHash , 'binary' ) . toString ( 'hex' ) ;
}
2017-08-28 19:27:45 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
2017-12-14 01:52:57 +03:00
// Generate a time limited user login token
obj . getLoginToken = function ( userid , func ) {
2018-01-03 03:52:49 +03:00
if ( ( userid == null ) || ( typeof userid != 'string' ) ) { func ( 'Invalid userid.' ) ; return ; }
2022-04-14 23:07:05 +03:00
const x = userid . split ( '/' ) ;
2017-12-14 01:52:57 +03:00
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 22:38:05 +03:00
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
2017-12-14 01:52:57 +03: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 22:05:23 +03:00
} ;
2017-12-14 01:52:57 +03:00
2018-12-29 08:55:23 +03:00
// Show the user login token generation key
2017-12-14 01:52:57 +03:00
obj . showLoginTokenKey = function ( func ) {
// Load the login cookie encryption key from the database
obj . db . Get ( 'LoginCookieEncryptionKey' , function ( err , docs ) {
2018-12-29 22:38:05 +03:00
if ( ( docs . length > 0 ) && ( docs [ 0 ] . key != null ) && ( obj . args . logintokengen == null ) && ( docs [ 0 ] . key . length >= 160 ) ) {
2017-12-14 01:52:57 +03: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 22:05:23 +03:00
} ;
2017-12-14 01:52:57 +03:00
2021-04-04 04:29:02 +03:00
// Load the list of Intel AMT UUID and passwords from "amtactivation.log"
obj . loadAmtActivationLogPasswords = function ( func ) {
2022-04-14 23:07:05 +03:00
const amtlogfilename = obj . path . join ( obj . datapath , 'amtactivation.log' ) ;
2021-04-04 04:29:02 +03:00
obj . fs . readFile ( amtlogfilename , 'utf8' , function ( err , data ) {
2022-04-14 23:07:05 +03:00
const amtPasswords = { } ; // UUID --> [Passwords]
2021-04-04 04:29:02 +03:00
if ( ( err == null ) && ( data != null ) ) {
2021-10-13 21:04:02 +03:00
const lines = data . split ( '\n' ) ;
2021-04-04 04:29:02 +03:00
for ( var i in lines ) {
2022-04-14 23:07:05 +03:00
const line = lines [ i ] ;
2021-04-04 04:29:02 +03:00
if ( line . startsWith ( '{' ) ) {
2021-10-13 08:36:41 +03:00
var j = null ;
try { j = JSON . parse ( line ) ; } catch ( ex ) { }
if ( ( j != null ) && ( typeof j == 'object' ) ) {
if ( ( typeof j . amtUuid == 'string' ) && ( typeof j . password == 'string' ) ) {
if ( amtPasswords [ j . amtUuid ] == null ) {
amtPasswords [ j . amtUuid ] = [ j . password ] ; // Add password to array
} else {
amtPasswords [ j . amtUuid ] . unshift ( j . password ) ; // Add password at the start of the array
}
2021-04-04 04:29:02 +03:00
}
}
}
}
2021-04-06 00:55:52 +03:00
// Remove all duplicates and only keep the 3 last passwords for any given device
for ( var i in amtPasswords ) {
amtPasswords [ i ] = [ ... new Set ( amtPasswords [ i ] ) ] ;
while ( amtPasswords [ i ] . length > 3 ) { amtPasswords [ i ] . pop ( ) ; }
}
2021-04-04 04:29:02 +03:00
}
func ( obj . common . sortObj ( amtPasswords ) ) ; // Sort by UUID
} ) ;
}
2022-07-13 03:45:19 +03:00
// Encrypt session data
obj . encryptSessionData = function ( data , key ) {
if ( data == null ) return null ;
if ( key == null ) { key = obj . loginCookieEncryptionKey ; }
try {
const iv = Buffer . from ( obj . crypto . randomBytes ( 12 ) , 'binary' ) , cipher = obj . crypto . createCipheriv ( 'aes-256-gcm' , key . slice ( 0 , 32 ) , iv ) ;
const crypted = Buffer . concat ( [ cipher . update ( JSON . stringify ( data ) , 'utf8' ) , cipher . final ( ) ] ) ;
2024-05-18 21:45:31 +03:00
return Buffer . concat ( [ iv , cipher . getAuthTag ( ) , crypted ] ) . toString ( obj . args . cookieencoding ? obj . args . cookieencoding : 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ;
2022-07-13 03:45:19 +03:00
} catch ( ex ) { return null ; }
}
// Decrypt the session data
obj . decryptSessionData = function ( data , key ) {
if ( ( typeof data != 'string' ) || ( data . length < 13 ) ) return { } ;
if ( key == null ) { key = obj . loginCookieEncryptionKey ; }
try {
2024-05-18 21:45:31 +03:00
const buf = Buffer . from ( data . replace ( /\@/g , '+' ) . replace ( /\$/g , '/' ) , obj . args . cookieencoding ? obj . args . cookieencoding : 'base64' ) ;
2022-07-13 03:45:19 +03:00
const decipher = obj . crypto . createDecipheriv ( 'aes-256-gcm' , key . slice ( 0 , 32 ) , buf . slice ( 0 , 12 ) ) ;
decipher . setAuthTag ( buf . slice ( 12 , 28 ) ) ;
return JSON . parse ( decipher . update ( buf . slice ( 28 ) , 'binary' , 'utf8' ) + decipher . final ( 'utf8' ) ) ;
} catch ( ex ) { return { } ; }
}
2017-12-14 01:52:57 +03:00
// Generate a cryptographic key used to encode and decode cookies
obj . generateCookieKey = function ( ) {
2019-01-03 05:03:34 +03:00
return Buffer . from ( obj . crypto . randomBytes ( 80 ) , 'binary' ) ;
2018-12-29 22:38:05 +03:00
//return Buffer.alloc(80, 0); // Sets the key to zeros, debug only.
2018-08-30 22:05:23 +03:00
} ;
2017-12-14 01:52:57 +03:00
2018-12-29 08:55:23 +03:00
// Encode an object as a cookie using a key using AES-GCM. (key must be 32 bytes or more)
2017-12-14 01:52:57 +03: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-03 05:03:34 +03: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 08:55:23 +03:00
const crypted = Buffer . concat ( [ cipher . update ( JSON . stringify ( o ) , 'utf8' ) , cipher . final ( ) ] ) ;
2022-04-14 23:07:05 +03:00
const r = Buffer . concat ( [ iv , cipher . getAuthTag ( ) , crypted ] ) . toString ( obj . args . cookieencoding ? obj . args . cookieencoding : 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ;
2019-08-23 21:51:48 +03:00
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 22:05:23 +03:00
} ;
2017-12-14 01:52:57 +03:00
2020-12-29 02:21:50 +03:00
// Decode a cookie back into an object using a key using AES256-GCM or AES128-CBC/HMAC-SHA384. Return null if it's not a valid cookie. (key must be 32 bytes or more)
2017-12-14 01:52:57 +03:00
obj . decodeCookie = function ( cookie , key , timeout ) {
2021-02-21 21:35:37 +03:00
if ( cookie == null ) return null ;
2022-04-15 10:36:05 +03:00
var r = obj . decodeCookieAESGCM ( cookie , key , timeout ) ;
2022-08-31 03:53:27 +03:00
if ( r === - 1 ) { r = obj . decodeCookieAESSHA ( cookie , key , timeout ) ; } // If decodeCookieAESGCM() failed to decode, try decodeCookieAESSHA()
2020-06-27 00:43:58 +03:00
if ( ( r == null ) && ( obj . args . cookieencoding == null ) && ( cookie . length != 64 ) && ( ( cookie == cookie . toLowerCase ( ) ) || ( cookie == cookie . toUpperCase ( ) ) ) ) {
2020-02-03 21:45:09 +03:00
obj . debug ( 'cookie' , 'Upper/Lowercase cookie, try "CookieEncoding":"hex" in settings section of config.json.' ) ;
console . log ( 'Upper/Lowercase cookie, try "CookieEncoding":"hex" in settings section of config.json.' ) ;
}
2019-12-12 02:44:10 +03:00
if ( ( r != null ) && ( typeof r . once == 'string' ) && ( r . once . length > 0 ) ) {
// This cookie must only be used once.
if ( timeout == null ) { timeout = 2 ; }
if ( obj . cookieUseOnceTable [ r . once ] == null ) {
const ctimeout = ( ( ( r . expire ) == null || ( typeof r . expire != 'number' ) ) ? ( r . time + ( ( timeout + 3 ) * 60000 ) ) : ( r . time + ( ( r . expire + 3 ) * 60000 ) ) ) ;
// Store the used cookie in RAM
obj . cookieUseOnceTable [ r . once ] = ctimeout ;
// Store the used cookie in the database
// TODO
// Send the used cookie to peer servers
// TODO
// Clean up the used table
if ( ++ obj . cookieUseOnceTableCleanCounter > 20 ) {
const now = Date . now ( ) ;
for ( var i in obj . cookieUseOnceTable ) { if ( obj . cookieUseOnceTable [ i ] < now ) { delete obj . cookieUseOnceTable [ i ] ; } }
obj . cookieUseOnceTableCleanCounter = 0 ;
}
} else { return null ; }
}
2018-12-29 08:55:23 +03:00
return r ;
}
2018-12-29 22:38:05 +03: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 08:55:23 +03:00
obj . decodeCookieAESGCM = function ( cookie , key , timeout ) {
2017-12-14 01:52:57 +03:00
try {
if ( key == null ) { key = obj . serverKey ; }
2020-02-03 21:45:09 +03:00
cookie = Buffer . from ( cookie . replace ( /\@/g , '+' ) . replace ( /\$/g , '/' ) , obj . args . cookieencoding ? obj . args . cookieencoding : 'base64' ) ;
2018-12-29 08:55:23 +03:00
const decipher = obj . crypto . createDecipheriv ( 'aes-256-gcm' , key . slice ( 0 , 32 ) , cookie . slice ( 0 , 12 ) ) ;
2020-05-04 00:12:26 +03:00
decipher . setAuthTag ( cookie . slice ( 12 , 28 ) ) ;
2018-12-29 08:55:23 +03:00
const o = JSON . parse ( decipher . update ( cookie . slice ( 28 ) , 'binary' , 'utf8' ) + decipher . final ( 'utf8' ) ) ;
2019-08-23 01:31:39 +03: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-14 01:52:57 +03: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 23:15:38 +03:00
if ( ( o . expire ) == null || ( typeof o . expire != 'number' ) ) {
// Use a fixed cookie expire time
if ( timeout == null ) { timeout = 2 ; }
2019-08-23 01:31:39 +03: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 23:15:38 +03:00
} else {
// An expire time is included in the cookie (in minutes), use this.
2019-08-23 01:31:39 +03: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 23:15:38 +03:00
}
2019-08-23 21:51:48 +03:00
obj . debug ( 'cookie' , 'Decoded AESGCM cookie: ' + JSON . stringify ( o ) ) ;
2017-12-14 01:52:57 +03:00
return o ;
2022-08-31 03:53:27 +03:00
} catch ( ex ) { obj . debug ( 'cookie' , 'ERR: Bad AESGCM cookie due to exception: ' + ex ) ; return - 1 ; }
2018-08-30 22:05:23 +03:00
} ;
2017-12-14 01:52:57 +03:00
2020-12-29 02:21:50 +03:00
// Decode a cookie back into an object using a key using AES256 / HMAC-SHA384. Return null if it's not a valid cookie. (key must be 80 bytes or more)
2018-12-29 22:38:05 +03:00
// We do this because poor .NET does not support AES256-GCM.
2018-12-29 08:55:23 +03:00
obj . decodeCookieAESSHA = function ( cookie , key , timeout ) {
try {
if ( key == null ) { key = obj . serverKey ; }
2018-12-29 22:38:05 +03:00
if ( key . length < 80 ) { return null ; }
2020-02-03 21:45:09 +03:00
cookie = Buffer . from ( cookie . replace ( /\@/g , '+' ) . replace ( /\$/g , '/' ) , obj . args . cookieencoding ? obj . args . cookieencoding : 'base64' ) ;
2018-12-29 22:38:05 +03:00
const decipher = obj . crypto . createDecipheriv ( 'aes-256-cbc' , key . slice ( 48 , 80 ) , cookie . slice ( 0 , 16 ) ) ;
2018-12-29 08:55:23 +03:00
const rawmsg = decipher . update ( cookie . slice ( 16 ) , 'binary' , 'binary' ) + decipher . final ( 'binary' ) ;
2018-12-29 22:38:05 +03: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-23 01:31:39 +03: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 08:55:23 +03: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 23:15:38 +03:00
if ( ( o . expire ) == null || ( typeof o . expire != 'number' ) ) {
// Use a fixed cookie expire time
if ( timeout == null ) { timeout = 2 ; }
2019-08-23 01:31:39 +03: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 23:15:38 +03:00
} else {
// An expire time is included in the cookie (in minutes), use this.
2019-08-23 01:31:39 +03: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 23:15:38 +03:00
}
2019-08-23 21:51:48 +03:00
obj . debug ( 'cookie' , 'Decoded AESSHA cookie: ' + JSON . stringify ( o ) ) ;
2018-12-29 08:55:23 +03:00
return o ;
2019-08-23 01:31:39 +03:00
} catch ( ex ) { obj . debug ( 'cookie' , 'ERR: Bad AESSHA cookie due to exception: ' + ex ) ; return null ; }
2018-12-29 08:55:23 +03:00
} ;
2017-08-28 19:27:45 +03:00
// Debug
2019-08-23 01:31:39 +03: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 ) ; }
2019-11-16 23:13:04 +03:00
// Send event to log file
if ( obj . config . settings && obj . config . settings . log ) {
2019-11-16 23:27:12 +03:00
if ( typeof obj . args . log == 'string' ) { obj . args . log = obj . args . log . split ( ',' ) ; }
if ( obj . args . log . indexOf ( source ) >= 0 ) {
2019-11-16 23:13:04 +03:00
const d = new Date ( ) ;
2019-11-16 23:27:12 +03:00
if ( obj . xxLogFile == null ) {
try {
2021-04-19 19:57:27 +03:00
obj . xxLogFile = obj . fs . openSync ( obj . getConfigFilePath ( 'log.txt' ) , 'a+' , 0o666 ) ;
2019-11-16 23:27:12 +03:00
obj . fs . writeSync ( obj . xxLogFile , '---- Log start at ' + new Date ( ) . toLocaleString ( ) + ' ----\r\n' ) ;
obj . xxLogDateStr = d . toLocaleDateString ( ) ;
} catch ( ex ) { }
}
2019-11-16 23:13:04 +03:00
if ( obj . xxLogFile != null ) {
try {
if ( obj . xxLogDateStr != d . toLocaleDateString ( ) ) { obj . xxLogDateStr = d . toLocaleDateString ( ) ; obj . fs . writeSync ( obj . xxLogFile , '---- ' + d . toLocaleDateString ( ) + ' ----\r\n' ) ; }
2022-02-03 22:57:32 +03:00
obj . fs . writeSync ( obj . xxLogFile , new Date ( ) . toLocaleTimeString ( ) + ' - ' + source + ': ' + args . join ( ', ' ) + '\r\n' ) ;
2019-11-16 23:13:04 +03:00
} catch ( ex ) { }
}
}
}
2019-08-23 01:31:39 +03:00
// Send the event to logged in administrators
if ( ( obj . debugRemoteSources != null ) && ( ( obj . debugRemoteSources == '*' ) || ( obj . debugRemoteSources . indexOf ( source ) >= 0 ) ) ) {
2019-08-23 21:51:48 +03:00
var sendcount = 0 ;
2019-08-23 01:31:39 +03:00
for ( var sessionid in obj . webserver . wssessions2 ) {
2022-04-14 23:07:05 +03:00
const ws = obj . webserver . wssessions2 [ sessionid ] ;
2019-08-23 01:31:39 +03:00
if ( ( ws != null ) && ( ws . userid != null ) ) {
2022-04-14 23:07:05 +03:00
const user = obj . webserver . users [ ws . userid ] ;
2019-08-23 01:31:39 +03:00
if ( ( user != null ) && ( user . siteadmin == 4294967295 ) ) {
2019-08-23 21:51:48 +03:00
try { ws . send ( JSON . stringify ( { action : 'trace' , source : source , args : args , time : Date . now ( ) } ) ) ; sendcount ++ ; } catch ( ex ) { }
2019-08-23 01:31:39 +03:00
}
}
}
2019-08-23 21:51:48 +03:00
if ( sendcount == 0 ) { obj . debugRemoteSources = null ; } // If there are no listeners, remove debug sources.
2019-08-23 01:31:39 +03:00
}
2018-08-30 22:05:23 +03:00
} ;
2018-03-14 22:10:13 +03:00
// Update server state. Writes a server state file.
2022-04-14 23:07:05 +03:00
const meshServerState = { } ;
2018-08-30 22:05:23 +03:00
obj . updateServerState = function ( name , val ) {
2019-01-04 01:46:52 +03:00
//console.log('updateServerState', name, val);
2018-09-20 21:45:12 +03: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-27 00:58:55 +03: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 22:05:23 +03:00
} ;
2022-01-04 04:12:02 +03:00
// Read a list of IP addresses from a file
function readIpListFromFile ( arg ) {
if ( ( typeof arg != 'string' ) || ( ! arg . startsWith ( 'file:' ) ) ) return arg ;
var lines = null ;
2024-06-21 00:34:08 +03:00
try { lines = obj . fs . readFileSync ( obj . path . join ( obj . datapath , arg . substring ( 5 ) ) ) . toString ( ) . split ( /\r?\n/ ) . join ( '\r' ) . split ( '\r' ) ; } catch ( ex ) { }
2022-01-04 04:12:02 +03:00
if ( lines == null ) return null ;
2022-04-14 23:07:05 +03:00
const validLines = [ ] ;
2022-01-04 04:12:02 +03:00
for ( var i in lines ) { if ( ( lines [ i ] . length > 0 ) && ( ( ( lines [ i ] . charAt ( 0 ) > '0' ) && ( lines [ i ] . charAt ( 0 ) < '9' ) ) || ( lines [ i ] . charAt ( 0 ) == ':' ) ) ) validLines . push ( lines [ i ] ) ; }
return validLines ;
}
2017-08-28 19:27:45 +03: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 ) ; }
2019-12-11 05:17:25 +03:00
obj . getServerWarnings = function ( ) { return serverWarnings ; }
2021-09-02 06:06:14 +03:00
obj . addServerWarning = function ( msg , id , args , print ) { serverWarnings . push ( { msg : msg , id : id , args : args } ) ; if ( print !== false ) { console . log ( "WARNING: " + msg ) ; } }
2017-08-28 19:27:45 +03:00
2020-02-17 21:24:32 +03:00
// auth.log functions
2022-09-02 08:06:08 +03:00
obj . authLog = function ( server , msg , args ) {
2020-04-20 08:37:26 +03:00
if ( typeof msg != 'string' ) return ;
2022-09-02 08:06:08 +03:00
var str = msg ;
if ( args != null ) {
if ( typeof args . sessionid == 'string' ) { str += ', SessionID: ' + args . sessionid ; }
if ( typeof args . useragent == 'string' ) { const userAgentInfo = obj . webserver . getUserAgentInfo ( args . useragent ) ; str += ', Browser: ' + userAgentInfo . browserStr + ', OS: ' + userAgentInfo . osStr ; }
}
obj . debug ( 'authlog' , str ) ;
if ( obj . syslogauth != null ) { try { obj . syslogauth . log ( obj . syslogauth . LOG _INFO , str ) ; } catch ( ex ) { } }
2020-04-20 21:43:21 +03:00
if ( obj . authlogfile != null ) { // Write authlog to file
2020-04-20 08:37:26 +03:00
try {
2022-04-14 23:07:05 +03:00
const d = new Date ( ) , month = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ] [ d . getMonth ( ) ] ;
2024-03-04 03:03:27 +03:00
str = month + ' ' + d . getDate ( ) + ' ' + obj . common . zeroPad ( d . getHours ( ) , 2 ) + ':' + obj . common . zeroPad ( d . getMinutes ( ) , 2 ) + ':' + d . getSeconds ( ) + ' meshcentral ' + server + '[' + process . pid + ']: ' + msg + ( ( obj . platform == 'win32' ) ? '\r\n' : '\n' ) ;
2024-03-10 10:46:01 +03:00
obj . fs . write ( obj . authlogfile , str , function ( err , written , string ) { if ( err ) { console . error ( err ) ; } } ) ;
2024-03-04 03:03:27 +03:00
} catch ( ex ) { console . error ( ex ) ; }
2020-04-20 08:37:26 +03:00
}
2020-02-17 21:24:32 +03:00
}
2018-07-14 05:18:43 +03: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 22:05:23 +03:00
} ;
2018-07-14 05:18:43 +03:00
2017-08-28 19:27:45 +03:00
return obj ;
}
2019-12-09 07:46:25 +03:00
// Resolve a list of names, call back with list of failed resolves.
function checkResolveAll ( names , func ) {
2022-04-14 23:07:05 +03:00
const dns = require ( 'dns' ) , state = { func : func , count : names . length , err : null } ;
2019-12-09 07:46:25 +03:00
for ( var i in names ) {
2024-04-07 01:47:02 +03:00
dns . lookup ( names [ i ] , { all : true } , function ( err , records ) {
2019-12-09 07:46:25 +03:00
if ( err != null ) { if ( this . state . err == null ) { this . state . err = [ this . name ] ; } else { this . state . err . push ( this . name ) ; } }
if ( -- this . state . count == 0 ) { this . state . func ( this . state . err ) ; }
} . bind ( { name : names [ i ] , state : state } ) )
}
}
2018-02-01 03:10:15 +03:00
// Return the server configuration
2019-02-03 01:54:36 +03:00
function getConfig ( createSampleConfig ) {
2018-02-01 03:10:15 +03:00
// Figure out the datapath location
2022-04-14 23:07:05 +03:00
var i , datapath = null ;
const fs = require ( 'fs' ) , path = require ( 'path' ) , args = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
2018-02-01 03:10:15 +03:00
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 ; }
2022-02-04 06:11:35 +03:00
try { fs . mkdirSync ( datapath ) ; } catch ( ex ) { }
2018-02-01 03:10:15 +03:00
// Read configuration file if present and change arguments.
var config = { } , configFilePath = path . join ( datapath , 'config.json' ) ;
2023-10-04 06:35:02 +03:00
if ( args . configfile ) { configFilePath = common . joinPath ( datapath , args . configfile ) ; }
2018-02-01 03:10:15 +03:00
if ( fs . existsSync ( configFilePath ) ) {
// Load and validate the configuration file
2022-02-04 06:11:35 +03:00
try { config = require ( configFilePath ) ; } catch ( ex ) { console . log ( 'ERROR: Unable to parse ' + configFilePath + '.' ) ; return null ; }
2018-02-01 03:10:15 +03:00
if ( config . domains == null ) { config . domains = { } ; }
2018-08-30 22:05:23 +03: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-02-01 03:10:15 +03:00
} else {
2019-02-03 01:54:36 +03:00
if ( createSampleConfig === true ) {
// Copy the "sample-config.json" to give users a starting point
2022-04-14 23:07:05 +03:00
const sampleConfigPath = path . join ( _ _dirname , 'sample-config.json' ) ;
2019-02-03 01:54:36 +03:00
if ( fs . existsSync ( sampleConfigPath ) ) { fs . createReadStream ( sampleConfigPath ) . pipe ( fs . createWriteStream ( configFilePath ) ) ; }
}
2018-02-01 03:10:15 +03:00
}
// Set the command line arguments to the config file if they are not present
if ( ! config . settings ) { config . settings = { } ; }
2018-08-30 22:05:23 +03:00
for ( i in args ) { config . settings [ i ] = args [ i ] ; }
2018-02-01 03:10:15 +03:00
// Lower case all keys in the config file
2019-01-05 01:35:01 +03:00
try {
2022-11-04 21:50:42 +03:00
require ( './common.js' ) . objKeysToLower ( config , [ 'ldapoptions' , 'defaultuserwebstate' , 'forceduserwebstate' , 'httpheaders' , 'telegram/proxy' ] ) ;
2019-01-05 01:35:01 +03:00
} catch ( ex ) {
console . log ( 'CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.' ) ;
process . exit ( ) ;
}
2019-04-11 23:41:51 +03:00
2018-02-01 03:10:15 +03:00
return config ;
}
// Check if a list of modules are present and install any missing ones
2023-10-21 09:36:02 +03:00
function InstallModules ( modules , args , func ) {
var missingModules = [ ] ;
2019-03-23 23:28:17 +03:00
if ( modules . length > 0 ) {
2022-04-14 23:07:05 +03:00
const dependencies = require ( './package.json' ) . dependencies ;
2019-03-27 00:11:51 +03:00
for ( var i in modules ) {
2019-11-06 21:41:11 +03:00
// Modules may contain a version tag (foobar@1.0.0), remove it so the module can be found using require
2022-04-14 23:07:05 +03:00
const moduleNameAndVersion = modules [ i ] ;
const moduleInfo = moduleNameAndVersion . split ( '@' , 2 ) ;
2020-01-25 18:58:44 +03:00
var moduleName = moduleInfo [ 0 ] ;
var moduleVersion = moduleInfo [ 1 ] ;
2022-04-14 23:07:05 +03:00
if ( moduleName == '' ) { moduleName = moduleNameAndVersion ; moduleVersion = null ; } // If the module name starts with @, don't use @ as a version seperator.
2019-03-27 00:11:51 +03:00
try {
2020-01-25 18:58:44 +03:00
// Does the module need a specific version?
if ( moduleVersion ) {
2020-01-26 01:17:21 +03:00
if ( require ( ` ${ moduleName } /package.json ` ) . version != moduleVersion ) { throw new Error ( ) ; }
} else {
2019-11-14 09:47:17 +03:00
// For all other modules, do the check here.
2020-01-25 18:58:44 +03:00
// Is the module in package.json? Install exact version.
2022-04-14 23:07:05 +03:00
if ( typeof dependencies [ moduleName ] != null ) { moduleVersion = dependencies [ moduleName ] ; }
2019-11-14 09:47:17 +03:00
require ( moduleName ) ;
}
2022-02-04 06:11:35 +03:00
} catch ( ex ) {
2023-10-21 09:36:02 +03:00
missingModules . push ( moduleNameAndVersion ) ;
2019-03-27 00:11:51 +03:00
}
}
2024-03-10 10:46:01 +03:00
2024-01-13 21:52:59 +03:00
if ( missingModules . length > 0 ) { if ( args . debug ) { console . log ( 'Missing Modules: ' + missingModules . join ( ', ' ) ) ; } InstallModuleEx ( missingModules , args , func ) ; } else { func ( ) ; }
2019-03-23 23:28:17 +03:00
}
2017-08-28 19:27:45 +03:00
}
2023-10-21 09:36:02 +03:00
// Install all missing modules at once. We will be running "npm install" once, with a full list of all modules we need, no matter if they area already installed or not,
// this is to make sure NPM gives us exactly what we need. Also, we install the meshcentral with current version, so that NPM does not update it - which it will do if obmitted.
function InstallModuleEx ( modulenames , args , func ) {
var names = modulenames . join ( ' ' ) ;
2024-01-13 21:52:59 +03:00
console . log ( 'Installing modules' , modulenames ) ;
2022-03-17 21:19:18 +03:00
const child _process = require ( 'child_process' ) ;
2019-04-05 19:21:21 +03:00
var parentpath = _ _dirname ;
2023-10-21 09:36:02 +03:00
function getCurrentVersion ( ) { try { return JSON . parse ( require ( 'fs' ) . readFileSync ( require ( 'path' ) . join ( _ _dirname , 'package.json' ) , 'utf8' ) ) . version ; } catch ( ex ) { } return null ; } // Fetch server version
2024-01-13 21:52:59 +03:00
//const meshCentralVersion = getCurrentVersion();
//if ((meshCentralVersion != null) && (args.dev == null)) { names = 'meshcentral@' + getCurrentVersion() + ' ' + names; }
2019-04-05 19:21:21 +03:00
// 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 21:32:16 +03:00
2024-01-13 21:52:59 +03:00
if ( args . debug ) { console . log ( 'NPM Command Line: ' + npmpath + ` install --save-exact --no-audit --omit=optional --no-fund ${ names } ` ) ; }
// always use --save-exact - https://stackoverflow.com/a/64507176/1210734
child _process . exec ( npmpath + ` install --save-exact --no-audit --no-optional --omit=optional ${ names } ` , { maxBuffer : 512000 , timeout : 300000 , cwd : parentpath } , function ( error , stdout , stderr ) {
2019-05-17 22:40:15 +03:00
if ( ( error != null ) && ( error != '' ) ) {
2020-11-28 23:51:23 +03:00
var mcpath = _ _dirname ;
if ( mcpath . endsWith ( '\\node_modules\\meshcentral' ) || mcpath . endsWith ( '/node_modules/meshcentral' ) ) { mcpath = require ( 'path' ) . join ( mcpath , '..' , '..' ) ; }
2024-01-13 21:52:59 +03:00
console . log ( 'ERROR: Unable to install required modules. MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. To manualy install this module try:\r\n\r\n cd "' + mcpath + '"\r\n npm install --no-audit --no-optional --omit=optional ' + names + '\r\n node node_modules' + ( ( require ( 'os' ) . platform ( ) == 'win32' ) ? '\\' : '/' ) + 'meshcentral' ) ;
2019-03-27 22:55:31 +03:00
process . exit ( ) ;
return ;
}
2023-10-21 09:36:02 +03:00
func ( ) ;
2017-08-28 19:27:45 +03:00
return ;
2019-03-25 21:32:16 +03:00
} ) ;
2017-08-28 19:27:45 +03:00
}
// Detect CTRL-C on Linux and stop nicely
2017-09-04 22:20:18 +03:00
process . on ( 'SIGINT' , function ( ) { if ( meshserver != null ) { meshserver . Stop ( ) ; meshserver = null ; } console . log ( 'Server Ctrl-C exit...' ) ; process . exit ( ) ; } ) ;
2017-08-28 19:27:45 +03:00
2019-11-27 01:11:09 +03:00
// Add a server warning, warnings will be shown to the administrator on the web application
2022-04-14 23:07:05 +03:00
const serverWarnings = [ ] ;
2021-09-02 06:06:14 +03:00
function addServerWarning ( msg , id , args , print ) { serverWarnings . push ( { msg : msg , id : id , args : args } ) ; if ( print !== false ) { console . log ( "WARNING: " + msg ) ; } }
/ *
var ServerWarnings = {
2023-10-21 11:00:47 +03:00
1 : "" ,
2021-09-02 06:06:14 +03:00
2 : "Missing WebDAV parameters." ,
3 : "Unrecognized configuration option \"{0}\"." ,
4 : "WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15 and v13.2" ,
5 : "Unable to load Intel AMT TLS root certificate for default domain." ,
6 : "Unable to load Intel AMT TLS root certificate for domain {0}." ,
7 : "CIRA local FQDN's ignored when server in LAN-only or WAN-only mode." ,
8 : "Can't have more than 4 CIRA local FQDN's. Ignoring value." ,
9 : "Agent hash checking is being skipped, this is unsafe." ,
10 : "Missing Let's Encrypt email address." ,
11 : "Invalid Let's Encrypt host names." ,
12 : "Invalid Let's Encrypt names, can't contain a *." ,
13 : "Unable to setup Let's Encrypt module." ,
14 : "Invalid Let's Encrypt names, unable to resolve: {0}" ,
15 : "Invalid Let's Encrypt email address, unable to resolve: {0}" ,
16 : "Unable to load CloudFlare trusted proxy IPv6 address list." ,
17 : "SendGrid server has limited use in LAN mode." ,
18 : "SMTP server has limited use in LAN mode." ,
19 : "SMS gateway has limited use in LAN mode." ,
2022-09-07 00:40:52 +03:00
20 : "Invalid \"LoginCookieEncryptionKey\" in config.json." ,
21 : "Backup path can't be set within meshcentral-data folder, backup settings ignored." ,
22 : "Failed to sign agent {0}: {1}" ,
23 : "Unable to load agent icon file: {0}." ,
24 : "Unable to load agent logo file: {0}." ,
2022-10-26 23:19:40 +03:00
25 : "This NodeJS version does not support OpenID." ,
26 : "This NodeJS version does not support Discord.js."
2021-09-02 06:06:14 +03:00
} ;
* /
2019-11-27 01:11:09 +03:00
2018-02-01 03:10:15 +03:00
// Load the really basic modules
2020-03-15 21:35:33 +03:00
var npmpath = 'npm' ;
2017-08-28 19:27:45 +03:00
var meshserver = null ;
2019-07-18 01:57:42 +03:00
var childProcess = null ;
2019-11-27 01:11:09 +03:00
var previouslyInstalledModules = { } ;
2019-05-18 01:44:01 +03:00
function mainStart ( ) {
2024-05-12 17:37:47 +03:00
// Check the NodeJS is version 16 or better.
if ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) < 16 ) { console . log ( "MeshCentral requires Node v16 or above, current version is " + process . version + "." ) ; return ; }
2018-09-01 01:23:42 +03:00
2020-01-30 02:29:15 +03:00
// If running within the node_modules folder, move working directory to the parent of the node_modules folder.
if ( _ _dirname . endsWith ( '\\node_modules\\meshcentral' ) || _ _dirname . endsWith ( '/node_modules/meshcentral' ) ) { process . chdir ( require ( 'path' ) . join ( _ _dirname , '..' , '..' ) ) ; }
2020-01-29 22:01:33 +03:00
2018-09-01 01:23:42 +03:00
// Check for any missing modules.
2023-10-21 09:36:02 +03:00
InstallModules ( [ 'minimist' ] , { } , function ( ) {
2019-05-18 01:44:01 +03:00
// Parse inbound arguments
2022-04-14 23:07:05 +03:00
const args = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
2019-05-18 01:44:01 +03:00
2020-03-15 21:35:33 +03:00
// Setup the NPM path
if ( args . npmpath == null ) {
try {
var xnodepath = process . argv [ 0 ] ;
var xnpmpath = require ( 'path' ) . join ( require ( 'path' ) . dirname ( process . argv [ 0 ] ) , 'npm' ) ;
if ( require ( 'fs' ) . existsSync ( xnodepath ) && require ( 'fs' ) . existsSync ( xnpmpath ) ) {
if ( xnodepath . indexOf ( ' ' ) >= 0 ) { xnodepath = '"' + xnodepath + '"' ; }
if ( xnpmpath . indexOf ( ' ' ) >= 0 ) { xnpmpath = '"' + xnpmpath + '"' ; }
if ( require ( 'os' ) . platform ( ) == 'win32' ) { npmpath = xnpmpath ; } else { npmpath = ( xnodepath + ' ' + xnpmpath ) ; }
}
} catch ( ex ) { console . log ( ex ) ; }
} else {
npmpath = args . npmpath ;
}
2018-03-15 01:16:55 +03:00
// Get the server configuration
2019-02-03 01:54:36 +03:00
var config = getConfig ( false ) ;
2018-03-15 01:16:55 +03:00
if ( config == null ) { process . exit ( ) ; }
2019-05-27 21:58:31 +03:00
// Lowercase the auth value if present
2019-04-11 23:41:51 +03:00
for ( var i in config . domains ) { if ( typeof config . domains [ i ] . auth == 'string' ) { config . domains [ i ] . auth = config . domains [ i ] . auth . toLowerCase ( ) ; } }
2020-08-16 23:44:54 +03:00
// Get the current node version
2021-06-08 22:14:20 +03:00
const verSplit = process . version . substring ( 1 ) . split ( '.' ) ;
var nodeVersion = parseInt ( verSplit [ 0 ] ) + ( parseInt ( verSplit [ 1 ] ) / 100 ) ;
2020-08-16 23:44:54 +03:00
2022-04-29 21:13:58 +03:00
// Check if RDP support if present
var mstsc = true ;
try { require ( './rdp' ) } catch ( ex ) { mstsc = false ; }
2020-05-14 06:49:58 +03:00
// Check if Windows SSPI, LDAP, Passport and YubiKey OTP will be used
2018-05-30 02:57:08 +03:00
var sspi = false ;
2019-04-11 23:41:51 +03:00
var ldap = false ;
2020-05-14 06:49:58 +03:00
var passport = null ;
2019-03-23 23:28:17 +03:00
var allsspi = true ;
2019-03-25 21:32:16 +03:00
var yubikey = false ;
2021-04-29 09:22:54 +03:00
var ssh = false ;
2021-02-22 23:08:58 +03:00
var sessionRecording = false ;
2019-04-23 03:00:46 +03:00
var domainCount = 0 ;
2020-08-16 23:44:54 +03:00
var wildleek = false ;
2021-02-10 22:28:21 +03:00
var nodemailer = false ;
var sendgrid = false ;
2022-07-12 00:35:05 +03:00
var captcha = false ;
2019-04-23 03:00:46 +03:00
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 23:41:51 +03:00
for ( var i in config . domains ) {
2020-07-10 01:18:33 +03:00
if ( i . startsWith ( '_' ) ) continue ;
2022-09-03 01:27:07 +03:00
if ( ( ( config . domains [ i ] . smtp != null ) && ( config . domains [ i ] . smtp . name != 'console' ) ) || ( config . domains [ i ] . sendmail != null ) ) { nodemailer = true ; }
2021-02-10 22:28:21 +03:00
if ( config . domains [ i ] . sendgrid != null ) { sendgrid = true ; }
2019-04-11 23:41:51 +03:00
if ( config . domains [ i ] . yubikey != null ) { yubikey = true ; }
if ( config . domains [ i ] . auth == 'ldap' ) { ldap = true ; }
2022-04-29 21:13:58 +03:00
if ( mstsc == false ) { config . domains [ i ] . mstsc = false ; }
2023-10-21 11:00:47 +03:00
if ( config . domains [ i ] . ssh == true ) { ssh = true ; }
2020-05-14 06:49:58 +03:00
if ( ( typeof config . domains [ i ] . authstrategies == 'object' ) ) {
2024-06-17 12:10:35 +03:00
if ( passport == null ) { passport = [ 'passport' , 'connect-flash' ] ; } // Passport v0.6.0 requires a patch, see https://github.com/jaredhanson/passport/issues/904 and include connect-flash here to display errors
2020-07-10 02:40:31 +03:00
if ( ( typeof config . domains [ i ] . authstrategies . twitter == 'object' ) && ( typeof config . domains [ i ] . authstrategies . twitter . clientid == 'string' ) && ( typeof config . domains [ i ] . authstrategies . twitter . clientsecret == 'string' ) && ( passport . indexOf ( 'passport-twitter' ) == - 1 ) ) { passport . push ( 'passport-twitter' ) ; }
2020-05-14 06:49:58 +03:00
if ( ( typeof config . domains [ i ] . authstrategies . google == 'object' ) && ( typeof config . domains [ i ] . authstrategies . google . clientid == 'string' ) && ( typeof config . domains [ i ] . authstrategies . google . clientsecret == 'string' ) && ( passport . indexOf ( 'passport-google-oauth20' ) == - 1 ) ) { passport . push ( 'passport-google-oauth20' ) ; }
if ( ( typeof config . domains [ i ] . authstrategies . github == 'object' ) && ( typeof config . domains [ i ] . authstrategies . github . clientid == 'string' ) && ( typeof config . domains [ i ] . authstrategies . github . clientsecret == 'string' ) && ( passport . indexOf ( 'passport-github2' ) == - 1 ) ) { passport . push ( 'passport-github2' ) ; }
2020-05-24 10:04:49 +03:00
if ( ( typeof config . domains [ i ] . authstrategies . azure == 'object' ) && ( typeof config . domains [ i ] . authstrategies . azure . clientid == 'string' ) && ( typeof config . domains [ i ] . authstrategies . azure . clientsecret == 'string' ) && ( typeof config . domains [ i ] . authstrategies . azure . tenantid == 'string' ) && ( passport . indexOf ( 'passport-azure-oauth2' ) == - 1 ) ) { passport . push ( 'passport-azure-oauth2' ) ; passport . push ( 'jwt-simple' ) ; }
2024-03-04 03:03:27 +03:00
if ( ( typeof config . domains [ i ] . authstrategies . oidc == 'object' ) && ( passport . indexOf ( 'openid-client' ) == - 1 ) ) {
2024-03-10 10:46:01 +03:00
if ( ( nodeVersion >= 17 )
|| ( ( Math . floor ( nodeVersion ) == 16 ) && ( nodeVersion >= 16.13 ) )
|| ( ( Math . floor ( nodeVersion ) == 14 ) && ( nodeVersion >= 14.15 ) )
|| ( ( Math . floor ( nodeVersion ) == 12 ) && ( nodeVersion >= 12.19 ) ) ) {
passport . push ( 'openid-client' ) ;
2024-03-04 03:03:27 +03:00
} else {
addServerWarning ( 'This NodeJS version does not support OpenID Connect on MeshCentral.' , 25 ) ;
delete config . domains [ i ] . authstrategies . oidc ;
}
2022-09-07 00:40:52 +03:00
}
2020-05-20 05:02:27 +03:00
if ( ( typeof config . domains [ i ] . authstrategies . saml == 'object' ) || ( typeof config . domains [ i ] . authstrategies . jumpcloud == 'object' ) ) { passport . push ( 'passport-saml' ) ; }
2020-05-14 06:49:58 +03:00
}
2021-02-22 23:08:58 +03:00
if ( config . domains [ i ] . sessionrecording != null ) { sessionRecording = true ; }
2021-05-28 01:25:54 +03:00
if ( ( config . domains [ i ] . passwordrequirements != null ) && ( config . domains [ i ] . passwordrequirements . bancommonpasswords == true ) ) { wildleek = true ; }
2022-07-12 00:35:05 +03:00
if ( ( config . domains [ i ] . newaccountscaptcha != null ) && ( config . domains [ i ] . newaccountscaptcha !== false ) ) { captcha = true ; }
2019-04-11 23:41:51 +03:00
}
2018-05-30 02:57:08 +03:00
2018-03-15 01:16:55 +03:00
// Build the list of required modules
2024-01-12 04:49:12 +03:00
// NOTE: ALL MODULES MUST HAVE A VERSION NUMBER AND THE VERSION MUST MATCH THAT USED IN Dockerfile
2024-05-24 01:47:44 +03:00
var modules = [ 'archiver@7.0.0' , 'body-parser@1.20.2' , 'cbor@5.2.0' , 'compression@1.7.4' , 'cookie-session@2.0.0' , 'express@4.19.2' , 'express-handlebars@5.3.5' , 'express-ws@4.0.0' , 'ipcheck@0.1.0' , 'minimist@1.2.8' , 'multiparty@4.2.3' , '@yetzt/nedb' , 'node-forge@1.3.1' , 'ua-parser-js@1.0.37' , 'ws@8.14.2' , 'yauzl@2.10.0' ] ;
2023-10-21 09:36:02 +03:00
if ( require ( 'os' ) . platform ( ) == 'win32' ) { modules . push ( 'node-windows@0.1.14' ) ; modules . push ( 'loadavg-windows@1.1.1' ) ; if ( sspi == true ) { modules . push ( 'node-sspi@0.2.10' ) ; } } // Add Windows modules
if ( ldap == true ) { modules . push ( 'ldapauth-fork@5.0.5' ) ; }
2024-01-12 04:49:12 +03:00
if ( ssh == true ) { modules . push ( 'ssh2@1.15.0' ) ; }
2020-05-14 06:49:58 +03:00
if ( passport != null ) { modules . push ( ... passport ) ; }
2023-10-21 09:36:02 +03:00
if ( captcha == true ) { modules . push ( 'svg-captcha@1.4.0' ) ; }
2022-07-12 00:35:05 +03:00
2023-10-21 09:36:02 +03:00
if ( sessionRecording == true ) { modules . push ( 'image-size@1.0.2' ) ; } // Need to get the remote desktop JPEG sizes to index the recodring file.
2022-07-22 04:38:36 +03:00
if ( config . letsencrypt != null ) { modules . push ( 'acme-client@4.2.5' ) ; } // Add acme-client module. We need to force v4.2.4 or higher since olver versions using SHA-1 which is no longer supported by Let's Encrypt.
2021-04-06 19:22:26 +03:00
if ( config . settings . mqtt != null ) { modules . push ( 'aedes@0.39.0' ) ; } // Add MQTT Modules
2023-10-21 09:36:02 +03:00
if ( config . settings . mysql != null ) { modules . push ( 'mysql2@3.6.2' ) ; } // Add MySQL.
//if (config.settings.mysql != null) { modules.push('@mysql/xdevapi@8.0.33'); } // Add MySQL, official driver (https://dev.mysql.com/doc/dev/connector-nodejs/8.0/)
if ( config . settings . mongodb != null ) { modules . push ( 'mongodb@4.13.0' ) ; modules . push ( 'saslprep@1.0.3' ) ; } // Add MongoDB, official driver.
2021-11-03 05:44:41 +03:00
if ( config . settings . postgres != null ) { modules . push ( 'pg@8.7.1' ) ; modules . push ( 'pgtools@0.3.2' ) ; } // Add Postgres, Postgres driver.
2023-10-21 09:36:02 +03:00
if ( config . settings . mariadb != null ) { modules . push ( 'mariadb@3.2.2' ) ; } // Add MariaDB, official driver.
if ( config . settings . acebase != null ) { modules . push ( 'acebase@1.29.5' ) ; } // Add AceBase, official driver.
if ( config . settings . sqlite3 != null ) { modules . push ( 'sqlite3@5.1.6' ) ; } // Add sqlite3, official driver.
if ( config . settings . vault != null ) { modules . push ( 'node-vault@0.10.2' ) ; } // Add official HashiCorp's Vault module.
if ( config . settings . plugins != null ) { modules . push ( 'semver@7.5.4' ) ; } // Required for version compat testing and update checks
if ( ( config . settings . plugins != null ) && ( config . settings . plugins . proxy != null ) ) { modules . push ( 'https-proxy-agent@7.0.2' ) ; } // Required for HTTP/HTTPS proxy support
else if ( config . settings . xmongodb != null ) { modules . push ( 'mongojs@3.1.0' ) ; } // Add MongoJS, old driver.
2024-01-12 04:49:12 +03:00
if ( nodemailer || ( ( config . smtp != null ) && ( config . smtp . name != 'console' ) ) || ( config . sendmail != null ) ) { modules . push ( 'nodemailer@6.9.8' ) ; } // Add SMTP support
2021-02-10 22:28:21 +03:00
if ( sendgrid || ( config . sendgrid != null ) ) { modules . push ( '@sendgrid/mail' ) ; } // Add SendGrid support
2023-10-21 11:00:47 +03:00
if ( ( args . translate || args . dev ) && ( Number ( process . version . match ( /^v(\d+\.\d+)/ ) [ 1 ] ) >= 16 ) ) { modules . push ( 'jsdom@22.1.0' ) ; modules . push ( 'esprima@4.0.1' ) ; modules . push ( 'minify-js@0.0.4' ) ; modules . push ( 'html-minifier@4.0.0' ) ; } // Translation support
2023-10-21 09:36:02 +03:00
if ( typeof config . settings . crowdsec == 'object' ) { modules . push ( '@crowdsec/express-bouncer@0.1.0' ) ; } // Add CrowdSec bounser module (https://www.npmjs.com/package/@crowdsec/express-bouncer)
2018-03-15 01:16:55 +03:00
2023-09-11 19:50:15 +03:00
if ( typeof config . settings . autobackup == 'object' ) {
// Setup encrypted zip support if needed
2023-10-21 09:36:02 +03:00
if ( config . settings . autobackup . zippassword ) { modules . push ( 'archiver-zip-encrypted@1.0.11' ) ; }
2023-09-11 19:50:15 +03:00
// Enable Google Drive Support
2023-10-21 09:36:02 +03:00
if ( typeof config . settings . autobackup . googledrive == 'object' ) { modules . push ( 'googleapis@128.0.0' ) ; }
2023-09-11 19:50:15 +03:00
// Enable WebDAV Support
if ( typeof config . settings . autobackup . webdav == 'object' ) {
if ( ( typeof config . settings . autobackup . webdav . url != 'string' ) || ( typeof config . settings . autobackup . webdav . username != 'string' ) || ( typeof config . settings . autobackup . webdav . password != 'string' ) ) { addServerWarning ( "Missing WebDAV parameters." , 2 , null , ! args . launch ) ; } else { modules . push ( 'webdav@4.11.3' ) ; }
}
2020-08-21 11:57:03 +03:00
}
2019-05-17 22:40:15 +03:00
2020-08-16 23:44:54 +03:00
// Setup common password blocking
if ( wildleek == true ) { modules . push ( 'wildleek@2.0.0' ) ; }
2019-03-27 00:11:51 +03:00
// Setup 2nd factor authentication
if ( config . settings . no2factorauth !== true ) {
// Setup YubiKey OTP if configured
2023-10-21 09:36:02 +03:00
if ( yubikey == true ) { modules . push ( 'yubikeyotp@0.2.0' ) ; } // Add YubiKey OTP support
2020-03-11 11:27:16 +03:00
if ( allsspi == false ) { modules . push ( 'otplib@10.2.3' ) ; } // Google Authenticator support (v10 supports older NodeJS versions).
2019-03-27 00:11:51 +03:00
}
2019-11-14 09:47:17 +03:00
2020-04-24 09:19:28 +03:00
// Desktop multiplexor support
2023-10-21 09:36:02 +03:00
if ( config . settings . desktopmultiplex === true ) { modules . push ( 'image-size@1.0.2' ) ; }
2020-04-24 09:19:28 +03:00
2020-04-22 05:33:53 +03:00
// SMS support
2022-10-22 17:23:55 +03:00
if ( config . sms != null ) {
2023-10-21 09:36:02 +03:00
if ( config . sms . provider == 'twilio' ) { modules . push ( 'twilio@4.19.0' ) ; }
if ( config . sms . provider == 'plivo' ) { modules . push ( 'plivo@4.58.0' ) ; }
if ( config . sms . provider == 'telnyx' ) { modules . push ( 'telnyx@1.25.5' ) ; }
2022-10-22 17:23:55 +03:00
}
// Messaging support
if ( config . messaging != null ) {
2023-10-21 09:36:02 +03:00
if ( config . messaging . telegram != null ) { modules . push ( 'telegram@2.19.8' ) ; modules . push ( 'input@1.0.1' ) ; }
2022-10-27 21:23:35 +03:00
if ( config . messaging . discord != null ) { if ( nodeVersion >= 17 ) { modules . push ( 'discord.js@14.6.0' ) ; } else { delete config . messaging . discord ; addServerWarning ( 'This NodeJS version does not support Discord.js.' , 26 ) ; } }
2023-10-21 09:36:02 +03:00
if ( config . messaging . xmpp != null ) { modules . push ( '@xmpp/client@0.13.1' ) ; }
if ( config . messaging . pushover != null ) { modules . push ( 'node-pushover@1.0.0' ) ; }
if ( config . messaging . zulip != null ) { modules . push ( 'zulip@0.1.0' ) ; }
2022-10-22 17:23:55 +03:00
}
2020-04-22 05:33:53 +03:00
2021-02-09 06:31:29 +03:00
// Setup web based push notifications
2023-10-21 09:36:02 +03:00
if ( ( typeof config . settings . webpush == 'object' ) && ( typeof config . settings . webpush . email == 'string' ) ) { modules . push ( 'web-push@3.6.6' ) ; }
2021-02-09 06:31:29 +03:00
2021-01-31 03:15:06 +03:00
// Firebase Support
2022-11-22 00:29:41 +03:00
// Avoid 0.1.8 due to bugs: https://github.com/guness/node-xcs/issues/43
if ( config . firebase != null ) { modules . push ( 'node-xcs@0.1.7' ) ; }
2021-01-31 03:15:06 +03:00
2020-04-07 22:02:29 +03:00
// Syslog support
2023-10-21 09:36:02 +03:00
if ( ( require ( 'os' ) . platform ( ) != 'win32' ) && ( config . settings . syslog || config . settings . syslogjson ) ) { modules . push ( 'modern-syslog@1.2.0' ) ; }
if ( config . settings . syslogtcp ) { modules . push ( 'syslog@0.1.1-1' ) ; }
2020-04-07 22:02:29 +03:00
2020-03-29 03:28:44 +03:00
// Setup heapdump support if needed, useful for memory leak debugging
// https://www.arbazsiddiqui.me/a-practical-guide-to-memory-leaks-in-nodejs/
2023-10-21 09:36:02 +03:00
if ( config . settings . heapdump === true ) { modules . push ( 'heapdump@0.3.15' ) ; }
2020-03-29 03:28:44 +03:00
2018-03-15 01:16:55 +03:00
// Install any missing modules and launch the server
2023-10-21 09:36:02 +03:00
InstallModules ( modules , args , function ( ) {
2021-02-21 22:17:18 +03:00
if ( require ( 'os' ) . platform ( ) == 'win32' ) { try { require ( 'node-windows' ) ; } catch ( ex ) { console . log ( "Module node-windows can't be loaded. Restart MeshCentral." ) ; process . exit ( ) ; return ; } }
2020-10-30 01:42:46 +03:00
meshserver = CreateMeshCentralServer ( config , args ) ;
meshserver . Start ( ) ;
} ) ;
2019-07-18 01:57:42 +03:00
// On exit, also terminate the child process if applicable
2020-03-11 11:31:29 +03:00
process . on ( 'exit' , function ( ) { if ( childProcess ) { childProcess . kill ( ) ; childProcess = null ; } } ) ;
2019-07-18 01:57:42 +03:00
// If our parent exits, we also exit
2019-07-18 03:34:34 +03: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-15 01:16:55 +03:00
} ) ;
}
if ( require . main === module ) {
2019-05-18 01:44:01 +03:00
mainStart ( ) ; // Called directly, launch normally.
2018-03-15 01:16:55 +03:00
} else {
module . exports . mainStart = mainStart ; // Required as a module, useful for winservice.js
2019-03-25 21:32:16 +03:00
}