Correctly handle multiple IPs in X-Forwarded-For (#2177)

Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
This commit is contained in:
Ben Scobie 2022-10-05 16:45:21 +01:00 committed by GitHub
parent 528a615fb2
commit c28d8ddff9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 4 deletions

View File

@ -23,9 +23,9 @@
"start-server": "node server/server.js", "start-server": "node server/server.js",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"build": "vite build --config ./config/vite.config.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", "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", "tsc": "tsc",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js", "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", "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",

View File

@ -138,7 +138,9 @@ class UptimeKumaServer {
} }
if (await Settings.get("trustProxy")) { 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"] || socket.client.conn.request.headers["x-real-ip"]
|| clientIP.replace(/^.*:/, ""); || clientIP.replace(/^.*:/, "");
} else { } else {

View File

@ -1,7 +1,11 @@
const { genSecret, DOWN } = require("../src/util"); const { genSecret, DOWN, log} = require("../src/util");
const utilServerRewire = require("../server/util-server"); const utilServerRewire = require("../server/util-server");
const Discord = require("../server/notification-providers/discord"); const Discord = require("../server/notification-providers/discord");
const axios = require("axios"); 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"); jest.mock("axios");
@ -225,3 +229,80 @@ describe("The function filterAndJoin", () => {
expect(result).toBe(""); 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);
});