Added CloudFlare Support, improved reverse-proxy HTTP header handling.
Ylian Saint-Hilaire committed
May 21, 2020 at 21:25 UTC
31e1f61e524b8d04b1a246d994e21540f6454579
5 files changed
+113
-100
meshagent.js
+2
-2
@@ -19,14 +19,14 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
19
const forge = parent.parent.certificateOperations.forge;
20
const common = parent.parent.common;
21
parent.agentStats.createMeshAgentCount++;
22
- parent.parent.debug('agent', 'New agent at ' + req.ip + ':' + ws._socket.remotePort);
22
+ parent.parent.debug('agent', 'New agent at ' + req.clientIp + ':' + ws._socket.remotePort);
23
24
var obj = {};
25
obj.domain = domain;
26
obj.authenticated = 0;
27
obj.receivedCommands = 0;
28
obj.agentCoreCheck = 0;
29
- obj.remoteaddr = (req.ip.startsWith('::ffff:')) ? (req.ip.substring(7)) : req.ip;
29
+ obj.remoteaddr = req.clientIp;
30
obj.remoteaddrport = obj.remoteaddr + ':' + ws._socket.remotePort;
31
obj.nonce = parent.crypto.randomBytes(48).toString('binary');
32
//ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive, 4 minutes
meshdesktopmultiplex.js
+8
-8
@@ -815,7 +815,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
815
}
816
817
// If there is no authentication, drop this connection
818
- if ((obj.id != null) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Connection with no authentication (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } return; }
818
+ if ((obj.id != null) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Connection with no authentication (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } return; }
819
820
// Relay session count (we may remove this in the future)
821
obj.relaySessionCounted = true;
@@ -848,8 +848,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
848
if (obj.ws == null) return; // Already closed.
849
850
// Close the connection
851
- if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Soft disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
852
- if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'DesktopRelay: Hard disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
851
+ if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'DesktopRelay: Soft disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
852
+ if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'DesktopRelay: Hard disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
853
if (obj.relaySessionCounted) { parent.relaySessionCount--; delete obj.relaySessionCounted; }
854
if (obj.deskMultiplexor != null) { if (obj.deskMultiplexor.removePeer(obj) == true) { delete parent.desktoprelays[obj.nodeid]; } }
855
@@ -948,7 +948,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
948
if ((typeof parent.parent.args.agentping == 'number') && (obj.pingtimer == null)) { obj.pingtimer = setInterval(sendPing, parent.parent.args.agentping * 1000); }
949
else if ((typeof parent.parent.args.agentpong == 'number') && (obj.pongtimer == null)) { obj.pongtimer = setInterval(sendPong, parent.parent.args.agentpong * 1000); }
950
951
- parent.parent.debug('relay', 'DesktopRelay: Connection (' + cleanRemoteAddr(obj.req.ip) + ')');
951
+ parent.parent.debug('relay', 'DesktopRelay: Connection (' + obj.req.clientIp + ')');
952
}
953
954
// Create if needed and add this peer to the desktop multiplexor
@@ -991,7 +991,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
991
ws.on('error', function (err) {
992
//console.log('ws-error', err);
993
parent.relaySessionErrorCount++;
994
- console.log('Relay error from ' + cleanRemoteAddr(obj.req.ip) + ', ' + err.toString().split('\r')[0] + '.');
994
+ console.log('Relay error from ' + obj.req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
995
obj.close();
996
});
997
@@ -1020,7 +1020,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
1020
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
1021
const command = { nodeid: cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr };
1022
parent.parent.debug('relay', 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
1023
- if (obj.sendAgentMessage(command, user._id, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
1023
+ if (obj.sendAgentMessage(command, user._id, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
1024
performRelay(0);
1025
});
1026
return obj;
@@ -1040,11 +1040,11 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
1040
if (obj.req.query.tcpport != null) {
1041
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: obj.req.query.tcpport, tcpaddr: ((obj.req.query.tcpaddr == null) ? '127.0.0.1' : obj.req.query.tcpaddr) };
1042
parent.parent.debug('relay', 'Relay: Sending agent TCP tunnel command: ' + JSON.stringify(command));
1043
- if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
1043
+ if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
1044
} else if (obj.req.query.udpport != null) {
1045
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, udpport: obj.req.query.udpport, udpaddr: ((obj.req.query.udpaddr == null) ? '127.0.0.1' : obj.req.query.udpaddr) };
1046
parent.parent.debug('relay', 'Relay: Sending agent UDP tunnel command: ' + JSON.stringify(command));
1047
- if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
1047
+ if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
1048
}
1049
performRelay(0);
1050
});
meshrelay.js
+18
-18
@@ -28,7 +28,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
28
}
29
30
// If there is no authentication, drop this connection
31
- if ((obj.id != null) && (obj.id.startsWith('meshmessenger/') == false) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } return; }
31
+ if ((obj.id != null) && (obj.id.startsWith('meshmessenger/') == false) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } return; }
32
33
// Relay session count (we may remove this in the future)
34
obj.relaySessionCounted = true;
@@ -65,8 +65,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
65
66
// Disconnect this agent
67
obj.close = function (arg) {
68
- if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
69
- if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect (' + cleanRemoteAddr(obj.req.ip) + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
68
+ if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
69
+ if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
70
71
// Aggressive cleanup
72
delete obj.id;
@@ -172,7 +172,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
172
// Check that at least one connection is authenticated
173
if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
174
ws.close();
175
- parent.parent.debug('relay', 'Relay without-auth: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ')');
175
+ parent.parent.debug('relay', 'Relay without-auth: ' + obj.id + ' (' + obj.req.clientIp + ')');
176
delete obj.id;
177
delete obj.ws;
178
delete obj.peer;
@@ -189,7 +189,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
189
}
190
if (u1 != u2) {
191
ws.close();
192
- parent.parent.debug('relay', 'Relay auth mismatch (' + u1 + ' != ' + u2 + '): ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ')');
192
+ parent.parent.debug('relay', 'Relay auth mismatch (' + u1 + ' != ' + u2 + '): ' + obj.id + ' (' + obj.req.clientIp + ')');
193
delete obj.id;
194
delete obj.ws;
195
delete obj.peer;
@@ -247,7 +247,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
247
} else {
248
// Write the recording file header
249
parent.parent.debug('relay', 'Relay: Started recoding to file: ' + recFullFilename);
250
- var metadata = { magic: 'MeshCentralRelaySession', ver: 1, userid: sessionUser._id, username: sessionUser.name, sessionid: obj.id, ipaddr1: cleanRemoteAddr(obj.req.ip), ipaddr2: cleanRemoteAddr(obj.peer.req.ip), time: new Date().toLocaleString(), protocol: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.p), nodeid: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.nodeid ) };
250
+ var metadata = { magic: 'MeshCentralRelaySession', ver: 1, userid: sessionUser._id, username: sessionUser.name, sessionid: obj.id, ipaddr1: obj.req.clientIp, ipaddr2: obj.peer.req.clientIp, time: new Date().toLocaleString(), protocol: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.p), nodeid: (((obj.req == null) || (obj.req.query == null)) ? null : obj.req.query.nodeid ) };
251
if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
252
var firstBlock = JSON.stringify(metadata);
253
var logfile = { fd: fd, lock: false, filename: recFullFilename, startTime: Date.now(), size: 0 };
@@ -270,7 +270,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
270
try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
271
}
272
273
- parent.parent.debug('relay', 'Relay connected: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ' --> ' + cleanRemoteAddr(obj.peer.req.ip) + ')');
273
+ parent.parent.debug('relay', 'Relay connected: ' + obj.id + ' (' + obj.req.clientIp + ' --> ' + obj.peer.req.clientIp + ')');
274
275
// Log the connection
276
if (sessionUser != null) {
@@ -278,13 +278,13 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
278
if (obj.req.query.p == 1) { msg = 'Started terminal session'; }
279
else if (obj.req.query.p == 2) { msg = 'Started desktop session'; }
280
else if (obj.req.query.p == 5) { msg = 'Started file management session'; }
281
- var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: sessionUser._id, username: sessionUser.name, msg: msg + ' \"' + obj.id + '\" from ' + cleanRemoteAddr(obj.peer.req.ip) + ' to ' + cleanRemoteAddr(req.ip), protocol: req.query.p, nodeid: req.query.nodeid };
281
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: sessionUser._id, username: sessionUser.name, msg: msg + ' \"' + obj.id + '\" from ' + obj.peer.req.clientIp + ' to ' + req.clientIp, protocol: req.query.p, nodeid: req.query.nodeid };
282
parent.parent.DispatchEvent(['*', sessionUser._id], obj, event);
283
}
284
} else {
285
// Connected already, drop (TODO: maybe we should re-connect?)
286
ws.close();
287
- parent.parent.debug('relay', 'Relay duplicate: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ')');
287
+ parent.parent.debug('relay', 'Relay duplicate: ' + obj.id + ' (' + obj.req.clientIp + ')');
288
delete obj.id;
289
delete obj.ws;
290
delete obj.peer;
@@ -294,7 +294,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
294
// Wait for other relay connection
295
ws._socket.pause(); // Hold traffic until the other connection
296
parent.wsrelays[obj.id] = { peer1: obj, state: 1, timeout: setTimeout(function () { closeBothSides(); }, 30000) };
297
- parent.parent.debug('relay', 'Relay holding: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ') ' + (obj.authenticated ? 'Authenticated' : ''));
297
+ parent.parent.debug('relay', 'Relay holding: ' + obj.id + ' (' + obj.req.clientIp + ') ' + (obj.authenticated ? 'Authenticated' : ''));
298
299
// Check if a peer server has this connection
300
if (parent.parent.multiServer != null) {
@@ -354,7 +354,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
354
ws.on('error', function (err) {
355
parent.relaySessionErrorCount++;
356
if (obj.relaySessionCounted) { parent.relaySessionCount--; delete obj.relaySessionCounted; }
357
- console.log('Relay error from ' + cleanRemoteAddr(obj.req.ip) + ', ' + err.toString().split('\r')[0] + '.');
357
+ console.log('Relay error from ' + obj.req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
358
closeBothSides();
359
});
360
@@ -374,7 +374,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
374
375
// Disconnect the peer
376
try { if (peer.relaySessionCounted) { parent.relaySessionCount--; delete peer.relaySessionCounted; } } catch (ex) { console.log(ex); }
377
- parent.parent.debug('relay', 'Relay disconnect: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ' --> ' + cleanRemoteAddr(peer.req.ip) + ')');
377
+ parent.parent.debug('relay', 'Relay disconnect: ' + obj.id + ' (' + obj.req.clientIp + ' --> ' + peer.req.clientIp + ')');
378
try { peer.ws.close(); } catch (e) { } // Soft disconnect
379
try { peer.ws._socket._parent.end(); } catch (e) { } // Hard disconnect
380
@@ -385,10 +385,10 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
385
else if (obj.req.query.p == 2) { msg = 'Ended desktop session'; }
386
else if (obj.req.query.p == 5) { msg = 'Ended file management session'; }
387
if (user) {
388
- var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msg: msg + ' \"' + obj.id + '\" from ' + cleanRemoteAddr(obj.req.ip) + ' to ' + cleanRemoteAddr(obj.peer.req.ip) + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: obj.req.query.p, nodeid: obj.req.query.nodeid };
388
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msg: msg + ' \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.peer.req.clientIp + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: obj.req.query.p, nodeid: obj.req.query.nodeid };
389
parent.parent.DispatchEvent(['*', user._id], obj, event);
390
} else if (peer.user) {
391
- var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: peer.user._id, username: peer.user.name, msg: msg + ' \"' + obj.id + '\" from ' + cleanRemoteAddr(obj.req.ip) + ' to ' + cleanRemoteAddr(obj.peer.req.ip) + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: obj.req.query.p, nodeid: obj.req.query.nodeid };
391
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: peer.user._id, username: peer.user.name, msg: msg + ' \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.peer.req.clientIp + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: obj.req.query.p, nodeid: obj.req.query.nodeid };
392
parent.parent.DispatchEvent(['*', peer.user._id], obj, event);
393
}
394
}
@@ -400,7 +400,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
400
if (peer.pingtimer != null) { clearInterval(peer.pingtimer); delete peer.pingtimer; }
401
if (peer.pongtimer != null) { clearInterval(peer.pongtimer); delete peer.pongtimer; }
402
} else {
403
- parent.parent.debug('relay', 'Relay disconnect: ' + obj.id + ' (' + cleanRemoteAddr(obj.req.ip) + ')');
403
+ parent.parent.debug('relay', 'Relay disconnect: ' + obj.id + ' (' + obj.req.clientIp + ')');
404
}
405
406
// Close the recording file if needed
@@ -494,7 +494,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
494
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
495
const command = { nodeid: cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr };
496
parent.parent.debug('relay', 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
497
- if (obj.sendAgentMessage(command, user._id, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
497
+ if (obj.sendAgentMessage(command, user._id, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
498
performRelay();
499
});
500
return obj;
@@ -514,11 +514,11 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
514
if (obj.req.query.tcpport != null) {
515
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: obj.req.query.tcpport, tcpaddr: ((obj.req.query.tcpaddr == null) ? '127.0.0.1' : obj.req.query.tcpaddr) };
516
parent.parent.debug('relay', 'Relay: Sending agent TCP tunnel command: ' + JSON.stringify(command));
517
- if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
517
+ if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
518
} else if (obj.req.query.udpport != null) {
519
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, udpport: obj.req.query.udpport, udpaddr: ((obj.req.query.udpaddr == null) ? '127.0.0.1' : obj.req.query.udpaddr) };
520
parent.parent.debug('relay', 'Relay: Sending agent UDP tunnel command: ' + JSON.stringify(command));
521
- if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + cleanRemoteAddr(obj.req.ip) + ')'); }
521
+ if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
522
}
523
performRelay();
524
});
meshuser.js
+5
-5
@@ -188,7 +188,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
188
if (typeof user.consent == 'number') { command.consent |= user.consent; } // Add user consent
189
command.username = user.name; // Add user name
190
command.userid = user._id; // Add user id
191
- command.remoteaddr = cleanRemoteAddr(req.ip); // User's IP address
191
+ command.remoteaddr = req.clientIp; // User's IP address
192
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
193
delete command.nodeid; // Remove the nodeid since it's implied
194
try { agent.send(JSON.stringify(command)); } catch (ex) { }
@@ -211,7 +211,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
211
if (typeof user.consent == 'number') { command.consent |= user.consent; } // Add user consent
212
command.username = user.name; // Add user name
213
command.userid = user._id; // Add user id
214
- command.remoteaddr = cleanRemoteAddr(req.ip); // User's IP address
214
+ command.remoteaddr = req.clientIp; // User's IP address
215
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
216
parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
217
}
@@ -456,7 +456,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
456
try {
457
ws.send(JSON.stringify({
458
action: 'authcookie',
459
- cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, parent.parent.loginCookieEncryptionKey),
459
+ cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: req.clientIp }, parent.parent.loginCookieEncryptionKey),
460
rcookie: parent.parent.encodeCookie({ ruserid: user._id }, parent.parent.loginCookieEncryptionKey)
461
}));
462
} catch (ex) { }
@@ -1136,11 +1136,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1136
for (var i in parent.wsrelays) {
1137
r += 'id: ' + i + ', ' + ((parent.wsrelays[i].state == 2)?'connected':'pending');
1138
if (parent.wsrelays[i].peer1 != null) {
1139
- r += ', ' + cleanRemoteAddr(parent.wsrelays[i].peer1.req.ip);
1139
+ r += ', ' + cleanRemoteAddr(parent.wsrelays[i].peer1.req.clientIp);
1140
if (parent.wsrelays[i].peer1.user) { r += ' (User:' + parent.wsrelays[i].peer1.user.name + ')' }
1141
}
1142
if (parent.wsrelays[i].peer2 != null) {
1143
- r += ' to ' + cleanRemoteAddr(parent.wsrelays[i].peer2.req.ip);
1143
+ r += ' to ' + cleanRemoteAddr(parent.wsrelays[i].peer2.req.clientIp);
1144
if (parent.wsrelays[i].peer2.user) { r += ' (User:' + parent.wsrelays[i].peer2.user.name + ')' }
1145
}
1146
r += '\r\n';
webserver.js
+80
-67
@@ -572,22 +572,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
572
// Check if the source IP address is in the IP list, return false if not.
573
function checkIpAddressEx(req, res, ipList, closeIfThis) {
574
try {
575
- var ip;
576
- if (req.connection) { // HTTP(S) request
577
- ip = req.ip;
578
-
579
- if (ip) { for (var i = 0; i < ipList.length; i++) { if (require('ipcheck').match(ip, ipList[i])) { if (closeIfThis === true) { res.sendStatus(401); } return true; } } }
575
+ if (req.connection) {
576
+ // HTTP(S) request
577
+ if (req.clientIp) { for (var i = 0; i < ipList.length; i++) { if (require('ipcheck').match(req.clientIp, ipList[i])) { if (closeIfThis === true) { res.sendStatus(401); } return true; } } }
578
if (closeIfThis === false) { res.sendStatus(401); }
581
- } else if (req._socket) { // WebSocket request
582
- ip = req._socket.remoteAddress;
583
- var ipex = (ip.startsWith('::ffff:')) ? ip.substring(7) : ip;
584
-
585
- // If a trusted reverse-proxy is sending us the remote IP address, use it.
586
- // This is not done automatically for web socket like it's done for HTTP requests.
587
- if ((obj.args.trustedproxy) && (res.headers['x-forwarded-for']) && ((obj.args.trustedproxy === true) || (obj.args.trustedproxy.indexOf(ipex) >= 0))) { ip = res.headers['x-forwarded-for']; }
588
- else if ((obj.args.tlsoffload) && (res.headers['x-forwarded-for']) && ((obj.args.tlsoffload === true) || (obj.args.tlsoffload.indexOf(ipex) >= 0))) { ip = res.headers['x-forwarded-for']; }
589
-
590
- if (ip) { for (var i = 0; i < ipList.length; i++) { if (require('ipcheck').match(ip, ipList[i])) { if (closeIfThis === true) { try { req.close(); } catch (e) { } } return true; } } }
579
+ } else {
580
+ // WebSocket request
581
+ if (res.clientIp) { for (var i = 0; i < ipList.length; i++) { if (require('ipcheck').match(res.clientIp, ipList[i])) { if (closeIfThis === true) { try { req.close(); } catch (e) { } } return true; } } }
582
if (closeIfThis === false) { try { req.close(); } catch (e) { } }
583
}
584
} catch (e) { console.log(e); } // Should never happen
@@ -649,8 +640,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
640
// Return true if this user has 2-step auth active
641
function checkUserOneTimePasswordRequired(domain, user, req) {
642
// Check if we can skip 2nd factor auth because of the source IP address
652
- if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
653
- for (var i in domain.passwordrequirements.skip2factor) { if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) return false; }
643
+ if ((req != null) && (req.clientIp != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
644
+ for (var i in domain.passwordrequirements.skip2factor) { if (require('ipcheck').match(req.clientIp, domain.passwordrequirements.skip2factor[i]) === true) return false; }
645
}
646
647
// Check if a 2nd factor cookie is present
@@ -659,7 +650,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
650
for (var i in cookies) {
651
if (cookies[i].startsWith('twofactor=')) {
652
var twoFactorCookie = obj.parent.decodeCookie(decodeURIComponent(cookies[i].substring(10)), obj.parent.loginCookieEncryptionKey, (30 * 24 * 60)); // If the cookies does not have an expire feild, assume 30 day timeout.
662
- if ((twoFactorCookie != null) && ((obj.args.cookieipcheck === false) || (twoFactorCookie.ip == null) || (twoFactorCookie.ip === cleanRemoteAddr(req.ip))) && (twoFactorCookie.userid == user._id)) { return false; }
653
+ if ((twoFactorCookie != null) && ((obj.args.cookieipcheck === false) || (twoFactorCookie.ip == null) || (twoFactorCookie.ip === req.clientIp)) && (twoFactorCookie.userid == user._id)) { return false; }
654
}
655
}
656
}
@@ -896,9 +887,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
887
if ((req.body.token != null) || (req.body.hwtoken != null)) {
888
randomWaitTime = 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095); // This is a fail, wait a random time. 2 to 6 seconds.
889
req.session.messageid = 108; // Invalid token, try again.
899
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed 2FA for ' + xusername + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.port); }
890
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed 2FA for ' + xusername + ' from ' + cleanRemoteAddr(req.clientIp) + ' port ' + req.port); }
891
parent.debug('web', 'handleLoginRequest: invalid 2FA token');
901
- obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + user.name], obj, { action: 'authfail', username: user.name, userid: 'user/' + domain.id + '/' + user.name, domain: domain.id, msg: 'User login attempt with incorrect 2nd factor from ' + cleanRemoteAddr(req.ip) });
892
+ obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + user.name], obj, { action: 'authfail', username: user.name, userid: 'user/' + domain.id + '/' + user.name, domain: domain.id, msg: 'User login attempt with incorrect 2nd factor from ' + req.clientIp });
893
obj.setbadLogin(req);
894
} else {
895
parent.debug('web', 'handleLoginRequest: 2FA token required');
@@ -919,7 +910,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
910
var maxCookieAge = domain.twofactorcookiedurationdays;
911
if (typeof maxCookieAge != 'number') { maxCookieAge = 30; }
912
console.log('maxCookieAge', maxCookieAge);
922
- const twoFactorCookie = obj.parent.encodeCookie({ userid: user._id, expire: maxCookieAge * 24 * 60 /*, ip: cleanRemoteAddr(req.ip)*/ }, obj.parent.loginCookieEncryptionKey);
913
+ const twoFactorCookie = obj.parent.encodeCookie({ userid: user._id, expire: maxCookieAge * 24 * 60 /*, ip: req.clientIp*/ }, obj.parent.loginCookieEncryptionKey);
914
res.cookie('twofactor', twoFactorCookie, { maxAge: (maxCookieAge * 24 * 60 * 60 * 1000), httpOnly: true, sameSite: 'strict', secure: true });
915
}
916
@@ -936,7 +927,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
927
}
928
929
// Login successful
939
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + xusername + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
930
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + xusername + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
931
parent.debug('web', 'handleLoginRequest: successful 2FA login');
932
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct);
933
}
@@ -957,12 +948,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
948
}
949
950
// Login successful
960
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + xusername + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
951
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + xusername + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
952
parent.debug('web', 'handleLoginRequest: successful login');
953
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct);
954
} else {
955
// Login failed, log the error
965
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed password for ' + xusername + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
956
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed password for ' + xusername + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
957
958
// Wait a random delay
959
setTimeout(function () {
@@ -972,12 +963,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
963
if (err == 'locked') {
964
parent.debug('web', 'handleLoginRequest: login failed, locked account');
965
req.session.messageid = 110; // Account locked.
975
- obj.parent.DispatchEvent(['*', 'server-users', xuserid], obj, { action: 'authfail', userid: xuserid, username: xusername, domain: domain.id, msg: 'User login attempt on locked account from ' + cleanRemoteAddr(req.ip) });
966
+ obj.parent.DispatchEvent(['*', 'server-users', xuserid], obj, { action: 'authfail', userid: xuserid, username: xusername, domain: domain.id, msg: 'User login attempt on locked account from ' + req.clientIp });
967
obj.setbadLogin(req);
968
} else {
969
parent.debug('web', 'handleLoginRequest: login failed, bad username and password');
970
req.session.messageid = 112; // Login failed, check username and password.
980
- obj.parent.DispatchEvent(['*', 'server-users', xuserid], obj, { action: 'authfail', userid: xuserid, username: xusername, domain: domain.id, msg: 'Invalid user login attempt from ' + cleanRemoteAddr(req.ip) });
971
+ obj.parent.DispatchEvent(['*', 'server-users', xuserid], obj, { action: 'authfail', userid: xuserid, username: xusername, domain: domain.id, msg: 'Invalid user login attempt from ' + req.clientIp });
972
obj.setbadLogin(req);
973
}
974
}
@@ -1033,7 +1024,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1024
req.session.userid = userid;
1025
req.session.domainid = domain.id;
1026
req.session.currentNode = '';
1036
- req.session.ip = req.ip;
1027
+ req.session.ip = req.clientIp;
1028
if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; }
1029
if (req.body.host) {
1030
// TODO: This is a terrible search!!! FIX THIS.
@@ -1156,7 +1147,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1147
obj.users[user._id] = user;
1148
req.session.userid = user._id;
1149
req.session.domainid = domain.id;
1159
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1150
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1151
// Create a user, generate a salt and hash the password
1152
require('./pass').hash(req.body.password1, function (err, salt, hash, tag) {
1153
if (err) throw err;
@@ -1244,7 +1235,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1235
parent.debug('web', 'handleResetPasswordRequest: success');
1236
req.session.userid = userid;
1237
req.session.domainid = domain.id;
1247
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1238
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1239
completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword, direct);
1240
}, 0);
1241
}
@@ -1309,7 +1300,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1300
parent.debug('web', 'handleResetAccountRequest: Invalid 2FA token, try again');
1301
if ((req.body.token != null) || (req.body.hwtoken != null)) {
1302
req.session.messageid = 108; // Invalid token, try again.
1312
- obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + user.name], obj, { action: 'authfail', username: user.name, userid: 'user/' + domain.id + '/' + user.name, domain: domain.id, msg: 'User login attempt with incorrect 2nd factor from ' + cleanRemoteAddr(req.ip) });
1303
+ obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + user.name], obj, { action: 'authfail', username: user.name, userid: 'user/' + domain.id + '/' + user.name, domain: domain.id, msg: 'User login attempt with incorrect 2nd factor from ' + req.clientIp });
1304
obj.setbadLogin(req);
1305
}
1306
req.session.loginmode = '5';
@@ -1811,11 +1802,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1802
// Login using SSPI
1803
domain.sspi.authenticate(req, res, function (err) {
1804
if ((err != null) || (req.connection.user == null)) {
1814
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed SSPI-auth for ' + req.connection.user + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
1805
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Failed SSPI-auth for ' + req.connection.user + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
1806
parent.debug('web', 'handleRootRequest: SSPI auth required.');
1807
res.end('Authentication Required...');
1808
} else {
1818
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted SSPI-auth for ' + req.connection.user + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
1809
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted SSPI-auth for ' + req.connection.user + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
1810
parent.debug('web', 'handleRootRequest: SSPI auth ok.');
1811
handleRootRequestEx(req, res, domain, direct);
1812
}
@@ -1823,12 +1814,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1814
} else if (req.query.user && req.query.pass) {
1815
// User credentials are being passed in the URL. WARNING: Putting credentials in a URL is bad security... but people are requesting this option.
1816
obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
1826
- if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + req.connection.user + ' from ' + cleanRemoteAddr(req.ip) + ' port ' + req.connection.remotePort); }
1817
+ if (obj.parent.authlog) { obj.parent.authLog('https', 'Accepted password for ' + req.connection.user + ' from ' + req.clientIp + ' port ' + req.connection.remotePort); }
1818
parent.debug('web', 'handleRootRequest: user/pass in URL auth ok.');
1819
req.session.userid = userid;
1820
req.session.domainid = domain.id;
1821
req.session.currentNode = '';
1831
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1822
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1823
handleRootRequestEx(req, res, domain, direct);
1824
});
1825
} else {
@@ -1854,7 +1845,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1845
req.session.userid = 'user/' + domain.id + '/~';
1846
req.session.domainid = domain.id;
1847
req.session.currentNode = '';
1857
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1848
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1849
if (obj.users[req.session.userid] == null) {
1850
// Create the dummy user ~ with impossible password
1851
parent.debug('web', 'handleRootRequestEx: created dummy user in nouser mode.');
@@ -1868,10 +1859,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1859
req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
1860
req.session.domainid = domain.id;
1861
req.session.currentNode = '';
1871
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1862
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1863
} else if (req.query.login && (obj.parent.loginCookieEncryptionKey != null)) {
1864
var loginCookie = obj.parent.decodeCookie(req.query.login, obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
1874
- //if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != cleanRemoteAddr(req.ip))) { loginCookie = null; } // If the cookie if binded to an IP address, check here.
1865
+ //if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != req.clientIp)) { loginCookie = null; } // If the cookie if binded to an IP address, check here.
1866
if ((loginCookie != null) && (loginCookie.a == 3) && (loginCookie.u != null) && (loginCookie.u.split('/')[1] == domain.id)) {
1867
// If a login cookie was provided, setup the session here.
1868
parent.debug('web', 'handleRootRequestEx: cookie auth ok.');
@@ -1879,7 +1870,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1870
req.session.userid = loginCookie.u;
1871
req.session.domainid = domain.id;
1872
req.session.currentNode = '';
1882
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1873
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1874
} else {
1875
parent.debug('web', 'handleRootRequestEx: cookie auth failed.');
1876
}
@@ -1896,7 +1887,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1887
req.session.usersGroups = req.connection.userGroups;
1888
req.session.domainid = domain.id;
1889
req.session.currentNode = '';
1899
- req.session.ip = req.ip; // Bind this session to the IP address of the request
1890
+ req.session.ip = req.clientIp; // Bind this session to the IP address of the request
1891
1892
// Check if this user exists, create it if not.
1893
user = obj.users[req.session.userid];
@@ -1996,9 +1987,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1987
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) {
1988
// Check if we can skip 2nd factor auth because of the source IP address
1989
var skip2factor = false;
1999
- if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
1990
+ if ((req != null) && (req.clientIp != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) {
1991
for (var i in domain.passwordrequirements.skip2factor) {
2001
- if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) { skip2factor = true; }
1992
+ if (require('ipcheck').match(req.clientIp, domain.passwordrequirements.skip2factor[i]) === true) { skip2factor = true; }
1993
}
1994
}
1995
if (skip2factor == false) { features += 0x00040000; } // Force 2-factor auth
@@ -2015,7 +2006,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2006
if (domain.urlswitching === false) { features += 0x10000000; } // Disables the URL switching feature
2007
2008
// Create a authentication cookie
2018
- const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, obj.parent.loginCookieEncryptionKey);
2009
+ const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: req.clientIp }, obj.parent.loginCookieEncryptionKey);
2010
const authRelayCookie = obj.parent.encodeCookie({ ruserid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
2011
2012
// Send the master web application
@@ -2196,7 +2187,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2187
if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrols.logoutUrl = (domain.url + 'logout?' + Math.random() + extras); } // If a default user is in use or no user mode, don't display the logout button
2188
2189
// Create a authentication cookie
2199
- const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, obj.parent.loginCookieEncryptionKey);
2190
+ const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: req.clientIp }, obj.parent.loginCookieEncryptionKey);
2191
const authRelayCookie = obj.parent.encodeCookie({ ruserid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
2192
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
2193
render(req, res, getRenderPage('xterm', req, domain), getRenderArgs({ serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, authCookie: authCookie, authRelayCookie: authRelayCookie, logoutControls: encodeURIComponent(JSON.stringify(logoutcontrols)).replace(/'/g, '%27'), name: EscapeHtml(node.name) }, req, domain));
@@ -2701,7 +2692,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2692
// If an authentication cookie is embedded in the form, use that.
2693
if ((fields != null) && (fields.auth != null) && (fields.auth.length == 1) && (typeof fields.auth[0] == 'string')) {
2694
var loginCookie = obj.parent.decodeCookie(fields.auth[0], obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
2704
- if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != cleanRemoteAddr(req.ip))) { loginCookie = null; } // Check cookie IP binding.
2695
+ if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != req.clientIp)) { loginCookie = null; } // Check cookie IP binding.
2696
if ((loginCookie != null) && (domain.id == loginCookie.domainid)) { authUserid = loginCookie.userid; } // Use cookie authentication
2697
}
2698
if (authUserid == null) { res.sendStatus(401); return; }
@@ -2737,7 +2728,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2728
// If an authentication cookie is embedded in the form, use that.
2729
if ((fields != null) && (fields.auth != null) && (fields.auth.length == 1) && (typeof fields.auth[0] == 'string')) {
2730
var loginCookie = obj.parent.decodeCookie(fields.auth[0], obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
2740
- if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != cleanRemoteAddr(req.ip))) { loginCookie = null; } // Check cookie IP binding.
2731
+ if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != req.clientIp)) { loginCookie = null; } // Check cookie IP binding.
2732
if ((loginCookie != null) && (domain.id == loginCookie.domainid)) { authUserid = loginCookie.userid; } // Use cookie authentication
2733
}
2734
if (authUserid == null) { res.sendStatus(401); return; }
@@ -2895,7 +2886,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2886
var fd = obj.fs.openSync(recFullFilename, 'w');
2887
if (fd != null) {
2888
// Write the recording file header
2898
- var firstBlock = JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, userid: user._id, username: user.name, ipaddr: cleanRemoteAddr(req.ip), nodeid: node._id, intelamt: true, protocol: (req.query.p == 2) ? 101 : 100, time: new Date().toLocaleString() })
2889
+ 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() })
2890
recordingEntry(fd, 1, 0, firstBlock, function () { });
2891
ws.logfile = { fd: fd, lock: false };
2892
if (req.query.p == 2) { ws.send(Buffer.from(String.fromCharCode(0xF0), 'binary')); } // Intel AMT Redirection: Indicate the session is being recorded
@@ -2990,7 +2981,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2981
2982
// If error, close the associated TCP connection.
2983
ws.on('error', function (err) {
2993
- console.log('CIRA server websocket error from ' + cleanRemoteAddr(req.ip) + ', ' + err.toString().split('\r')[0] + '.');
2984
+ console.log('CIRA server websocket error from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
2985
parent.debug('webrelay', 'Websocket relay closed on error.');
2986
if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS
2987
@@ -3083,8 +3074,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3074
3075
// If error, close the associated TCP connection.
3076
ws.on('error', function (err) {
3086
- console.log('Error with relay web socket connection from ' + cleanRemoteAddr(req.ip) + ', ' + err.toString().split('\r')[0] + '.');
3087
- parent.debug('webrelay', 'Error with relay web socket connection from ' + cleanRemoteAddr(req.ip) + '.');
3077
+ console.log('Error with relay web socket connection from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
3078
+ parent.debug('webrelay', 'Error with relay web socket connection from ' + req.clientIp + '.');
3079
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
3080
3081
// Close the recording file
@@ -3197,7 +3188,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3188
if (mesh.mtype != 1) { ws.send(JSON.stringify({ errorText: 'Invalid device group type:' + ws.meshid })); delete ws.meshid; ws.close(); return; }
3189
3190
// Fetch the remote IP:Port for logging
3200
- ws.remoteaddr = cleanRemoteAddr(req.ip);
3191
+ ws.remoteaddr = req.clientIp;
3192
ws.remoteaddrport = ws.remoteaddr + ':' + ws._socket.remotePort;
3193
3194
// When data is received from the web socket, echo it back
@@ -3389,7 +3380,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3380
});
3381
3382
// If error, do nothing.
3392
- ws.on('error', function (err) { console.log('Echo server error from ' + cleanRemoteAddr(req.ip) + ', ' + err.toString().split('\r')[0] + '.'); });
3383
+ ws.on('error', function (err) { console.log('Echo server error from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.'); });
3384
3385
// If closed, do nothing
3386
ws.on('close', function (req) { });
@@ -3452,7 +3443,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3443
if (auth.response === obj.common.ComputeDigesthash(auth.username, amtpass, auth.realm, 'POST', auth.uri, auth.qop, auth.nonce, auth.nc, auth.cnonce)) {
3444
3445
// This is an authenticated Intel AMT event, update the host address
3455
- var amthost = req.ip;
3446
+ var amthost = req.clientIp;
3447
if (amthost.substring(0, 7) === '::ffff:') { amthost = amthost.substring(7); }
3448
if (node.host != amthost) {
3449
// Get the mesh for this device
@@ -3549,7 +3540,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3540
// If an authentication cookie is embedded in the form, use that.
3541
if ((fields != null) && (fields.auth != null) && (fields.auth.length == 1) && (typeof fields.auth[0] == 'string')) {
3542
var loginCookie = obj.parent.decodeCookie(fields.auth[0], obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
3552
- if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != cleanRemoteAddr(req.ip))) { loginCookie = null; } // Check cookie IP binding.
3543
+ if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != req.clientIp)) { loginCookie = null; } // Check cookie IP binding.
3544
if ((loginCookie != null) && (domain.id == loginCookie.domainid)) { authUserid = loginCookie.userid; } // Use cookie authentication
3545
}
3546
if (authUserid == null) { res.sendStatus(401); return; }
@@ -4015,9 +4006,31 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4006
4007
// Add HTTP security headers to all responses
4008
obj.app.use(function (req, res, next) {
4018
- parent.debug('webrequest', '(' + cleanRemoteAddr(req.ip) + ') ' + req.url);
4009
+ // Set the real IP address of the request
4010
+ // If a trusted reverse-proxy is sending us the remote IP address, use it.
4011
+ const ipex = (req.ip.startsWith('::ffff:')) ? req.ip.substring(7) : req.ip;
4012
+ if (
4013
+ (obj.args.trustedproxy === true) ||
4014
+ ((typeof obj.args.trustedproxy == 'object') && (obj.args.trustedproxy.indexOf(ipex) >= 0)) ||
4015
+ ((typeof obj.args.tlsoffload == 'object') && (obj.args.tlsoffload.indexOf(ipex) >= 0))
4016
+ ) {
4017
+ if (req.headers['cf-connecting-ip']) { // Use CloudFlare IP address if present
4018
+ req.clientIp = req.headers['cf-connecting-ip'].split(',')[0].trim();
4019
+ } else if (res.headers['x-forwarded-for']) {
4020
+ req.clientIp = req.headers['x-forwarded-for'].split(',')[0].trim();
4021
+ } else if (res.headers['x-real-ip']) {
4022
+ req.clientIp = req.headers['x-real-ip'].split(',')[0].trim();
4023
+ } else {
4024
+ req.clientIp = ipex;
4025
+ }
4026
+ } else {
4027
+ req.clientIp = ipex;
4028
+ }
4029
+
4030
+ // Get the domain for this request
4031
+ const domain = req.xdomain = getDomain(req);
4032
+ parent.debug('webrequest', '(' + req.clientIp + ') ' + req.url);
4033
res.removeHeader('X-Powered-By');
4020
- var domain = req.xdomain = getDomain(req);
4034
4035
// If this domain has configured headers, use them.
4036
// Example headers: { 'Strict-Transport-Security': 'max-age=360000;includeSubDomains' };
@@ -4039,7 +4052,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4052
}
4053
4054
// Check the session if bound to the external IP address
4042
- if ((req.session.ip != null) && (req.ip != null) && (req.session.ip != req.ip)) { req.session = {}; }
4055
+ if ((req.session.ip != null) && (req.clientIp != null) && (req.session.ip != req.clientIp)) { req.session = {}; }
4056
4057
// Detect if this is a file sharing domain, if so, just share files.
4058
if ((domain != null) && (domain.share != null)) {
@@ -4354,8 +4367,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4367
// Receive mesh agent connections
4368
obj.app.ws(url + 'agent.ashx', function (ws, req) {
4369
var domain = checkAgentIpAddress(ws, req);
4357
- if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + cleanRemoteAddr(req.ip) + ', holding.'); return; }
4358
- //console.log('Agent connect: ' + cleanRemoteAddr(req.ip));
4370
+ if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + req.clientIp + ', holding.'); return; }
4371
+ //console.log('Agent connect: ' + req.clientIp);
4372
try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, domain); } catch (e) { console.log(e); }
4373
});
4374
@@ -4363,11 +4376,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4376
if (obj.parent.mqttbroker != null) {
4377
obj.app.ws(url + 'mqtt.ashx', function (ws, req) {
4378
var domain = checkAgentIpAddress(ws, req);
4366
- if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + cleanRemoteAddr(req.ip) + ', holding.'); return; }
4379
+ if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + req.clientIp + ', holding.'); return; }
4380
var serialtunnel = SerialTunnel();
4381
serialtunnel.xtransport = 'ws';
4382
serialtunnel.xdomain = domain;
4370
- serialtunnel.xip = req.ip;
4383
+ serialtunnel.xip = req.clientIp;
4384
ws.on('message', function (b) { serialtunnel.updateBuffer(Buffer.from(b, 'binary')) });
4385
serialtunnel.forwardwrite = function (b) { ws.send(b, 'binary') }
4386
ws.on('close', function () { serialtunnel.emit('end'); });
@@ -4380,8 +4393,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4393
// Receive mesh agent connections on alternate port
4394
obj.agentapp.ws(url + 'agent.ashx', function (ws, req) {
4395
var domain = checkAgentIpAddress(ws, req);
4383
- if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + cleanRemoteAddr(req.ip) + ', holding.'); return; }
4384
- //console.log('Agent connect: ' + cleanRemoteAddr(req.ip));
4396
+ if (domain == null) { parent.debug('web', 'Got agent connection with bad domain or blocked IP address ' + req.clientIp + ', holding.'); return; }
4397
+ //console.log('Agent connect: ' + req.clientIp);
4398
try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, domain); } catch (e) { console.log(e); }
4399
});
4400
@@ -4554,7 +4567,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4567
} else {
4568
// If not authenticated, close the websocket connection
4569
parent.debug('web', 'ERR: Websocket bad user/pass auth');
4557
- //obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + obj.args.user.toLowerCase()], obj, { action: 'authfail', userid: 'user/' + domain.id + '/' + obj.args.user.toLowerCase(), username: obj.args.user, domain: domain.id, msg: 'Invalid user login attempt from ' + cleanRemoteAddr(req.ip) });
4570
+ //obj.parent.DispatchEvent(['*', 'server-users', 'user/' + domain.id + '/' + obj.args.user.toLowerCase()], obj, { action: 'authfail', userid: 'user/' + domain.id + '/' + obj.args.user.toLowerCase(), username: obj.args.user, domain: domain.id, msg: 'Invalid user login attempt from ' + req.clientIp });
4571
//obj.setbadLogin(req);
4572
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'noauth-2' })); ws.close(); } catch (e) { }
4573
}
@@ -4565,8 +4578,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
4578
// This is a encrypted cookie authentication
4579
var cookie = obj.parent.decodeCookie(req.query.auth, obj.parent.loginCookieEncryptionKey, 240); // Cookie with 4 hour timeout
4580
if ((cookie == null) && (obj.parent.multiServer != null)) { cookie = obj.parent.decodeCookie(req.query.auth, obj.parent.serverKey, 240); } // Try the server key
4568
- if ((obj.args.cookieipcheck !== false) && (cookie != null) && (cookie.ip != null) && (cookie.ip != cleanRemoteAddr(req.ip) && (cookie.ip != req.ip))) { // If the cookie if binded to an IP address, check here.
4569
- parent.debug('web', 'ERR: Invalid cookie IP address, got \"' + cookie.ip + '\", expected \"' + cleanRemoteAddr(req.ip) + '\".');
4581
+ if ((obj.args.cookieipcheck !== false) && (cookie != null) && (cookie.ip != null) && (cookie.ip != req.clientIp && (cookie.ip != req.clientIp))) { // If the cookie if binded to an IP address, check here.
4582
+ parent.debug('web', 'ERR: Invalid cookie IP address, got \"' + cookie.ip + '\", expected \"' + cleanRemoteAddr(req.clientIp) + '\".');
4583
cookie = null;
4584
}
4585
if ((cookie != null) && (obj.users[cookie.userid]) && (cookie.domainid == domain.id)) {
@@ -5549,7 +5562,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5562
}
5563
obj.setbadLogin = function (ip) { // Set an IP address that just did a bad login request
5564
if (parent.config.settings.maxinvalidlogin === false) return;
5552
- if (typeof ip == 'object') { ip = cleanRemoteAddr(ip.ip); }
5565
+ if (typeof ip == 'object') { ip = ip.clientIp; }
5566
var splitip = ip.split('.');
5567
if (splitip.length == 4) { ip = (splitip[0] + '.' + splitip[1] + '.' + splitip[2] + '.*'); }
5568
if (++obj.badLoginTableLastClean > 100) { obj.cleanBadLoginTable(); }
@@ -5561,7 +5574,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5574
}
5575
obj.checkAllowLogin = function (ip) { // Check if an IP address is allowed to login
5576
if (parent.config.settings.maxinvalidlogin === false) return true;
5564
- if (typeof ip == 'object') { ip = cleanRemoteAddr(ip.ip); }
5577
+ if (typeof ip == 'object') { ip = ip.clientIp; }
5578
var splitip = ip.split('.');
5579
if (splitip.length == 4) { ip = (splitip[0] + '.' + splitip[1] + '.' + splitip[2] + '.*'); } // If this is IPv4, keep only the 3 first
5580
var cutoffTime = Date.now() - (parent.config.settings.maxinvalidlogin.time * 60000); // Time in minutes