From c28d8ddff9342da501e60df23cbb79e3259a4306 Mon Sep 17 00:00:00 2001 From: Ben Scobie Date: Wed, 5 Oct 2022 16:45:21 +0100 Subject: [PATCH] Correctly handle multiple IPs in X-Forwarded-For (#2177) Co-authored-by: Louis Lam --- package.json | 4 +- server/uptime-kuma-server.js | 4 +- test/backend.spec.js | 83 +++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4f7da6815..1411168d0 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "start-server": "node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js", "build": "vite build --config ./config/vite.config.js", - "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", + "test": "node test/prepare-test-server.js && npm run jest-backend", "test-with-build": "npm run build && npm test", - "jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js", + "jest-backend": "cross-env TEST_BACKEND=1 jest --runInBand --detectOpenHandles --forceExit --config=./config/jest-backend.config.js", "tsc": "tsc", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js", "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine", diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 98de65a43..6e77e1fd2 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -138,7 +138,9 @@ class UptimeKumaServer { } if (await Settings.get("trustProxy")) { - return socket.client.conn.request.headers["x-forwarded-for"] + const forwardedFor = socket.client.conn.request.headers["x-forwarded-for"]; + + return (typeof forwardedFor === "string" ? forwardedFor.split(",")[0].trim() : null) || socket.client.conn.request.headers["x-real-ip"] || clientIP.replace(/^.*:/, ""); } else { diff --git a/test/backend.spec.js b/test/backend.spec.js index 6deb28534..5b9fa92c6 100644 --- a/test/backend.spec.js +++ b/test/backend.spec.js @@ -1,7 +1,11 @@ -const { genSecret, DOWN } = require("../src/util"); +const { genSecret, DOWN, log} = require("../src/util"); const utilServerRewire = require("../server/util-server"); const Discord = require("../server/notification-providers/discord"); const axios = require("axios"); +const { UptimeKumaServer } = require("../server/uptime-kuma-server"); +const Database = require("../server/database"); +const {Settings} = require("../server/settings"); +const fs = require("fs"); jest.mock("axios"); @@ -225,3 +229,80 @@ describe("The function filterAndJoin", () => { expect(result).toBe(""); }); }); + +describe("Test uptimeKumaServer.getClientIP()", () => { + it("should able to get a correct client IP", async () => { + Database.init({ + "data-dir": "./data/test" + }); + + if (! fs.existsSync(Database.path)) { + log.info("server", "Copying Database"); + fs.copyFileSync(Database.templatePath, Database.path); + } + + await Database.connect(true); + await Database.patch(); + + const fakeSocket = { + client: { + conn: { + remoteAddress: "192.168.10.10", + request: { + headers: { + } + } + } + } + } + const server = Object.create(UptimeKumaServer.prototype); + let ip = await server.getClientIP(fakeSocket); + + await Settings.set("trustProxy", false); + expect(await Settings.get("trustProxy")).toBe(false); + expect(ip).toBe("192.168.10.10"); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "10.10.10.10"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("192.168.10.10"); + + fakeSocket.client.conn.request.headers["x-real-ip"] = "20.20.20.20"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("192.168.10.10"); + + await Settings.set("trustProxy", true); + expect(await Settings.get("trustProxy")).toBe(true); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "10.10.10.10"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("10.10.10.10"); + + // x-real-ip + delete fakeSocket.client.conn.request.headers["x-forwarded-for"]; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("20.20.20.20"); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "2001:db8:85a3:8d3:1319:8a2e:370:7348"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("2001:db8:85a3:8d3:1319:8a2e:370:7348"); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("203.0.113.195"); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("203.0.113.195"); + + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("203.0.113.195"); + + // Elements are comma-separated, with optional whitespace surrounding the commas. + fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195 , 2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178"; + ip = await server.getClientIP(fakeSocket); + expect(ip).toBe("203.0.113.195"); + + await Database.close(); + }, 120000); +});