socketio: Mimic what Express does to get client IP address

This also makes it easier for plugins to get the client IP address.
pull/4406/head
Richard Hansen 2020-10-06 19:44:34 -04:00 committed by John McLear
parent ba6bdf35be
commit 661a89355f
5 changed files with 17 additions and 35 deletions

View File

@ -35,7 +35,6 @@ var _ = require('underscore');
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
var channels = require("channels");
var stats = require('../stats');
var remoteAddress = require("../utils/RemoteAddress").remoteAddress;
const assert = require('assert').strict;
const nodeify = require("nodeify");
const { RateLimiterMemory } = require('rate-limiter-flexible');
@ -127,19 +126,11 @@ exports.handleDisconnect = async function(client)
// if this connection was already etablished with a handshake, send a disconnect message to the others
if (session && session.author) {
// Get the IP address from our persistant object
let ip = remoteAddress[client.id];
// Anonymize the IP address if IP logging is disabled
if (settings.disableIPlogging) {
ip = 'ANONYMOUS';
}
const {session: {user} = {}} = client.client.request;
accessLogger.info('[LEAVE]' +
` pad:${session.padId}` +
` socket:${client.id}` +
` IP:${ip}` +
` IP:${settings.disableIPlogging ? 'ANONYMOUS' : client.request.ip}` +
` authorID:${session.author}` +
((user && user.username) ? ` username:${user.username}` : ''));
@ -181,11 +172,11 @@ exports.handleMessage = async function(client, message)
var env = process.env.NODE_ENV || 'development';
if (env === 'production') {
const clientIPAddress = remoteAddress[client.id];
try {
await rateLimiter.consume(clientIPAddress); // consume 1 point per event from IP
}catch(e){
console.warn("Rate limited: ", clientIPAddress, " to reduce the amount of rate limiting that happens edit the rateLimit values in settings.json");
await rateLimiter.consume(client.request.ip); // consume 1 point per event from IP
} catch (e) {
console.warn(`Rate limited: ${client.request.ip} to reduce the amount of rate limiting ` +
'that happens edit the rateLimit values in settings.json');
stats.meter('rateLimited').mark();
client.json.send({disconnect:"rateLimited"});
return;
@ -239,7 +230,7 @@ exports.handleMessage = async function(client, message)
'Rejecting message from client because the author ID changed mid-session.' +
' Bad or missing token or sessionID?' +
` socket:${client.id}` +
` IP:${settings.disableIPlogging ? ANONYMOUS : remoteAddress[client.id]}` +
` IP:${settings.disableIPlogging ? 'ANONYMOUS' : client.request.ip}` +
` originalAuthorID:${thisSession.author}` +
` newAuthorID:${authorID}` +
((user && user.username) ? ` username:${user.username}` : '') +
@ -967,19 +958,11 @@ async function handleClientReady(client, message, authorID)
sessionInfo.readonly =
padIds.readonly || !webaccess.userCanModify(message.padId, client.client.request);
// Log creation/(re-)entering of a pad
let ip = remoteAddress[client.id];
// Anonymize the IP address if IP logging is disabled
if (settings.disableIPlogging) {
ip = 'ANONYMOUS';
}
const {session: {user} = {}} = client.client.request;
accessLogger.info(`[${pad.head > 0 ? 'ENTER' : 'CREATE'}]` +
` pad:${padIds.padId}` +
` socket:${client.id}` +
` IP:${ip}` +
` IP:${settings.disableIPlogging ? 'ANONYMOUS' : client.request.ip}` +
` authorID:${authorID}` +
((user && user.username) ? ` username:${user.username}` : ''));

View File

@ -23,7 +23,6 @@ var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
var securityManager = require("../db/SecurityManager");
var readOnlyManager = require("../db/ReadOnlyManager");
var remoteAddress = require("../utils/RemoteAddress").remoteAddress;
var settings = require('../utils/Settings');
/**
@ -56,15 +55,6 @@ exports.setSocketIO = function(_socket) {
socket.sockets.on('connection', function(client)
{
// Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js
// Fixed by having a persistant object, ideally this would actually be in the database layer
// TODO move to database layer
if (settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined) {
remoteAddress[client.id] = client.handshake.headers['x-forwarded-for'];
} else {
remoteAddress[client.id] = client.handshake.address;
}
// wrap the original send function to log the messages
client._send = client.send;
client.send = function(message) {

View File

@ -1,4 +1,5 @@
const express = require("../express");
const proxyaddr = require('proxy-addr');
var settings = require('../../utils/Settings');
var socketio = require('socket.io');
var socketIORouter = require("../../handler/SocketIORouter");
@ -38,6 +39,14 @@ exports.expressCreateServer = function (hook_name, args, cb) {
io.use((socket, next) => {
const req = socket.request;
// Express sets req.ip but socket.io does not. Replicate Express's behavior here.
if (req.ip == null) {
if (settings.trustProxy) {
req.ip = proxyaddr(req, args.app.get('trust proxy fn'));
} else {
req.ip = socket.handshake.address;
}
}
if (!req.headers.cookie) {
// socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the
// token and express_sid cookies have to be passed via a query parameter for unit tests.

View File

@ -1 +0,0 @@
exports.remoteAddress = {};

View File

@ -55,6 +55,7 @@
"nodeify": "1.0.1",
"npm": "6.14.8",
"openapi-backend": "2.4.1",
"proxy-addr": "^2.0.6",
"rate-limiter-flexible": "^2.1.4",
"rehype": "^10.0.0",
"rehype-format": "^3.0.1",