mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-10-29 23:35:02 -04:00
Merge branch 'Ylianst:master' into dockerrewrite
This commit is contained in:
commit
39395c9ac7
Binary file not shown.
@ -148,7 +148,7 @@ function linux_identifiers()
|
||||
// Fetch Storage Info
|
||||
child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.stdout.str = ''; child.stdout.on('data', dataHandler);
|
||||
child.stdin.write("lshw -class disk | tr '\\n' '`' | awk '" + '{ len=split($0,lines,"*"); printf "["; for(i=2;i<=len;++i) { model=""; caption=""; size=""; clen=split(lines[i],item,"`"); for(j=2;j<clen;++j) { split(item[j],tokens,":"); split(tokens[1],key," "); if(key[1]=="description") { caption=substr(tokens[2],2); } if(key[1]=="product") { model=substr(tokens[2],2); } if(key[1]=="size") { size=substr(tokens[2],2); } } if(model=="") { model=caption; } if(caption!="" || model!="") { printf "%s{\\"Caption\\":\\"%s\\",\\"Model\\":\\"%s\\",\\"Size\\":\\"%s\\"}",(i==2?"":","),caption,model,size; } } printf "]"; }\'\nexit\n');
|
||||
child.stdin.write("lshw -class disk -disable network | tr '\\n' '`' | awk '" + '{ len=split($0,lines,"*"); printf "["; for(i=2;i<=len;++i) { model=""; caption=""; size=""; clen=split(lines[i],item,"`"); for(j=2;j<clen;++j) { split(item[j],tokens,":"); split(tokens[1],key," "); if(key[1]=="description") { caption=substr(tokens[2],2); } if(key[1]=="product") { model=substr(tokens[2],2); } if(key[1]=="size") { size=substr(tokens[2],2); } } if(model=="") { model=caption; } if(caption!="" || model!="") { printf "%s{\\"Caption\\":\\"%s\\",\\"Model\\":\\"%s\\",\\"Size\\":\\"%s\\"}",(i==2?"":","),caption,model,size; } } printf "]"; }\'\nexit\n');
|
||||
child.waitExit();
|
||||
try { identifiers['storage_devices'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
|
||||
child = null;
|
||||
|
||||
@ -170,8 +170,8 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
|
||||
|
||||
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
|
||||
const TLSSocket = require('tls').TLSSocket;
|
||||
const tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
|
||||
if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
const tlsoptions = { minVersion: 'TLSv1', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
const tlsock = new TLSSocket(ser, tlsoptions);
|
||||
tlsock.on('error', function (err) { Debug(1, "CIRA TLS Connection Error ", err); });
|
||||
tlsock.on('secureConnect', function () { Debug(2, "CIRA Secure TLS Connection"); ws._socket.resume(); });
|
||||
@ -228,8 +228,8 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
|
||||
obj.forwardclient.setEncoding('binary');
|
||||
} else {
|
||||
// If TLS is going to be used, setup a TLS socket
|
||||
var tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
|
||||
if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
var tlsoptions = { minVersion: 'TLSv1', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
obj.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
|
||||
// The TLS connection method is the same as TCP, but located a bit differently.
|
||||
Debug(2, 'TLS Intel AMT transport connected to ' + node.host + ':' + port + '.');
|
||||
|
||||
@ -236,8 +236,8 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
|
||||
if (state == 0) { obj.xxOnSocketClosed(); }
|
||||
if (state == 2) {
|
||||
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
|
||||
var options = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
|
||||
if (obj.xtlsMethod == 1) { options.secureProtocol = 'TLSv1_method'; }
|
||||
var options = { minVersion: 'TLSv1', socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (obj.xtlsMethod == 1) { options.secureProtocol = 'TLSv1_method'; }
|
||||
if (obj.xtlsoptions) {
|
||||
if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }
|
||||
if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; }
|
||||
@ -274,8 +274,8 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
|
||||
obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected);
|
||||
} else {
|
||||
// Direct connect with TLS
|
||||
var options = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
if (obj.xtlsMethod != 0) { options.secureProtocol = 'TLSv1_method'; }
|
||||
var options = { minVersion: 'TLSv1', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (obj.xtlsMethod != 0) { options.secureProtocol = 'TLSv1_method'; }
|
||||
if (obj.xtlsoptions) {
|
||||
if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }
|
||||
if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; }
|
||||
|
||||
@ -387,8 +387,8 @@ module.exports.CreateAmtScanner = function (parent) {
|
||||
} else {
|
||||
// Connect using TLS, we will switch from default TLS to TLS1-only and back if we get a connection error to support older Intel AMT.
|
||||
if (scaninfo.tlsoption == null) { scaninfo.tlsoption = 0; }
|
||||
const tlsOptions = { rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE };
|
||||
if (scaninfo.tlsoption == 1) { tlsOptions.secureProtocol = 'TLSv1_method'; }
|
||||
const tlsOptions = { minVersion: 'TLSv1', rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION };
|
||||
// if (scaninfo.tlsoption == 1) { tlsOptions.secureProtocol = 'TLSv1_method'; }
|
||||
client = obj.tls.connect(port, host, tlsOptions, function () { this.write('GET / HTTP/1.1\r\nhost: ' + host + '\r\n\r\n'); });
|
||||
}
|
||||
client.scaninfo = scaninfo;
|
||||
|
||||
2
db.js
2
db.js
@ -3661,7 +3661,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
let mesg = 'Zipbackup failed (' + obj.backupStatus.toString(2).slice(-8) + '), deleting incomplete backup: ' + obj.newAutoBackupFile;
|
||||
if (func) { func(mesg) }
|
||||
else { parent.addServerWarning(mesg, true ) };
|
||||
if (fs.existsSync(obj.newAutoBackupFile)) { fs.unlink(obj.newAutoBackupFile, function (err) { console.error('Failed to clean up backupfile: ' + err.message) }) };
|
||||
if (fs.existsSync(obj.newAutoBackupFile)) { fs.unlink(obj.newAutoBackupFile, function (err) { if (err) {console.error('Failed to clean up backupfile: ' + err.message)} }) };
|
||||
};
|
||||
if (obj.databaseType != DB_NEDB) {
|
||||
//remove dump archive file, because zipped and otherwise fills up
|
||||
|
||||
@ -99,3 +99,50 @@ Now that MeshCentral customizes and signs the agent, you can set that value to a
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## External Signing Job
|
||||
|
||||
The externalsignjob feature allows you to perform additional operations on the agent after MeshCentral completes its code signing process. This is particularly useful for:
|
||||
|
||||
1. Using hardware security tokens for signing
|
||||
2. Performing signing on a separate server or cloud host
|
||||
3. Archiving signed agents
|
||||
4. Adding additional security measures
|
||||
|
||||
The externalsignjob is called after MeshCentral completes its entire code signing process, including:
|
||||
- Resource modification
|
||||
- Digital signature application
|
||||
- Timestamp application (if configured)
|
||||
|
||||
To use this feature, add the following to your config.json:
|
||||
|
||||
```json
|
||||
"settings": {
|
||||
"externalsignjob": "path/to/your/script.bat"
|
||||
}
|
||||
```
|
||||
|
||||
The script will receive the path to the agent as its first argument. Here are example scripts:
|
||||
|
||||
### Batch File Example
|
||||
```batch
|
||||
@echo off
|
||||
Echo External Signing Job
|
||||
signtool sign /tr http://timestamp.sectigo.com /td SHA256 /fd SHA256 /a /v /f path/to/your/signing.cer /csp "eToken Base Cryptographic Provider" /k "[{{MyPassword}}]=PrivateKeyContainerName" "%~1"
|
||||
```
|
||||
|
||||
### PowerShell Example
|
||||
```powershell
|
||||
$file = $args[0]
|
||||
signtool sign /tr http://timestamp.sectigo.com /td SHA256 /fd SHA256 /a /v /f path/to/your/signing.cer /csp "eToken Base Cryptographic Provider" /k "[{{MyPassword}}]=PrivateKeyContainerName" $file
|
||||
```
|
||||
|
||||
The externalsignjob can be used for more than just signing. For example, you could:
|
||||
|
||||
1. Archive signed agents to a secure location
|
||||
2. Upload signed agents to a distribution server
|
||||
3. Perform additional security checks
|
||||
4. Add custom metadata or watermarks
|
||||
5. Integrate with your organization's build pipeline
|
||||
|
||||
Note: The script must return a success exit code (0) for the process to be considered successful. Any non-zero exit code will be treated as a failure and will be logged.
|
||||
|
||||
@ -1095,6 +1095,7 @@ First we will start with the MeshCentral configuration, here is a minimal config
|
||||
"domains": {
|
||||
"": {
|
||||
"certUrl": "https://127.0.0.1:443/"
|
||||
"AgentConfig": [ "webSocketMaskOverride=1" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Click here to download the MeshCentral Assistant for Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Click here to download the MeshCentral Assistant for Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ For Linux, cut & paste the following in a terminal to install the agent:
|
||||
~<area-assistant>
|
||||
For MeshCentral Assistant on Windows, nagivate to the following link to complete the process:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Kliknite ovdje da preuzmete MeshCentral Assistant za Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Kliknite ovdje da preuzmete MeshCentral Assistant za Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Za Linux, izrežite i zalijepite sljedeće u terminal da biste instalirali agent
|
||||
~<area-assistant>
|
||||
Za MeshCentral Assistant na Windows-u, idite na sljedeću vezu da dovršite proces:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Kliknutím sem stáhnete MeshCentral Assistant pro Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Kliknutím sem stáhnete MeshCentral Assistant pro Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ V případě systému Linux vyjměte a vložte do terminálu a nainstalujte agen
|
||||
~<area-assistant>
|
||||
Pro MeshCentral Assistant v systému Windows přejděte na následující odkaz a dokončete proces:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Klik her for at downloade MeshCentral Assistant til Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Klik her for at downloade MeshCentral Assistant til Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ For Linux skal du klippe og indsætte følgende i en terminal for at installere
|
||||
~<area-assistant>
|
||||
For MeshCentral Assistant på Windows skal du gå til følgende link for at fuldføre processen:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Klicken Sie hier, um den MeshCentral-Assistenten für Windows herunterzuladen.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Klicken Sie hier, um den MeshCentral-Assistenten für Windows herunterzuladen.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Schneiden Sie unter Linux Folgendes aus und fügen Sie es in ein Terminal ein, u
|
||||
~<area-assistant>
|
||||
Navigieren Sie für MeshCentral Assistant unter Windows zum folgenden Link, um den Vorgang abzuschließen:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Haga clic aquí para descargar el Asistente de MeshCentral para Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Haga clic aquí para descargar el Asistente de MeshCentral para Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Para Linux, copia y pega lo siguiente en la terminal para instalar el agente:
|
||||
~<area-assistant>
|
||||
Para el Asistente de MeshCentral en Windows, navegue hasta el siguiente enlace para completar el proceso:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Napsauta tätä ladataksesi MeshCentral Assistant for Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Napsauta tätä ladataksesi MeshCentral Assistant for Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Linux: leikkaa ja liitä seuraava päätelaitteeseen agentin asentamiseksi:
|
||||
~<area-assistant>
|
||||
Jos käytät MeshCentral Assistantia Windowsissa, siirry seuraavaan linkkiin prosessin viimeistelemiseksi:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Cliquez ici pour télécharger l'assistant Meshcentral pour Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Cliquez ici pour télécharger l'assistant Meshcentral pour Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Pour Linux, copiez et collez les éléments suivants dans un terminal pour insta
|
||||
~<area-assistant>
|
||||
Pour l'assistant MeshCentral sous Windows, cliquez sur le lien suivant pour compléter le processus :
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">विंडोज के लिए मेशसेंट्रल असिस्टेंट डाउनलोड करने के लिए यहां क्लिक करें।</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">विंडोज के लिए मेशसेंट्रल असिस्टेंट डाउनलोड करने के लिए यहां क्लिक करें।</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Apple OSX के लिए, इस प्रक्रिया को पूर
|
||||
~<area-assistant>
|
||||
विंडोज़ पर मेशसेंट्रल असिस्टेंट के लिए, प्रक्रिया को पूरा करने के लिए निम्न लिंक पर जाएँ:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Kattintson ide a MeshCentral Assistant for Windows letöltéséhez.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Kattintson ide a MeshCentral Assistant for Windows letöltéséhez.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Linux esetén az Agent telepítéséhez vágja ki és illessze be egy terminálb
|
||||
~<area-assistant>
|
||||
Windows rendszeren futó MeshCentral Assistant esetén menjen a következő hivatkozásra a folyamat befejezéséhez:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Fare clic qui per scaricare MeshCentral Assistant per Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Fare clic qui per scaricare MeshCentral Assistant per Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Per Linux, taglia e incolla quanto segue in un terminale per installare l'agente
|
||||
~<area-assistant>
|
||||
Per MeshCentral Assistant su Windows, accedere al seguente collegamento per completare il processo:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Windows용 MeshCentral Assistant를 다운로드하려면 여기를 클릭하십시오.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Windows용 MeshCentral Assistant를 다운로드하려면 여기를 클릭하십시오.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Linuxの場合は、ターミナルで以下をカットアンドペーストし
|
||||
~<area-assistant>
|
||||
Windows용 MeshCentral Assistant의 경우 다음 링크로 이동하여 프로세스를 완료하십시오.
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Windows용 MeshCentral Assistant를 다운로드하려면 여기를 클릭하십시오.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Windows용 MeshCentral Assistant를 다운로드하려면 여기를 클릭하십시오.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Linux의 경우, 다음을 잘라내어 터미널에 붙여 넣어 에이전트
|
||||
~<area-assistant>
|
||||
Windows용 MeshCentral Assistant의 경우 다음 링크로 이동하여 프로세스를 완료하십시오.
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Klik hier om de MeshCentral Assistant voor Windows te downloaden.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Klik hier om de MeshCentral Assistant voor Windows te downloaden.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Voor Linux, knip het volgende en plak dit in een terminal om de agent te install
|
||||
~<area-assistant>
|
||||
Voor MeshCentral Assistant voor Windows navigeert u naar de volgende link om het proces te voltooien:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Kliknij tutaj by pobrać Asystenta MeshCentral dla Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Kliknij tutaj by pobrać Asystenta MeshCentral dla Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ W systemie Linuks, wytnij i wklej poniższe polecenia w terminalu, aby zainstalo
|
||||
~<area-assistant>
|
||||
W przypadku Asystenta MeshCentral w Windows, przejdź do tego linku aby zakończyć proces:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Clique aqui para baixar o Mesh Central Assistant para Windows</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Clique aqui para baixar o Mesh Central Assistant para Windows</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Para Linux, recorte e cole o seguinte em um terminal para instalar o agente:
|
||||
~<area-assistant>
|
||||
Para o MeshCentral Assistant no Windows, abre o seguinte link para completar o processo:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Clique aqui para baixar o Assistente MeshCentral para Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Clique aqui para baixar o Assistente MeshCentral para Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Para Linux, recorte e cole o seguinte em um terminal para instalar o agente:
|
||||
~<area-assistant>
|
||||
Para o MeshCentral Assistant no Windows, navegue até o seguinte link para concluir o processo:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Нажмите здесь, чтобы загрузить MeshCentral Assistant для Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Нажмите здесь, чтобы загрузить MeshCentral Assistant для Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
~<area-assistant>
|
||||
Для MeshCentral Assistant под Windows перейдите по следующей ссылке, чтобы завершить процесс:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Klicka här för att ladda ner MeshCentral Assistant för Windows.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Klicka här för att ladda ner MeshCentral Assistant för Windows.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ För Linux, klipp och klistra in följande i en terminal för att installera age
|
||||
~<area-assistant>
|
||||
För MeshCentral Assistant på Windows, gå till följande länk för att slutföra processen:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">Windows için MeshCentral Assistant'ı indirmek için buraya tıklayın.</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">Windows için MeshCentral Assistant'ı indirmek için buraya tıklayın.</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@ Linux için, aracıyı yüklemek için aşağıdakileri kesip bir terminale yap
|
||||
~<area-assistant>
|
||||
Windows'ta MeshCentral Assistant için işlemi tamamlamak üzere aşağıdaki bağlantıya gidin:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">单击此处下载适用于 Windows 的 MeshCentral 助手。</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">单击此处下载适用于 Windows 的 MeshCentral 助手。</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
~<area-assistant>
|
||||
对于 Windows 上的 MeshCentral Assistant,导航到以下链接以完成该过程:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</area-linux>
|
||||
<area-assistant>
|
||||
<p style="margin-left:30px">
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]">單擊此處下載適用於 Windows 的 MeshCentral 助手。</a>
|
||||
<a href="[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]">單擊此處下載適用於 Windows 的 MeshCentral 助手。</a>
|
||||
</p>
|
||||
</area-assistant>
|
||||
<area-link>
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
~<area-assistant>
|
||||
對於 Windows 上的 MeshCentral Assistant,導航到以下鏈接以完成該過程:
|
||||
~
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]
|
||||
~[[[SERVERURL]]]/meshagents?id=10006&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&ac=[[[ASSISTANTTYPE]]]
|
||||
~
|
||||
~</area-assistant>
|
||||
~<area-link>
|
||||
|
||||
@ -66,7 +66,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
|
||||
if (obj.parent.config.letsencrypt.email == null) { obj.configErr = "Let's Encrypt email address not specified."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; }
|
||||
if ((obj.parent.redirserver == null) || ((typeof obj.parent.config.settings.rediraliasport === 'number') && (obj.parent.config.settings.rediraliasport !== 80)) || ((obj.parent.config.settings.rediraliasport == null) && (obj.parent.redirserver.port !== 80))) { obj.configErr = "Redirection web server must be active on port 80 for Let's Encrypt to work."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; }
|
||||
if (obj.redirWebServerHooked !== true) { obj.configErr = "Redirection web server not setup for Let's Encrypt to work."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; }
|
||||
if ((obj.parent.config.letsencrypt.rsakeysize != null) && (obj.parent.config.letsencrypt.rsakeysize !== 2048) && (obj.parent.config.letsencrypt.rsakeysize !== 3072)) { obj.configErr = "Invalid Let's Encrypt certificate key size, must be 2048 or 3072."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; }
|
||||
if ((obj.parent.config.letsencrypt.rsakeysize != null) && (obj.parent.config.letsencrypt.rsakeysize !== 2048) && (obj.parent.config.letsencrypt.rsakeysize !== 3072) && (obj.parent.config.letsencrypt.rsakeysize !== 4096)) { obj.configErr = "Invalid Let's Encrypt certificate key size, must be 2048, 3072 or 4096."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; }
|
||||
if (obj.checkInterval == null) { obj.checkInterval = setInterval(obj.checkRenewCertificate, 86400000); } // Call certificate check every 24 hours.
|
||||
obj.configOk = true;
|
||||
|
||||
@ -165,7 +165,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
|
||||
|
||||
// Create a private key
|
||||
obj.log("Generating private key...");
|
||||
acme.forge.createPrivateKey().then(function (accountKey) {
|
||||
acme.forge.createPrivateKey(obj.parent.config.letsencrypt.rsakeysize != null ? obj.parent.config.letsencrypt.rsakeysize : 2048).then(function (accountKey) {
|
||||
|
||||
// Create the ACME client
|
||||
obj.log("Setting up ACME client...");
|
||||
@ -189,7 +189,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
|
||||
|
||||
// Create Certificate Request (CSR)
|
||||
obj.log("Creating certificate request...");
|
||||
var certRequest = { commonName: obj.leDomains[0] };
|
||||
var certRequest = { commonName: obj.leDomains[0], keySize: obj.parent.config.letsencrypt.rsakeysize != null ? obj.parent.config.letsencrypt.rsakeysize : 2048 };
|
||||
if (obj.leDomains.length > 1) { certRequest.altNames = obj.leDomains; }
|
||||
acme.forge.createCsr(certRequest).then(function (r) {
|
||||
obj.csr = r[1];
|
||||
|
||||
@ -546,7 +546,7 @@
|
||||
},
|
||||
"agentWsCompression": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"default": false,
|
||||
"description": "Enables agent-side, websocket per-message deflate compression. wscompression must also be true for this to work."
|
||||
},
|
||||
"noAgentUpdate": {
|
||||
@ -3892,6 +3892,11 @@
|
||||
"default": false,
|
||||
"description": "By default a test certificate will be obtained from Let's Encrypt. Setting \"zerossl\", will ignore this setting. Always start by getting a test certificate and make sure that works before setting this to true and obtaining a production certificate. Making too many bad requests for a production certificate will get you banned for a long period of time."
|
||||
},
|
||||
"rsaKeySize": {
|
||||
"type": "integer",
|
||||
"default": 2048,
|
||||
"description": "The size of the RSA key to generate. The default is 2048 bits."
|
||||
},
|
||||
"nochecks": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
|
||||
@ -3415,6 +3415,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
// Failed to sign agent
|
||||
addServerWarning('Failed to sign \"' + agentSignedFunc.objx.meshAgentsArchitectureNumbers[agentSignedFunc.archid].localname + '\": ' + err, 22, [agentSignedFunc.objx.meshAgentsArchitectureNumbers[agentSignedFunc.archid].localname, err]);
|
||||
}
|
||||
obj.callExternalSignJob(agentSignedFunc.signingArguments); // Call external signing job regardless of success or failure
|
||||
if (--pendingOperations === 0) { agentSignedFunc.func(); }
|
||||
}
|
||||
pendingOperations++;
|
||||
@ -3470,7 +3471,10 @@ function CreateMeshCentralServer(config, args) {
|
||||
}
|
||||
|
||||
const signingArguments = { out: signeedagentpath, desc: signDesc, url: signUrl, time: timeStampUrl, proxy: timeStampProxy }; // Shallow clone
|
||||
signingArguments.resChanges = resChanges;
|
||||
|
||||
obj.debug('main', "Code signing with arguments: " + JSON.stringify(signingArguments));
|
||||
xagentSignedFunc.signingArguments = signingArguments; // Attach the signing arguments to the callback function
|
||||
if (resChanges == false) {
|
||||
// Sign the agent the simple way, without changing any resources.
|
||||
originalAgent.sign(agentSignCertInfo, signingArguments, xagentSignedFunc);
|
||||
@ -3479,16 +3483,40 @@ function CreateMeshCentralServer(config, args) {
|
||||
// NOTE: This is experimental and could corupt the agent.
|
||||
originalAgent.writeExecutable(signingArguments, agentSignCertInfo, xagentSignedFunc);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Signed agent is already ok, use it.
|
||||
originalAgent.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (--pendingOperations === 0) { func(); }
|
||||
}
|
||||
|
||||
obj.callExternalSignJob = function (signingArguments) {
|
||||
if (obj.config.settings && !obj.config.settings.externalsignjob) {
|
||||
return;
|
||||
}
|
||||
obj.debug('main', "External signing job called for file: " + signingArguments.out);
|
||||
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const signResult = spawnSync('"' + obj.config.settings.externalsignjob + '"', ['"' + signingArguments.out + '"'], {
|
||||
encoding: 'utf-8',
|
||||
shell: true,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
if (signResult.error || signResult.status !== 0) {
|
||||
obj.debug('main', "External signing failed for file: " + signingArguments.out);
|
||||
console.error("External signing failed for file: " + signingArguments.out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the list of available mesh agents
|
||||
obj.updateMeshAgentsTable = function (domain, func) {
|
||||
// Check if a custom agent signing certificate is available
|
||||
|
||||
@ -346,7 +346,7 @@ module.exports.CreateMeshMail = function (parent, domain) {
|
||||
}
|
||||
|
||||
// Set all the template replacement options and generate the final email text (both in txt and html formats).
|
||||
var options = { username: username, name: name, email: email, installflags: flags, msg: msg, meshid: meshid, meshidhex: meshid.split('/')[2], servername: domain.title ? domain.title : 'MeshCentral' };
|
||||
var options = { username: username, name: name, email: email, installflags: flags, msg: msg, meshid: meshid, meshidhex: meshid.split('/')[2], servername: domain.title ? domain.title : 'MeshCentral', assistanttype: (domain.assistanttypeagentinvite ? domain.assistanttypeagentinvite : 0)};
|
||||
if (loginkey != null) { options.urlargs1 = '?key=' + loginkey; options.urlargs2 = '&key=' + loginkey; } else { options.urlargs1 = ''; options.urlargs2 = ''; }
|
||||
options.windows = ((os == 0) || (os == 1)) ? 1 : 0;
|
||||
options.linux = ((os == 0) || (os == 2)) ? 1 : 0;
|
||||
|
||||
138
meshrelay.js
138
meshrelay.js
@ -78,6 +78,72 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
}
|
||||
}
|
||||
|
||||
// Record a new entry in a recording log
|
||||
function recordingEntry (logfile, type, flags, data, func, tag) {
|
||||
try {
|
||||
if (logfile.text) {
|
||||
// Text recording format
|
||||
var out = '';
|
||||
const utcDate = new Date(Date.now());
|
||||
if (type == 1) {
|
||||
// End of start
|
||||
out = data + '\r\n' + utcDate.toUTCString() + ', ' + "<<<START>>>" + '\r\n';
|
||||
} else if (type == 3) {
|
||||
// End of log
|
||||
out = new Date(Date.now() - 5000).toUTCString() + ', ' + "<<<END>>>" + '\r\n';
|
||||
} else if (typeof data == 'string') {
|
||||
// Log message
|
||||
if (logfile.text == 1) {
|
||||
out = utcDate.toUTCString() + ', ' + data + '\r\n';
|
||||
} else if (logfile.text == 2) {
|
||||
try {
|
||||
var x = JSON.parse(data);
|
||||
if (typeof x.action == 'string') {
|
||||
if ((x.action == 'chat') && (typeof x.msg == 'string')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + x.msg + '\r\n'); }
|
||||
else if ((x.action == 'file') && (typeof x.name == 'string') && (typeof x.size == 'number')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + "File Transfer" + ', \"' + x.name + '\" (' + x.size + ' ' + "bytes" + ')\r\n'); }
|
||||
} else if (x.ctrlChannel == null) { out = utcDate.toUTCString() + ', ' + data + '\r\n'; }
|
||||
} catch (ex) {
|
||||
out = utcDate.toUTCString() + ', ' + data + '\r\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out != null) {
|
||||
// Log this event
|
||||
const block = Buffer.from(out);
|
||||
require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
} else {
|
||||
// Skip logging this.
|
||||
func(logfile, tag);
|
||||
}
|
||||
} else {
|
||||
// Binary recording format
|
||||
if (typeof data == 'string') {
|
||||
// String write
|
||||
var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Start, 2 = Network Data, 3 = End)
|
||||
header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(blockData.length, 4); // Size
|
||||
header.writeIntBE((type == 3 ? new Date(Date.now() - 5000) : new Date()), 10, 6); // Time
|
||||
var block = Buffer.concat([header, blockData]);
|
||||
require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
} else {
|
||||
// Binary write
|
||||
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Start, 2 = Network Data)
|
||||
header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(data.length, 4); // Size
|
||||
header.writeIntBE((type == 3 ? new Date(Date.now() - 5000) : new Date()), 10, 6); // Time
|
||||
var block = Buffer.concat([header, data]);
|
||||
require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
}
|
||||
}
|
||||
} catch (ex) { console.log(ex); func(logfile, tag); }
|
||||
}
|
||||
module.exports.recordingEntry = recordingEntry;
|
||||
|
||||
function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
|
||||
const currentTime = Date.now();
|
||||
if (cookie) {
|
||||
@ -123,6 +189,9 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
|
||||
// Check if protocol is set in the cookie and if so replace req.query.p but only if its not already set or blank
|
||||
if ((cookie != null) && (typeof cookie.p == 'number') && (obj.req.query.p === undefined || obj.req.query.p === "")) { obj.req.query.p = cookie.p; }
|
||||
|
||||
// Patch Messenger protocol to 200
|
||||
if ((obj.id != null) && (obj.id.startsWith('meshmessenger/') == true)) { obj.req.query.p = 200; }
|
||||
|
||||
// Mesh Rights
|
||||
const MESHRIGHT_EDITMESH = 1;
|
||||
const MESHRIGHT_MANAGEUSERS = 2;
|
||||
@ -512,6 +581,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
|
||||
if (obj.req.query.p == 1) { msg = 'Started terminal session'; msgid = 14; }
|
||||
else if (obj.req.query.p == 2) { msg = 'Started desktop session'; msgid = 15; }
|
||||
else if (obj.req.query.p == 5) { msg = 'Started file management session'; msgid = 16; }
|
||||
else if (obj.req.query.p == 200) { msg = 'Started messenger session'; msgid = 162; }
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: sessionUser._id, username: sessionUser.name, msgid: msgid, msgArgs: [obj.id, obj.peer.req.clientIp, req.clientIp], msg: msg + ' \"' + obj.id + '\" from ' + obj.peer.req.clientIp + ' to ' + req.clientIp, protocol: req.query.p, nodeid: req.query.nodeid };
|
||||
if (obj.guestname) { event.guestname = obj.guestname; } else if (relayinfo.peer1.guestname) { event.guestname = relayinfo.peer1.guestname; } // If this is a sharing session, set the guest name here.
|
||||
parent.parent.DispatchEvent(['*', sessionUser._id], obj, event);
|
||||
@ -747,13 +817,14 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
|
||||
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
|
||||
recordingEntry(logfile, 3, 0, 'MeshCentralMCREC', function (logfile, tag) {
|
||||
parent.parent.fs.closeSync(logfile.fd);
|
||||
parent.parent.debug('relay', 'Relay: Finished recording to file: ' + tag.logfile.filename);
|
||||
|
||||
// Now that the recording file is closed, check if we need to index this file.
|
||||
if (domain.sessionrecording.index && domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
|
||||
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (tag.logfile.startTime != null) { sessionLength = Math.round((Date.now() - tag.logfile.startTime) / 1000); }
|
||||
if (tag.logfile.startTime != null) { sessionLength = Math.round((Date.now() - tag.logfile.startTime) / 1000) - 5; }
|
||||
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.parent.path.basename(tag.logfile.filename);
|
||||
@ -792,71 +863,6 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
|
||||
if (obj.pid != null) { parent.parent.RemoveAllEventDispatch(obj); }
|
||||
}
|
||||
|
||||
// Record a new entry in a recording log
|
||||
function recordingEntry(logfile, type, flags, data, func, tag) {
|
||||
try {
|
||||
if (logfile.text) {
|
||||
// Text recording format
|
||||
var out = '';
|
||||
const utcDate = new Date(Date.now());
|
||||
if (type == 1) {
|
||||
// End of start
|
||||
out = data + '\r\n' + utcDate.toUTCString() + ', ' + "<<<START>>>" + '\r\n';
|
||||
} else if (type == 3) {
|
||||
// End of log
|
||||
out = utcDate.toUTCString() + ', ' + "<<<END>>>" + '\r\n';
|
||||
} else if (typeof data == 'string') {
|
||||
// Log message
|
||||
if (logfile.text == 1) {
|
||||
out = utcDate.toUTCString() + ', ' + data + '\r\n';
|
||||
} else if (logfile.text == 2) {
|
||||
try {
|
||||
var x = JSON.parse(data);
|
||||
if (typeof x.action == 'string') {
|
||||
if ((x.action == 'chat') && (typeof x.msg == 'string')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + x.msg + '\r\n'); }
|
||||
else if ((x.action == 'file') && (typeof x.name == 'string') && (typeof x.size == 'number')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + "File Transfer" + ', \"' + x.name + '\" (' + x.size + ' ' + "bytes" + ')\r\n'); }
|
||||
} else if (x.ctrlChannel == null) { out = utcDate.toUTCString() + ', ' + data + '\r\n'; }
|
||||
} catch (ex) {
|
||||
out = utcDate.toUTCString() + ', ' + data + '\r\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out != null) {
|
||||
// Log this event
|
||||
const block = Buffer.from(out);
|
||||
parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
} else {
|
||||
// Skip logging this.
|
||||
func(logfile, tag);
|
||||
}
|
||||
} else {
|
||||
// Binary recording format
|
||||
if (typeof data == 'string') {
|
||||
// String write
|
||||
var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
|
||||
header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(blockData.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, blockData]);
|
||||
parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
} else {
|
||||
// Binary write
|
||||
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
|
||||
header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(data.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, data]);
|
||||
parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
|
||||
logfile.size += block.length;
|
||||
}
|
||||
}
|
||||
} catch (ex) { console.log(ex); func(logfile, tag); }
|
||||
}
|
||||
|
||||
// If this session has a expire time, setup the expire timer now.
|
||||
setExpireTimer();
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "1.1.43",
|
||||
"version": "1.1.44",
|
||||
"keywords": [
|
||||
"Remote Device Management",
|
||||
"Remote Device Monitoring",
|
||||
|
||||
2
public/scripts/amt-desktop-0.0.2-min.js
vendored
2
public/scripts/amt-desktop-0.0.2-min.js
vendored
File diff suppressed because one or more lines are too long
@ -735,7 +735,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
|
||||
}
|
||||
|
||||
/*
|
||||
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you don’t need to
|
||||
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you dont need to
|
||||
implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol).
|
||||
The only subset recognized by the FW is the defined by the following sets : XK_LATIN1 , XK_MISCELLANY, XK_3270, XK_XKB_KEYS, XK_KATAKANA.
|
||||
In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support:
|
||||
@ -1018,7 +1018,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
|
||||
obj.recordedData = [];
|
||||
obj.recordedStart = Date.now();
|
||||
obj.recordedSize = 0;
|
||||
obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 200, bpp: obj.bpp, graymode: obj.graymode, lowcolor: obj.lowcolor, screenSize: [obj.width, obj.height] }))); // Metadata, 200 = Midstream Intel AMT KVM
|
||||
obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 102, bpp: obj.bpp, graymode: obj.graymode, lowcolor: obj.lowcolor, screenSize: [obj.width, obj.height] }))); // Metadata, 102 = Midstream Intel AMT KVM
|
||||
obj.DeskRecordServerInit = String.fromCharCode((obj.width >> 8), (obj.width & 0xFF), (obj.height >> 8), (obj.height & 0xFF)) + obj.DeskRecordServerInit.substring(4);
|
||||
obj.recordedData.push(recordingEntry(2, 1, obj.DeskRecordServerInit)); // This is the server init command
|
||||
obj.recordedData.push(recordingEntry(3, 0, atob(obj.CanvasId.toDataURL('image/png').split(',')[1]))); // Take a screen shot
|
||||
|
||||
File diff suppressed because one or more lines are too long
5
public/styles/flatpickr.min.css
vendored
5
public/styles/flatpickr.min.css
vendored
File diff suppressed because one or more lines are too long
11710
translate/translate.json
11710
translate/translate.json
File diff suppressed because it is too large
Load Diff
@ -5187,7 +5187,11 @@
|
||||
if (i > 0) { su = u.substring(i + 1); }
|
||||
su = EscapeHtml(su);
|
||||
if (su.length > 15) { su = su.substring(0, 14) + '…'; }
|
||||
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
||||
if (node.lusers && node.lusers.length > 0) {
|
||||
return addKeyLinkConditional(EscapeHtml(su), EscapeHtml(u) + " " + "(" + "Locked" + ")", (node.lusers && node.lusers.indexOf(u) >= 0));
|
||||
} else {
|
||||
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
|
||||
@ -8468,7 +8472,8 @@
|
||||
x += '<div id=d2modenow>';
|
||||
x += addHtmlValue("Expire Time", '<select id=d2inviteExpire style=float:right;width:250px>' + y + '</select>');
|
||||
x += '</div><div id=d2moderange style=display:none>';
|
||||
x += addHtmlValue("Time Range", '<input id=d2timeRangeSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Time Range Start", '<input id=d2timeRangeStartSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Time Range End", '<input id=d2timeRangeEndSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += '</div><div id=d2moderecurring style=display:none>';
|
||||
x += addHtmlValue("Start Time", '<input id=d2timeStartSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Duration", '<select id=d2inviteDuration style=float:right;width:250px>' + z + '</select>');
|
||||
@ -8480,9 +8485,18 @@
|
||||
showShareDeviceValidate();
|
||||
var tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
var rangeTime = flatpickr('#d2timeRangeSelector', { mode: 'range', enableTime: true, minDate: new Date(), defaultDate: [ new Date(), tomorrow ], minuteIncrement: 1 });
|
||||
var startTime = flatpickr('#d2timeStartSelector', { enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1 });
|
||||
xxdialogTag = { range: rangeTime, start: startTime };
|
||||
var rangeStartTime = flatpickr('#d2timeRangeStartSelector', {
|
||||
enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1, plugins: [new confirmDatePlugin({})],
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
rangeEndTime.set('minDate', dateStr);
|
||||
if (rangeEndTime.selectedDates[0] && rangeEndTime.selectedDates[0] < selectedDates[0]) {
|
||||
rangeEndTime.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
var rangeEndTime = flatpickr('#d2timeRangeEndSelector', { enableTime: true, minDate: new Date(), defaultDate: tomorrow, minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
var startTime = flatpickr('#d2timeStartSelector', { enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
xxdialogTag = { rangeStart: rangeStartTime, rangeEnd: rangeEndTime, start: startTime };
|
||||
}
|
||||
|
||||
function showShareDeviceValidate() {
|
||||
@ -8525,7 +8539,7 @@
|
||||
if (Q('d2timeRange').value == 0) {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly });
|
||||
} else if (Q('d2timeRange').value == 1) {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.range.selectedDates[0].getTime() / 1000), end: Math.floor(tag.range.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly });
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.rangeStart.selectedDates[0].getTime() / 1000), end: Math.floor(tag.rangeEnd.selectedDates[0].getTime() / 1000), consent: consent, viewOnly: viewOnly });
|
||||
} else {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.start.selectedDates[0].getTime() / 1000), expire: parseInt(Q('d2inviteDuration').value), recurring: Q('d2timeRange').value - 1, consent: consent, viewOnly: viewOnly });
|
||||
}
|
||||
@ -9740,7 +9754,7 @@
|
||||
var rdpflags = 0;
|
||||
for (var i = 1; i < 10; i++) { if ((i != 5) && (Q('d7rdp' + i).checked)) { rdpflags |= (1 << (i - 1)); } }
|
||||
desktopsettings.rdpflags = rdpflags;
|
||||
localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
|
||||
putstore('desktopsettings', JSON.stringify(desktopsettings));
|
||||
applyDesktopSettings();
|
||||
updateDesktopButtons();
|
||||
if (desktop) {
|
||||
@ -15356,7 +15370,8 @@
|
||||
158: "Displaying alert box, title=\"{0}\", message=\"{1}\"",
|
||||
159: "Device Powered On",
|
||||
160: "Enabled Duo two-factor authentication",
|
||||
161: "Disabled Duo two-factor authentication"
|
||||
161: "Disabled Duo two-factor authentication",
|
||||
162: "Started messenger session \"{0}\" from {1} to {2}"
|
||||
};
|
||||
|
||||
var eventsShortMessageId = {
|
||||
@ -17404,7 +17419,7 @@
|
||||
if (p52recordings != null) {
|
||||
var recdate = null;
|
||||
for (var i in p52recordings) {
|
||||
var rec = p52recordings[i], rect = new Date(rec.time), day = printDate(rect);
|
||||
var rec = p52recordings[i], rect = new Date(rec.startTime), day = printDate(rect);
|
||||
if (day != recdate) { recdate = day; x += '<tr><td class=userTableHeader colspan=4>' + day; }
|
||||
x += addRecordingHtml(i, rec);
|
||||
}
|
||||
@ -17422,7 +17437,7 @@
|
||||
function addRecordingHtml(i, rec) {
|
||||
var sessionLengthStr = '';
|
||||
if (rec.lengthTime) { sessionLengthStr = pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60)); }
|
||||
var sessionStartStr = printTime(new Date(rec.time));
|
||||
var sessionStartStr = printTime(new Date(rec.startTime));
|
||||
if (rec.sessionStart) { sessionStartStr = printTime(new Date(rec.sessionStart)); }
|
||||
var sessionSize = '';
|
||||
if (rec.size) { sessionSize = format("{0} Kb", Math.round(rec.size / 1024)); }
|
||||
@ -17484,6 +17499,7 @@
|
||||
if (rec.protocol == 5) { protocolStr = "Files"; }
|
||||
if (rec.protocol == 100) { protocolStr = "Intel® AMT WSMAN"; }
|
||||
if (rec.protocol == 101) { protocolStr = "Intel® AMT Redirection"; }
|
||||
if (rec.protocol == 200) { protocolStr = "Messenger"; }
|
||||
x += addHtmlValue4("Protocol", protocolStr);
|
||||
}
|
||||
x += addHtmlValue4("Status", (rec.present == 1)?"Present on server":"Not on server");
|
||||
@ -17491,7 +17507,7 @@
|
||||
if (rec.meshname) { x += addHtmlValue4("Device Group", EscapeHtml(rec.meshname)); }
|
||||
if (rec.size) { x += addHtmlValue4("Size", format("{0} bytes", rec.size)); }
|
||||
if (rec.startTime) { x += addHtmlValue4("Start Time", printTime(new Date(rec.startTime))); }
|
||||
if (rec.time) { x += addHtmlValue4("End Time", printTime(new Date(rec.time))); }
|
||||
if (rec.startTime && rec.lengthTime) { x += addHtmlValue4("End Time", printTime(new Date(rec.startTime + (rec.lengthTime * 1000)))); }
|
||||
if (rec.lengthTime) { x += addHtmlValue4("Duration", pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60))); }
|
||||
if (rec.multiplex == true) { x += addHtmlValue4("Multiplexor", "Enabled"); }
|
||||
if (rec.userids) { for (var i in rec.userids) { x += addHtmlValue4("User", rec.userids[i].split('/')[2]); } }
|
||||
@ -17661,7 +17677,8 @@
|
||||
x += '</div>';
|
||||
|
||||
x += '<div id=d2timeRangeDiv style=display:none>';
|
||||
x += addHtmlValue("Time Range", '<input id=d2timeRangeSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Time Range Start", '<input id=d2timeRangeStartSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlValue("Time Range End", '<input id=d2timeRangeEndSelector style=float:right;width:250px class=flatpickr type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += '</div>';
|
||||
|
||||
x += '<div id=d2showTrafficDiv style=display:none>';
|
||||
@ -17673,8 +17690,17 @@
|
||||
|
||||
var lastWeek = new Date();
|
||||
lastWeek.setDate(lastWeek.getDate() - 7);
|
||||
var rangeTime = flatpickr('#d2timeRangeSelector', { mode: 'range', enableTime: true, maxDate: new Date(), defaultDate: [ lastWeek, new Date() ], minuteIncrement: 1 });
|
||||
xxdialogTag = rangeTime;
|
||||
var rangeStartTime = flatpickr('#d2timeRangeStartSelector', {
|
||||
enableTime: true, maxDate: new Date(), defaultDate: lastWeek, minuteIncrement: 1, plugins: [new confirmDatePlugin({})],
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
rangeEndTime.set('minDate', dateStr);
|
||||
if (rangeEndTime.selectedDates[0] && rangeEndTime.selectedDates[0] < selectedDates[0]) {
|
||||
rangeEndTime.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
var rangeEndTime = flatpickr('#d2timeRangeEndSelector', { enableTime: true, maxDate: new Date(), defaultDate: new Date(), minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
xxdialogTag = { start: rangeStartTime, end: rangeEndTime };
|
||||
}
|
||||
|
||||
function generateReportDialogValidate() {
|
||||
@ -17689,8 +17715,8 @@
|
||||
function generateReportDialogEx(b, tag) {
|
||||
var start, end;
|
||||
if (Q('d2timeRange').value == 0) {
|
||||
end = Math.floor(tag.selectedDates[1].getTime() / 1000);
|
||||
start = Math.floor(tag.selectedDates[0].getTime() / 1000);
|
||||
end = Math.floor(tag.end.selectedDates[0].getTime() / 1000);
|
||||
start = Math.floor(tag.start.selectedDates[0].getTime() / 1000);
|
||||
} else {
|
||||
end = Math.floor(new Date() / 1000);
|
||||
start = new Date();
|
||||
|
||||
@ -5696,7 +5696,11 @@
|
||||
if (i > 0) { su = u.substring(i + 1); }
|
||||
su = EscapeHtml(su);
|
||||
if (su.length > 15) { su = su.substring(0, 14) + '…'; }
|
||||
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
||||
if (node.lusers && node.lusers.length > 0) {
|
||||
return addKeyLinkConditional(EscapeHtml(su), EscapeHtml(u) + " " + "(" + "Locked" + ")", (node.lusers && node.lusers.indexOf(u) >= 0));
|
||||
} else {
|
||||
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
|
||||
@ -6628,17 +6632,29 @@
|
||||
} else if (op == 107) {
|
||||
// Edit tags
|
||||
var x = "Perform batch device tag operation" + '<br /><br />';
|
||||
x += addHtmlFormFloating("Operation", '<select id=d2deviceop class="form-select-sm me-2"><option value=1>' + "Add tags" + '</option><option value=2>' + "Set tags" + '</option><option value=3>' + "Remove tags" + '</option></select>');
|
||||
x += addHtmlFormFloating("Tags", '<input id=dp10devicevalue class="form-control" maxlength=4096 placeholder="' + "Tag1, Tag2, Tag3" + '" />');
|
||||
x += addHtmlFormFloating("Operation", '<select id=d2deviceop class="form-select"><option value=1>' + "Add tags" + '</option><option value=2>' + "Set tags" + '</option><option value=3>' + "Remove tags" + '</option></select>');
|
||||
x += addHtmlFormFloating("Tags", '<select id=dp10devicevalue multiple class="form-control" maxlength=4096>');
|
||||
// Get a list of all possible device tags
|
||||
var allTags = [], y = '';
|
||||
for (var i in nodes) { if (nodes[i].tags) { for (var j in nodes[i].tags) { if (allTags.indexOf(nodes[i].tags[j]) == -1) { allTags.push(nodes[i].tags[j]); } } } }
|
||||
if (allTags.length > 0) {
|
||||
allTags.sort();
|
||||
for (var i in allTags) { y += '<span style=padding:4px;background-color:#BBB;border-radius:3px;cursor:pointer onclick=showEditNodeValueDialogAddTag("' + encodeURIComponentEx(allTags[i]) + '")>' + EscapeHtml(allTags[i]) + '</span> '; }
|
||||
x += '<div style=margin-top:8px;width:370px;line-height:26px;max-height:160px;overflow-y:auto>' + y + '</div>';
|
||||
for (var i in allTags) {
|
||||
var tag = EscapeHtml(allTags[i]);
|
||||
y += '<option>' + EscapeHtml(allTags[i]) + '</option>';
|
||||
}
|
||||
}
|
||||
x += y + '</select>'
|
||||
setModalContent('xxAddAgent', "Edit Device Tags", x);
|
||||
$('#dp10devicevalue').select2({
|
||||
theme: 'bootstrap-5',
|
||||
width: $( this ).data( 'width' ) ? $( this ).data( 'width' ) : $( this ).hasClass( 'w-100' ) ? '100%' : 'style',
|
||||
placeholder: "Tag1, Tag2, Tag3",
|
||||
closeOnSelect: false,
|
||||
allowClear: true,
|
||||
tokenSeparators: [','],
|
||||
tags: true
|
||||
});
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', () => d2groupActionFunctionTagsExec());
|
||||
} else if (op == 108) {
|
||||
// Device notification
|
||||
@ -6718,12 +6734,13 @@
|
||||
}
|
||||
|
||||
function d2groupActionFunctionTagsExec() {
|
||||
var chkNodeIds = getCheckedDevices(), op = Q('d2deviceop').value, optags = Q('dp10devicevalue').value;
|
||||
var chkNodeIds = getCheckedDevices(), op = Q('d2deviceop').value, optags = [];
|
||||
var tt = $('#dp10devicevalue').select2('data');
|
||||
for (var i in tt) { optags.push(tt[i]['text'].trim()); }
|
||||
if (op == 2) { // Set tags
|
||||
for (var i in chkNodeIds) { meshserver.send({ action: 'changedevice', nodeid: chkNodeIds[i], tags: optags }); }
|
||||
for (var i in chkNodeIds) { meshserver.send({ action: 'changedevice', nodeid: chkNodeIds[i], tags: optags.join(',') }); }
|
||||
} else {
|
||||
var taggroup = [];
|
||||
optags = optags.split(',');
|
||||
for (var i in optags) { var tname = optags[i].trim(); if ((tname.length > 0) && (tname.length < 64) && (taggroup.indexOf(tname) == -1)) { taggroup.push(tname); } }
|
||||
for (var i in chkNodeIds) {
|
||||
var nodeTags = null, tagChanges = false, n = getNodeFromId(chkNodeIds[i]);
|
||||
@ -9161,7 +9178,8 @@
|
||||
x += '<div id=d2modenow>';
|
||||
x += addHtmlFormFloating("Expire Time", '<select id=d2inviteExpire class="form-select">' + y + '</select>');
|
||||
x += '</div><div id=d2moderange style=display:none>';
|
||||
x += addHtmlFormFloating("Time Range", '<input id=d2timeRangeSelector class="flatpickr form-select" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlFormFloating("Time Range Start", '<input id=d2timeRangeStartSelector class="flatpickr form-select" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlFormFloating("Time Range End", '<input id=d2timeRangeEndSelector class="flatpickr form-select" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += '</div><div id=d2moderecurring style=display:none>';
|
||||
x += addHtmlFormFloating("Start Time", '<input id=d2timeStartSelector class="flatpickr form-select" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlFormFloating("Duration", '<select id=d2inviteDuration class="form-select">' + z + '</select>');
|
||||
@ -9172,14 +9190,24 @@
|
||||
|
||||
xxdialogButtons = 3;
|
||||
setModalContent('xxAddAgent', "Share Device", x);
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', showShareDeviceEx);
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', () => showShareDeviceEx('', xxdialogTag));
|
||||
|
||||
showShareDeviceValidate();
|
||||
var tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
var rangeTime = flatpickr('#d2timeRangeSelector', { mode: 'range', enableTime: true, minDate: new Date(), defaultDate: [new Date(), tomorrow], minuteIncrement: 1 });
|
||||
var startTime = flatpickr('#d2timeStartSelector', { enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1 });
|
||||
xxdialogTag = { range: rangeTime, start: startTime };
|
||||
var rangeStartTime = flatpickr('#d2timeRangeStartSelector', {
|
||||
enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1,
|
||||
plugins: [new confirmDatePlugin({})],
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
rangeEndTime.set('minDate', dateStr);
|
||||
if (rangeEndTime.selectedDates[0] && rangeEndTime.selectedDates[0] < selectedDates[0]) {
|
||||
rangeEndTime.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
var rangeEndTime = flatpickr('#d2timeRangeEndSelector', { enableTime: true, minDate: tomorrow, defaultDate: tomorrow, minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
var startTime = flatpickr('#d2timeStartSelector', { enableTime: true, minDate: new Date(), defaultDate: new Date(), minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
xxdialogTag = { rangeStart: rangeStartTime, rangeEnd: rangeEndTime, start: startTime };
|
||||
}
|
||||
|
||||
function showShareDeviceValidate() {
|
||||
@ -9222,7 +9250,7 @@
|
||||
if (Q('d2timeRange').value == 0) {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly });
|
||||
} else if (Q('d2timeRange').value == 1) {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.range.selectedDates[0].getTime() / 1000), end: Math.floor(tag.range.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly });
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.rangeStart.selectedDates[0].getTime() / 1000), end: Math.floor(tag.rangeEnd.selectedDates[0].getTime() / 1000), consent: consent, viewOnly: viewOnly });
|
||||
} else {
|
||||
meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.start.selectedDates[0].getTime() / 1000), expire: parseInt(Q('d2inviteDuration').value), recurring: Q('d2timeRange').value - 1, consent: consent, viewOnly: viewOnly });
|
||||
}
|
||||
@ -9815,13 +9843,12 @@
|
||||
if (v == null) v = '';
|
||||
if (mode == 3) {
|
||||
// Get a list of all possible device tags
|
||||
x = '<select id=dp10devicevalue multiple class="form-control" maxlength=' + showEditNodeValueDialog_modes4[mode] + ' class=form-control>';
|
||||
x = '<select id=dp10devicevalue multiple maxlength=' + showEditNodeValueDialog_modes4[mode] + ' class=form-control>';
|
||||
var allTags = [];
|
||||
for (var i in nodes) { if (nodes[i].tags) { for (var j in nodes[i].tags) { if (allTags.indexOf(nodes[i].tags[j]) == -1) { allTags.push(nodes[i].tags[j]); } } } }
|
||||
if (allTags.length > 0) {
|
||||
allTags.sort();
|
||||
for (var i in allTags) {
|
||||
var tag = EscapeHtml(allTags[i]);
|
||||
y += Array.isArray(v) && v.indexOf(allTags[i]) !== -1 ? '<option selected=selected>' + EscapeHtml(allTags[i]) + '</option>' : '<option>' + EscapeHtml(allTags[i]) + '</option>';
|
||||
}
|
||||
}
|
||||
@ -9841,24 +9868,12 @@
|
||||
setModalContent('xxAddAgent', "Edit Device", x);
|
||||
if (Array.isArray(v)) { v = v.join(', '); }
|
||||
Q('dp10devicevalue').value = v;
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', function () {
|
||||
showEditNodeValueDialogEx(3, mode);
|
||||
});
|
||||
}
|
||||
p10editdevicevalueValidate();
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', function () { showEditNodeValueDialogEx(3, mode); });
|
||||
Q('dp10devicevalue').focus();
|
||||
}
|
||||
|
||||
function showEditNodeValueDialogAddTag(t) {
|
||||
var tt = Q('dp10devicevalue').value.split(','), t2 = [];
|
||||
for (var i in tt) { t2.push(tt[i].trim()); }
|
||||
if (t2.indexOf(t) >= 0) return;
|
||||
Q('dp10devicevalue').value += ((Q('dp10devicevalue').value.length == 0) ? '' : ', ') + decodeURIComponent(t);
|
||||
setTimeout(function () { Q('dp10devicevalue').selectionStart = Q('dp10devicevalue').selectionEnd = 90000; }, 0);
|
||||
p10editdevicevalueValidate();
|
||||
}
|
||||
|
||||
function showEditNodeValueDialogEx(button, mode) {
|
||||
var tags = '';
|
||||
var x = { action: 'changedevice', nodeid: currentNode._id };
|
||||
@ -10506,7 +10521,7 @@
|
||||
var rdpflags = 0;
|
||||
for (var i = 1; i < 10; i++) { if ((i != 5) && (Q('d7rdp' + i).checked)) { rdpflags |= (1 << (i - 1)); } }
|
||||
desktopsettings.rdpflags = rdpflags;
|
||||
localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
|
||||
putstore('desktopsettings', JSON.stringify(desktopsettings));
|
||||
applyDesktopSettings();
|
||||
updateDesktopButtons();
|
||||
if (desktop) {
|
||||
@ -16510,7 +16525,8 @@
|
||||
158: "Displaying alert box, title=\"{0}\", message=\"{1}\"",
|
||||
159: "Device Powered On",
|
||||
160: "Enabled Duo two-factor authentication",
|
||||
161: "Disabled Duo two-factor authentication"
|
||||
161: "Disabled Duo two-factor authentication",
|
||||
162: "Started messenger session \"{0}\" from {1} to {2}"
|
||||
};
|
||||
|
||||
var eventsShortMessageId = {
|
||||
@ -18669,7 +18685,7 @@
|
||||
if (p52recordings != null) {
|
||||
var recdate = null;
|
||||
for (var i in p52recordings) {
|
||||
var rec = p52recordings[i], rect = new Date(rec.time), day = printDate(rect);
|
||||
var rec = p52recordings[i], rect = new Date(rec.startTime), day = printDate(rect);
|
||||
if (day != recdate) { recdate = day; x += '<tr><td class=userTableHeader colspan=4>' + day; }
|
||||
x += addRecordingHtml(i, rec);
|
||||
}
|
||||
@ -18687,7 +18703,7 @@
|
||||
function addRecordingHtml(i, rec) {
|
||||
var sessionLengthStr = '';
|
||||
if (rec.lengthTime) { sessionLengthStr = pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60)); }
|
||||
var sessionStartStr = printTime(new Date(rec.time));
|
||||
var sessionStartStr = printTime(new Date(rec.startTime));
|
||||
if (rec.sessionStart) { sessionStartStr = printTime(new Date(rec.sessionStart)); }
|
||||
var sessionSize = '';
|
||||
if (rec.size) { sessionSize = format("{0} Kb", Math.round(rec.size / 1024)); }
|
||||
@ -18749,6 +18765,7 @@
|
||||
if (rec.protocol == 5) { protocolStr = "Files"; }
|
||||
if (rec.protocol == 100) { protocolStr = "Intel® AMT WSMAN"; }
|
||||
if (rec.protocol == 101) { protocolStr = "Intel® AMT Redirection"; }
|
||||
if (rec.protocol == 200) { protocolStr = "Messenger"; }
|
||||
x += addHtmlValue4("Protocol", protocolStr);
|
||||
}
|
||||
x += addHtmlValue4("Status", (rec.present == 1) ? "Present on server" : "Not on server");
|
||||
@ -18756,7 +18773,7 @@
|
||||
if (rec.meshname) { x += addHtmlValue4("Device Group", EscapeHtml(rec.meshname)); }
|
||||
if (rec.size) { x += addHtmlValue4("Size", format("{0} bytes", rec.size)); }
|
||||
if (rec.startTime) { x += addHtmlValue4("Start Time", printTime(new Date(rec.startTime))); }
|
||||
if (rec.time) { x += addHtmlValue4("End Time", printTime(new Date(rec.time))); }
|
||||
if (rec.startTime && rec.lengthTime) { x += addHtmlValue4("End Time", printTime(new Date(rec.startTime + (rec.lengthTime * 1000)))); }
|
||||
if (rec.lengthTime) { x += addHtmlValue4("Duration", pad2(Math.floor(rec.lengthTime / 3600)) + ':' + pad2(Math.floor((rec.lengthTime % 3600) / 60)) + ':' + pad2(Math.floor(rec.lengthTime % 60))); }
|
||||
if (rec.multiplex == true) { x += addHtmlValue4("Multiplexor", "Enabled"); }
|
||||
if (rec.userids) { for (var i in rec.userids) { x += addHtmlValue4("User", rec.userids[i].split('/')[2]); } }
|
||||
@ -18929,7 +18946,8 @@
|
||||
x += '</div>';
|
||||
|
||||
x += '<div id=d2timeRangeDiv style=display:none>';
|
||||
x += addHtmlFormFloating("Time Range", '<input id=d2timeRangeSelector class="flatpickr form-control" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlFormFloating("Time Range Start", '<input id=d2timeRangeStartSelector class="flatpickr form-control" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += addHtmlFormFloating("Time Range End", '<input id=d2timeRangeEndSelector class="flatpickr form-control" type="text" placeholder="' + "Select Date & Time..." + '" data-id="altinput">');
|
||||
x += '</div>';
|
||||
|
||||
x += '<div id=d2showTrafficDiv style=display:none>';
|
||||
@ -18937,13 +18955,22 @@
|
||||
x += '</div>';
|
||||
|
||||
setModalContent('xxAddAgent', "Generate Report", x);
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', generateReportDialogEx);
|
||||
showModal('xxAddAgentModal', 'idx_dlgOkButton', () => generateReportDialogEx('', xxdialogTag));
|
||||
generateReportDialogValidate();
|
||||
|
||||
var lastWeek = new Date();
|
||||
lastWeek.setDate(lastWeek.getDate() - 7);
|
||||
var rangeTime = flatpickr('#d2timeRangeSelector', { mode: 'range', enableTime: true, maxDate: new Date(), defaultDate: [lastWeek, new Date()], minuteIncrement: 1 });
|
||||
xxdialogTag = rangeTime;
|
||||
var rangeStartTime = flatpickr('#d2timeRangeStartSelector', {
|
||||
enableTime: true, maxDate: new Date(), defaultDate: lastWeek, minuteIncrement: 1, plugins: [new confirmDatePlugin({})],
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
rangeEndTime.set('minDate', dateStr);
|
||||
if (rangeEndTime.selectedDates[0] && rangeEndTime.selectedDates[0] < selectedDates[0]) {
|
||||
rangeEndTime.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
var rangeEndTime = flatpickr('#d2timeRangeEndSelector', { enableTime: true, maxDate: new Date(), defaultDate: new Date(), minuteIncrement: 1, plugins: [new confirmDatePlugin({})] });
|
||||
xxdialogTag = { rangeStart: rangeStartTime, rangeEnd: rangeEndTime };
|
||||
}
|
||||
|
||||
function generateReportDialogValidate() {
|
||||
@ -18958,8 +18985,8 @@
|
||||
function generateReportDialogEx(b, tag) {
|
||||
var start, end;
|
||||
if (Q('d2timeRange').value == 0) {
|
||||
end = Math.floor(tag.selectedDates[1].getTime() / 1000);
|
||||
start = Math.floor(tag.selectedDates[0].getTime() / 1000);
|
||||
end = Math.floor(tag.rangeEnd.selectedDates[0].getTime() / 1000);
|
||||
start = Math.floor(tag.rangeStart.selectedDates[0].getTime() / 1000);
|
||||
} else {
|
||||
end = Math.floor(new Date() / 1000);
|
||||
start = new Date();
|
||||
|
||||
@ -403,7 +403,8 @@
|
||||
else if (p == 2) { p = "MeshCentral Desktop"; }
|
||||
else if (p == 100) { p = "Intel® AMT WSMAN"; }
|
||||
else if (p == 101) { p = "Intel® AMT Redirection"; }
|
||||
else if (p == 200) { p = "Intel® AMT KVM"; }
|
||||
else if ((p == 102) || (p == 200 && recFileMetadata.bpp != null)) { p = "Intel® AMT KVM"; }
|
||||
else if (p == 200) { p = "MeshMessenger"; }
|
||||
x += addInfoNoEsc("Protocol", p);
|
||||
}
|
||||
var encQualityStr = "2 byte-per-pixel";
|
||||
@ -469,9 +470,9 @@
|
||||
amtDesktop.Start();
|
||||
deskAdjust();
|
||||
}
|
||||
else if (recFileMetadata.protocol == 200) {
|
||||
else if (recFileMetadata.protocol == 102 || (recFileMetadata.protocol == 200 && recFileMetadata.bpp != null)) {
|
||||
// Intel AMT Midstream KVM
|
||||
recFileProtocol = 200;
|
||||
recFileProtocol = 102;
|
||||
x += '<br /><br /><span style=color:gray>' + "Press [space] to play/pause." + '</span>';
|
||||
QE('PlayButton', true);
|
||||
QE('PauseButton', false);
|
||||
@ -576,14 +577,14 @@
|
||||
var view = new Uint8Array(data.length);
|
||||
for (var i = 0; i < data.length; i++) { view[i] = data.charCodeAt(i); }
|
||||
|
||||
if ((readState == 0) && (rstr2hex(data) == '4100000000000000')) {
|
||||
if ((readState == 0) && (rstr2hex(data).startsWith('4100000000000000'))) {
|
||||
// We are not authenticated, KVM data starts here.
|
||||
readState = 1;
|
||||
if (data.length > 8) { amtDesktop.ProcessBinaryData(view.slice(8).buffer); }
|
||||
} else if (readState == 1) {
|
||||
amtDesktop.ProcessBinaryData(view.buffer);
|
||||
}
|
||||
} else if (recFileProtocol == 200) {
|
||||
} else if (recFileProtocol == 102) {
|
||||
// Intel AMT midstream KVM
|
||||
var view = new Uint8Array(data.length);
|
||||
for (var i = 0; i < data.length; i++) { view[i] = data.charCodeAt(i); }
|
||||
@ -600,7 +601,7 @@
|
||||
}
|
||||
|
||||
// This is a PNG screenshot of the display, load it and render it.
|
||||
if ((type == 3) && (recFileProtocol == 200)) {
|
||||
if ((type == 3) && (recFileProtocol == 102)) {
|
||||
var tile = new Image();
|
||||
tile.src = "data:image/png;base64," + btoa(data);
|
||||
tile.onload = function () { amtDesktop.canvas.drawImage(tile, 0, 0); }
|
||||
@ -836,7 +837,7 @@
|
||||
amtDesktop.onScreenSizeChange = deskAdjust;
|
||||
amtDesktop.State = 3;
|
||||
amtDesktop.Start();
|
||||
if (recFileMetadata.protocol == 200) {
|
||||
if (recFileMetadata.protocol == 102) {
|
||||
if (recFileMetadata.screenSize) { amtDesktop.width = recFileMetadata.screenSize[0]; amtDesktop.height = recFileMetadata.screenSize[1]; }
|
||||
if (recFileMetadata.bpp) { amtDesktop.bpp = recFileMetadata.bpp; }
|
||||
if (recFileMetadata.graymode) { amtDesktop.graymode = recFileMetadata.graymode; }
|
||||
|
||||
266
webserver.js
266
webserver.js
@ -4094,7 +4094,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
if (domain == null) return;
|
||||
|
||||
// Check the query
|
||||
if ((domain.sessionrecording == null) || (req.query.file == null) || (obj.common.IsFilenameValid(req.query.file) !== true) || (req.query.file.endsWith('.mcrec') == false)) { res.sendStatus(401); return; }
|
||||
if ((domain.sessionrecording == null) || (req.query.file == null) || (obj.common.IsFilenameValid(req.query.file) !== true) || (!req.query.file.endsWith('.mcrec') && !req.query.file.endsWith('.txt'))) { res.sendStatus(401); return; }
|
||||
|
||||
// Get the recording path
|
||||
var recordingsPath = null;
|
||||
@ -4727,8 +4727,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
// Fetch information about the target
|
||||
obj.db.Get(req.query.host, function (err, docs) {
|
||||
if (docs.length == 0) { console.log('ERR: Node not found'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
|
||||
var node = docs[0];
|
||||
var xusername = '', xdevicename = '', xdevicename2 = null, node = null;
|
||||
node = docs[0]; xdevicename2 = node.name; xdevicename = '-' + parent.common.makeFilename(node.name); ws.id = getRandomPassword(); ws.time = Date.now();
|
||||
if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
|
||||
var ciraconn = parent.mpsserver.GetConnectionToNode(req.query.host, null, false);
|
||||
|
||||
// Check if this user has permission to manage this computer
|
||||
if ((obj.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (3)'); try { ws.close(); } catch (e) { } return; }
|
||||
@ -4782,7 +4784,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
|
||||
if (record == true) {
|
||||
var now = new Date(Date.now());
|
||||
var recFilename = 'relaysession' + ((domain.id == '') ? '' : '-') + domain.id + '-' + now.getUTCFullYear() + '-' + obj.common.zeroPad(now.getUTCMonth() + 1, 2) + '-' + obj.common.zeroPad(now.getUTCDate(), 2) + '-' + obj.common.zeroPad(now.getUTCHours(), 2) + '-' + obj.common.zeroPad(now.getUTCMinutes(), 2) + '-' + obj.common.zeroPad(now.getUTCSeconds(), 2) + '-' + getRandomPassword() + '.mcrec'
|
||||
// Get the username and make it acceptable as a filename
|
||||
if (user._id) { xusername = '-' + parent.common.makeFilename(user._id.split('/')[2]); }
|
||||
var xsessionid = ws.id;
|
||||
var recFilename = 'relaysession' + ((domain.id == '') ? '' : '-') + domain.id + '-' + now.getUTCFullYear() + '-' + obj.common.zeroPad(now.getUTCMonth() + 1, 2) + '-' + obj.common.zeroPad(now.getUTCDate(), 2) + '-' + obj.common.zeroPad(now.getUTCHours(), 2) + '-' + obj.common.zeroPad(now.getUTCMinutes(), 2) + '-' + obj.common.zeroPad(now.getUTCSeconds(), 2) + xusername + xdevicename + '-' + xsessionid + '.mcrec';
|
||||
var recFullFilename = null;
|
||||
if (domain.sessionrecording.filepath) {
|
||||
try { obj.fs.mkdirSync(domain.sessionrecording.filepath); } catch (e) { }
|
||||
@ -4794,16 +4799,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
var fd = obj.fs.openSync(recFullFilename, 'w');
|
||||
if (fd != null) {
|
||||
// Write the recording file header
|
||||
var firstBlock = JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, userid: user._id, username: user.name, ipaddr: req.clientIp, nodeid: node._id, intelamt: true, protocol: (req.query.p == 2) ? 101 : 100, time: new Date().toLocaleString() })
|
||||
recordingEntry(fd, 1, 0, firstBlock, function () { });
|
||||
ws.logfile = { fd: fd, lock: false };
|
||||
parent.debug('relay', 'Relay: Started recording to file: ' + recFullFilename);
|
||||
var metadata = {
|
||||
magic: 'MeshCentralRelaySession',
|
||||
ver: 1,
|
||||
userid: user._id,
|
||||
username: user.name,
|
||||
sessionid: ws.id,
|
||||
ipaddr1: req.clientIp,
|
||||
time: new Date().toLocaleString(),
|
||||
protocol: (req.query.p == 2) ? 101 : 100,
|
||||
nodeid: node._id,
|
||||
intelamt: true
|
||||
};
|
||||
if (ciraconn != null) { metadata.ipaddr2 = ciraconn.remoteAddr; }
|
||||
else if ((conn & 4) != 0) { metadata.ipaddr2 = node.host; }
|
||||
if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
|
||||
var firstBlock = JSON.stringify(metadata)
|
||||
ws.logfile = { fd: fd, lock: false, filename: recFullFilename, startTime: Date.now(), size: 0, text: 0, req: req };
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 1, 0, firstBlock, function () { });
|
||||
if (node != null) { ws.logfile.nodeid = node._id; ws.logfile.meshid = node.meshid; ws.logfile.name = node.name; ws.logfile.icon = node.icon; }
|
||||
if (req.query.p == 2) { ws.send(Buffer.from(String.fromCharCode(0xF0), 'binary')); } // Intel AMT Redirection: Indicate the session is being recorded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If Intel AMT CIRA connection is available, use it
|
||||
var ciraconn = parent.mpsserver.GetConnectionToNode(req.query.host, null, false);
|
||||
if (ciraconn != null) {
|
||||
parent.debug('web', 'Opening relay CIRA channel connection to ' + req.query.host + '.');
|
||||
|
||||
@ -4833,8 +4854,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
if (state == 0) { try { ws.close(); } catch (e) { } }
|
||||
if (state == 2) {
|
||||
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
|
||||
const tlsoptions = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
|
||||
if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
const tlsoptions = { minVersion: 'TLSv1', socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
var tlsock = obj.tls.connect(tlsoptions, function () { parent.debug('webrelay', "CIRA Secure TLS Connection"); ws._socket.resume(); });
|
||||
tlsock.chnl = chnl;
|
||||
tlsock.setEncoding('binary');
|
||||
@ -4865,7 +4886,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
try { ws.send(data); } catch (e) { }
|
||||
} else {
|
||||
// Log to recording file
|
||||
recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -4897,7 +4918,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
try { ws.send(data); } catch (e) { }
|
||||
} else {
|
||||
// Log to recording file
|
||||
recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } });
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } });
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -4921,7 +4942,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
try { ws.forwardclient.write(data); } catch (ex) { }
|
||||
} else {
|
||||
// Log to recording file
|
||||
recordingEntry(ws.logfile.fd, 2, 2, data, function () { try { ws.forwardclient.write(data); } catch (ex) { } });
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 2, data, function () { try { ws.forwardclient.write(data); } catch (ex) { } });
|
||||
}
|
||||
});
|
||||
|
||||
@ -4930,6 +4951,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
console.log('CIRA server websocket error from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
|
||||
parent.debug('webrelay', 'Websocket relay closed on error.');
|
||||
|
||||
// Log the disconnection
|
||||
if (ws.time) {
|
||||
if (req.query.p == 2) { // Only log event if Intel Redirection, otherwise hundreds of logs for WSMAN are recorded
|
||||
var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
|
||||
if (user) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: 101, nodeid: node._id };
|
||||
obj.parent.DispatchEvent(['*', user._id, node._id, node.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Websocket closed, close the CIRA channel and TLS session.
|
||||
if (ws.forwardclient) {
|
||||
if (ws.forwardclient.close) { ws.forwardclient.close(); } // NonTLS, close the CIRA channel
|
||||
@ -4939,13 +4971,49 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
}
|
||||
|
||||
// Close the recording file
|
||||
if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); delete ws.logfile; }, ws); }
|
||||
if (ws.logfile != null) {
|
||||
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
|
||||
obj.fs.close(logfile.fd);
|
||||
parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000) - 5; }
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.path.basename(ws.logfile.filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
|
||||
if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
|
||||
var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
|
||||
if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
|
||||
var mesh = obj.meshes[ws.logfile.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
|
||||
if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
|
||||
if (ws.logfile.name) { event.name = ws.logfile.name; }
|
||||
if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
|
||||
obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
|
||||
delete ws.logfile;
|
||||
}, ws);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
// If the web socket is closed, close the associated TCP connection.
|
||||
ws.on('close', function (req) {
|
||||
ws.on('close', function () {
|
||||
parent.debug('webrelay', 'Websocket relay closed.');
|
||||
|
||||
// Log the disconnection
|
||||
if (ws.time) {
|
||||
if (req.query.p == 2) { // Only log event if Intel Redirection, otherwise hundreds of logs for WSMAN are recorded
|
||||
var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
|
||||
var nodeid = node._id;
|
||||
var meshid = node.meshid;
|
||||
if (user) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: nodeid };
|
||||
obj.parent.DispatchEvent(['*', user._id, nodeid, meshid], obj, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Websocket closed, close the CIRA channel and TLS session.
|
||||
if (ws.forwardclient) {
|
||||
if (ws.forwardclient.close) { ws.forwardclient.close(); } // NonTLS, close the CIRA channel
|
||||
@ -4955,7 +5023,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
}
|
||||
|
||||
// Close the recording file
|
||||
if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); delete ws.logfile; }, ws); }
|
||||
if (ws.logfile != null) {
|
||||
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
|
||||
obj.fs.close(logfile.fd);
|
||||
parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000) - 5; }
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.path.basename(ws.logfile.filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
|
||||
if (user) { event.userids = [user._id]; }
|
||||
var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
|
||||
if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
|
||||
var mesh = obj.meshes[ws.logfile.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
|
||||
if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
|
||||
if (ws.logfile.name) { event.name = ws.logfile.name; }
|
||||
if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
|
||||
obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
|
||||
delete ws.logfile;
|
||||
}, ws);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
// Note that here, req.query.p: 1 = WSMAN with server auth, 2 = REDIR with server auth, 3 = WSMAN without server auth, 4 = REDIR with server auth
|
||||
@ -4970,12 +5061,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass });
|
||||
ws.interceptor.blockAmtStorage = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If Intel AMT direct connection is possible, option a direct socket
|
||||
if ((conn & 4) != 0) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
|
||||
} else if ((conn & 4) != 0) { // If Intel AMT direct connection is possible, option a direct socket
|
||||
// We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
|
||||
parent.debug('webrelay', 'Opening relay TCP socket connection to ' + req.query.host + '.');
|
||||
|
||||
// When data is received from the web socket, forward the data into the associated TCP connection.
|
||||
@ -4991,7 +5078,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
try { ws.forwardclient.write(msg); } catch (ex) { }
|
||||
} else {
|
||||
// Log to recording file
|
||||
recordingEntry(ws.logfile.fd, 2, 2, msg, function () { try { ws.forwardclient.write(msg); } catch (ex) { } });
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 2, msg, function () { try { ws.forwardclient.write(msg); } catch (ex) { } });
|
||||
}
|
||||
});
|
||||
|
||||
@ -4999,28 +5086,80 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
ws.on('error', function (err) {
|
||||
console.log('Error with relay web socket connection from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
|
||||
parent.debug('webrelay', 'Error with relay web socket connection from ' + req.clientIp + '.');
|
||||
// Log the disconnection
|
||||
if (ws.time) {
|
||||
var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
|
||||
if (user) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: node._id };
|
||||
obj.parent.DispatchEvent(['*', user._id, node._id, node.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
|
||||
|
||||
// Close the recording file
|
||||
if (ws.logfile != null) {
|
||||
recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) {
|
||||
obj.fs.close(fd);
|
||||
ws.logfile = null;
|
||||
});
|
||||
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
|
||||
obj.fs.close(logfile.fd);
|
||||
parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.path.basename(ws.logfile.filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
|
||||
if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
|
||||
var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
|
||||
if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
|
||||
var mesh = obj.meshes[ws.logfile.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
|
||||
if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
|
||||
if (ws.logfile.name) { event.name = ws.logfile.name; }
|
||||
if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
|
||||
obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
|
||||
delete ws.logfile;
|
||||
}, ws);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
// If the web socket is closed, close the associated TCP connection.
|
||||
ws.on('close', function () {
|
||||
parent.debug('webrelay', 'Closing relay web socket connection to ' + req.query.host + '.');
|
||||
// Log the disconnection
|
||||
if (ws.time) {
|
||||
var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
|
||||
if (user) {
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: node._id };
|
||||
obj.parent.DispatchEvent(['*', user._id, node._id, node.meshid], obj, event);
|
||||
}
|
||||
}
|
||||
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
|
||||
|
||||
// Close the recording file
|
||||
if (ws.logfile != null) {
|
||||
recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) {
|
||||
obj.fs.close(fd);
|
||||
ws.logfile = null;
|
||||
});
|
||||
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
|
||||
obj.fs.close(logfile.fd);
|
||||
parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
|
||||
// Compute session length
|
||||
var sessionLength = null;
|
||||
if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
|
||||
// Add a event entry about this recording
|
||||
var basefile = parent.path.basename(ws.logfile.filename);
|
||||
var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
|
||||
if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
|
||||
var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
|
||||
if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
|
||||
var mesh = obj.meshes[ws.logfile.meshid];
|
||||
if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
|
||||
if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
|
||||
if (ws.logfile.name) { event.name = ws.logfile.name; }
|
||||
if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
|
||||
obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
|
||||
delete ws.logfile;
|
||||
}, ws);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
@ -5038,8 +5177,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
ws._socket.resume();
|
||||
} else {
|
||||
// If TLS is going to be used, setup a TLS socket
|
||||
var tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
var tlsoptions = { minVersion: 'TLSv1', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
|
||||
// if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
|
||||
ws.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
|
||||
// The TLS connection method is the same as TCP, but located a bit differently.
|
||||
parent.debug('webrelay', user.name + ' - TLS connected to ' + node.host + ':' + port + '.');
|
||||
@ -5064,7 +5203,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
try { ws.send(data); } catch (e) { }
|
||||
} else {
|
||||
// Log to recording file
|
||||
recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (e) { } });
|
||||
obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (e) { } });
|
||||
}
|
||||
});
|
||||
|
||||
@ -5092,9 +5231,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
ws._socket.resume();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the connection
|
||||
if (user != null) {
|
||||
if (req.query.p == 2) { // Only log event if Intel Redirection, otherwise hundreds of logs for WSMAN are recorded
|
||||
var msg = 'Started relay session', msgid = 13, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
|
||||
var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip, protocol: 101, nodeid: node._id };
|
||||
obj.parent.DispatchEvent(['*', user._id], obj, event);
|
||||
}
|
||||
|
||||
// Update user last access time
|
||||
if ((user != null)) {
|
||||
const timeNow = Math.floor(Date.now() / 1000);
|
||||
if (user.access < (timeNow - 300)) { // Only update user access time if longer than 5 minutes
|
||||
user.access = timeNow;
|
||||
obj.parent.db.SetUser(user);
|
||||
|
||||
// Event the change
|
||||
var message = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', domain: domain.id, nolog: 1 };
|
||||
if (parent.db.changeStream) { message.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
|
||||
var targets = ['*', 'server-users', user._id];
|
||||
if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
|
||||
obj.parent.DispatchEvent(targets, obj, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -6557,13 +6719,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
extraFrameSrc = ' https://' + req.headers.host + ':' + parent.webrelayserver.port;
|
||||
if ((xforwardedhost != null) && (xforwardedhost != req.headers.host)) { extraFrameSrc += ' https://' + xforwardedhost + ':' + parent.webrelayserver.port; }
|
||||
}
|
||||
|
||||
|
||||
// If using duo add apihostname to CSP
|
||||
var duoSrc = '';
|
||||
if ((typeof domain.duo2factor == 'object') && (typeof domain.duo2factor.apihostname == 'string')) {
|
||||
duoSrc = domain.duo2factor.apihostname;
|
||||
}
|
||||
|
||||
// Finish setup security headers
|
||||
const headers = {
|
||||
'Referrer-Policy': 'no-referrer',
|
||||
'X-XSS-Protection': '1; mode=block',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'Content-Security-Policy': "default-src 'none'; font-src 'self' fonts.gstatic.com data:; script-src 'self' 'unsafe-inline' " + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-src 'self' blob: mcrouter:" + extraFrameSrc + "; media-src 'self'; form-action 'self'; manifest-src 'self'"
|
||||
'Content-Security-Policy': "default-src 'none'; font-src 'self' fonts.gstatic.com data:; script-src 'self' 'unsafe-inline' " + extraScriptSrc + "; connect-src 'self'" + geourl + selfurl + "; img-src 'self' blob: data:" + geourl + " data:; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-src 'self' blob: mcrouter:" + extraFrameSrc + "; media-src 'self'; form-action 'self' " + duoSrc + "; manifest-src 'self'"
|
||||
};
|
||||
if (req.headers['user-agent'] && (req.headers['user-agent'].indexOf('Chrome') >= 0)) { headers['Permissions-Policy'] = 'interest-cohort=()'; } // Remove Google's FLoC Network, only send this if Chrome browser
|
||||
if ((parent.config.settings.allowframing !== true) && (typeof parent.config.settings.allowframing !== 'string')) { headers['X-Frame-Options'] = 'sameorigin'; }
|
||||
@ -9569,7 +9738,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
// Generate a random Intel AMT password
|
||||
function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
|
||||
function getRandomAmtPassword() { var p; do { p = Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; }
|
||||
function getRandomPassword() { return Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); }
|
||||
function getRandomPassword() { return Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); }
|
||||
function getRandomLowerCase(len) { var r = '', random = obj.crypto.randomBytes(len); for (var i = 0; i < len; i++) { r += String.fromCharCode(97 + (random[i] % 26)); } return r; }
|
||||
|
||||
// Generate a 8 digit integer with even random probability for each value.
|
||||
@ -9594,31 +9763,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
||||
}
|
||||
}
|
||||
|
||||
// Record a new entry in a recording log
|
||||
function recordingEntry(fd, type, flags, data, func, tag) {
|
||||
try {
|
||||
if (typeof data == 'string') {
|
||||
// String write
|
||||
var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
|
||||
header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(blockData.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, blockData]);
|
||||
obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
} else {
|
||||
// Binary write
|
||||
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
|
||||
header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
|
||||
header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
|
||||
header.writeInt32BE(data.length, 4); // Size
|
||||
header.writeIntBE(new Date(), 10, 6); // Time
|
||||
var block = Buffer.concat([header, data]);
|
||||
obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
|
||||
}
|
||||
} catch (ex) { console.log(ex); func(fd, tag); }
|
||||
}
|
||||
|
||||
// Perform a IP match against a list
|
||||
function isIPMatch(ip, matchList) {
|
||||
const ipcheck = require('ipcheck');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user