Added discovery key support.
Ylian Saint-Hilaire committed
Jul 14, 2020 at 15:39 UTC
f84220a7f44a441e1b00f570108d9cc3f7d16fc5
3 files changed
+55
-18
db.js
+1
-1
@@ -660,7 +660,7 @@ module.exports.CreateDB = function (parent, func) {
660
// If a DB encryption key is provided, perform database encryption
661
if ((typeof parent.args.dbencryptkey == 'string') && (parent.args.dbencryptkey.length != 0)) {
662
// Hash the database password into a AES256 key and setup encryption and decryption.
663
- obj.dbKey = parent.crypto.createHash('sha384').update(parent.args.dbencryptkey).digest("raw").slice(0, 32);
663
+ obj.dbKey = parent.crypto.createHash('sha384').update(parent.args.dbencryptkey).digest('raw').slice(0, 32);
664
datastoreOptions.afterSerialization = function (plaintext) {
665
const iv = parent.crypto.randomBytes(16);
666
const aes = parent.crypto.createCipheriv('aes-256-cbc', obj.dbKey, iv);
meshcentral-config-schema.json
+2
-2
@@ -18,7 +18,6 @@
18
"sessionKey": { "type": "string" },
19
"sessionSameSite": { "type": "string" },
20
"dbEncryptKey": { "type": "string" },
21
- "multicastKey": { "type": "string", "description": "When set, encrypts all multicast LAN traffic to agents using this key. This is only useful in LAN mode when agents and tools will multicast to find the server." },
21
"dbRecordsEncryptKey": { "type": "string" },
22
"dbRecordsDecryptKey": { "type": "string" },
23
"dbExpire": {
@@ -75,7 +74,8 @@
74
"additionalProperties": false,
75
"properties": {
76
"name": { "type": "string" },
78
- "info": { "type": "string" }
77
+ "info": { "type": "string" },
78
+ "key": { "type": "string", "description": "When set, encrypts all LAN discovery traffic to agents and tools using this key. This is only useful in LAN/Hybrid mode when agents and tools user multicast to find the server." }
79
},
80
"required": [ "name", "info" ]
81
},
meshscanner.js
+52
-15
@@ -29,6 +29,31 @@ module.exports.CreateMeshScanner = function (parent) {
29
obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }).toUpperCase();
30
obj.error = 0;
31
32
+ // Setup the multicast key if present
33
+ if ((typeof obj.parent.args.localdiscovery == 'object') && (typeof obj.parent.args.localdiscovery.key == 'string') && (obj.parent.args.localdiscovery.key.length > 0)) {
34
+ obj.multicastKey = parent.crypto.createHash('sha384').update(obj.parent.args.localdiscovery.key).digest('raw').slice(0, 32);
35
+ }
36
+
37
+ // Encrypt UDP packet
38
+ function encryptPacket(plainPacket) {
39
+ if (obj.multicastKey == null) { return plainPacket; }
40
+ const iv = parent.crypto.randomBytes(16), aes = parent.crypto.createCipheriv('aes-256-cbc', obj.multicastKey, iv);
41
+ var ciphertext = aes.update(plainPacket);
42
+ return Buffer.concat([iv, ciphertext, aes.final()]);
43
+ }
44
+
45
+ // Decrypt UDP packet
46
+ function decryptPacket(packet) {
47
+ if (obj.multicastKey == null) { return packet; }
48
+ if (packet.length < 17) { return null; }
49
+ try {
50
+ const iv = packet.slice(0, 16), data = packet.slice(16);
51
+ const aes = parent.crypto.createDecipheriv('aes-256-cbc', obj.multicastKey, iv);
52
+ var plaintextBytes = Buffer.from(aes.update(data));
53
+ return Buffer.concat([plaintextBytes, aes.final()]);
54
+ } catch (ex) { return null; }
55
+ }
56
+
57
// Get a list of IPv4 and IPv6 interface addresses
58
function getInterfaceList() {
59
var i;
@@ -131,11 +156,20 @@ module.exports.CreateMeshScanner = function (parent) {
156
157
// Setup the local discovery values
158
var name = 'MeshCentral';
134
- try { name = obj.parent.config.domains[''].title; } catch (ex) { }
135
- try { name = obj.parent.args.localdiscovery.name; } catch (ex) { }
136
- var info = 'Beta2';
137
- try { info = obj.parent.config.domains[''].title2; } catch (ex) { }
138
- try { info = obj.parent.args.localdiscovery.info; } catch (ex) { }
159
+ var info = '';
160
+ try {
161
+ if ((typeof obj.parent.config.domains[''].title == 'string') && (obj.parent.config.domains[''].title.length > 0)) {
162
+ name = obj.parent.config.domains[''].title; info = '';
163
+ try { if ((typeof obj.parent.config.domains[''].title2 == 'string') && (obj.parent.config.domains[''].title2.length > 0)) { info = obj.parent.config.domains[''].title2; } } catch (ex) { }
164
+ }
165
+ } catch (ex) { }
166
+ try {
167
+ if ((typeof obj.parent.args.localdiscovery.name == 'string') && (obj.parent.args.localdiscovery.name.length > 0)) {
168
+ name = obj.parent.args.localdiscovery.name; info = '';
169
+ try { if ((typeof obj.parent.args.localdiscovery.info == 'string') && (obj.parent.args.localdiscovery.info.length > 0)) { info = obj.parent.args.localdiscovery.info; } } catch (ex) { }
170
+ }
171
+ } catch (ex) { }
172
+ if (info == '') { info = parent.certificates.CommonName; }
173
174
// Figure out the correct websocket port
175
var port = (parent.args.aliasport)?parent.args.aliasport:parent.args.port;
@@ -167,26 +201,29 @@ module.exports.CreateMeshScanner = function (parent) {
201
obj.performScan = function (server) {
202
var i;
203
if (server != null) {
170
- if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } }
171
- if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } }
172
- if ((server.xxtype == 4) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } catch (e) { } }
173
- if ((server.xxtype == 6) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } catch (e) { } }
204
+ if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p.length, 16990, membershipIPv4); } catch (e) { } }
205
+ if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p.length, 16990, membershipIPv6); } catch (e) { } }
206
+ if ((server.xxtype == 4) && (server.xxlocal == '*')) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(p, 0, p.length, 16990, '255.255.255.255'); } catch (e) { } }
207
+ if ((server.xxtype == 6) && (server.xxlocal == '*')) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p.length, 16990, '::1'); } catch (e) { } }
208
} else {
175
- for (i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } }
176
- for (i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } }
209
+ for (i in obj.servers4) { var p = encryptPacket(obj.multicastPacket4); try { obj.servers4[i].send(p, 0, p.length, 16990, membershipIPv4); } catch (e) { } }
210
+ for (i in obj.servers6) { var p = encryptPacket(obj.multicastPacket6); try { obj.servers6[i].send(p, 0, p.length, 16990, membershipIPv6); } catch (e) { } }
211
setupServers(); // Check if any network interfaces where added or removed
212
}
213
};
214
215
// Called when a UDP packet is received from an agent.
216
function onUdpPacket(msg, info, server) {
217
+ // Decrypt the packet if needed
218
+ if ((msg = decryptPacket(msg)) == null) return;
219
+
220
//console.log('Received ' + msg.length + ' bytes from ' + info.address + ':' + info.port + ', on interface: ' + server.xxlocal + '.');
221
if ((msg.length == 96) && (msg.toString('ascii') == obj.agentCertificateHashHex)) {
185
- if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, info.port, info.address); } catch (e) { } }
186
- if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, info.port, info.address); } catch (e) { } }
222
+ if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p, info.port, info.address); } catch (e) { } }
223
+ if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p, info.port, info.address); } catch (e) { } }
224
} else if (msg.toString('ascii') == 'MeshServerScan') {
188
- if (server.xxtype == 4) { try { server.send(obj.multicastPacket4x, 0, obj.multicastPacket4x.length, info.port, info.address); } catch (e) { } }
189
- if (server.xxtype == 6) { try { server.send(obj.multicastPacket6x, 0, obj.multicastPacket6x.length, info.port, info.address); } catch (e) { } }
225
+ if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4x); try { server.send(p, 0, p.length, info.port, info.address); } catch (e) { } }
226
+ if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6x); try { server.send(p, 0, p.length, info.port, info.address); } catch (e) { } }
227
}
228
}
229