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