mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Use X-Forwarded-For header on trusted proxies
If the controller is behind a proxy then the socket.remoteAddress will be that of the proxy instead of the end client that connected. In this case the X-Forwarded-For header will contain the address of the end client, but this should only be relied upon if the party connecting is a trusted proxy, otherwise a malicious user could spoof the IP address that is logged by providing a forged X-Forwarded-For header. Implement a trusted proxy list consisting of a configured list of IP addresses and/or CIDR blocks and use the last address[1] in the X-Forwarded-For header if the remoteAddress address of the connecting party is in the trusted proxy list. [1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
This commit is contained in:
1 parent
c2a3d4361a
commit
5f25aad273
6 files changed
+191
-9
No files matched your search
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const { Controller } = require("@clusterio/controller");
|
||||
const { ControllerConfig } = require("@clusterio/lib");
|
||||
|
||||
describe("controller/src/Controller", function() {
|
||||
describe("class Controller", function() {
|
||||
/** @type {Controller} */
|
||||
let controller;
|
||||
before(function() {
|
||||
controller = new Controller({}, [], "", new ControllerConfig("controller"));
|
||||
});
|
||||
function check(list, ip, present = true) {
|
||||
if (present === false) {
|
||||
assert(!list.check(ip), `${ip} unexpectedly in list`);
|
||||
} else {
|
||||
assert(list.check(ip), `${ip} missing from list`);
|
||||
}
|
||||
}
|
||||
describe(".parseTrustedProxies()", function() {
|
||||
it("should parse addresses", function() {
|
||||
controller.config.set("controller.trusted_proxies", "10.0.0.1");
|
||||
let list = controller.parseTrustedProxies();
|
||||
check(list, "10.0.0.1");
|
||||
check(list, "10.0.0.2", false);
|
||||
controller.config.set("controller.trusted_proxies", "10.0.0.1, 10.0.0.2, 10.1.2.3");
|
||||
list = controller.parseTrustedProxies();
|
||||
check(list, "10.0.0.1");
|
||||
check(list, "10.0.0.2");
|
||||
check(list, "10.0.0.3", false);
|
||||
});
|
||||
it("should ignore invalid entries", function() {
|
||||
controller.config.set("controller.trusted_proxies", "10.0.0.1, invalid, 0.0.0.0/200, 10.0.0.2");
|
||||
const list = controller.parseTrustedProxies();
|
||||
check(list, "10.0.0.1");
|
||||
check(list, "10.0.0.2");
|
||||
check(list, "10.0.0.3", false);
|
||||
});
|
||||
it("should parse CIDR blocks", function() {
|
||||
controller.config.set("controller.trusted_proxies", "10.0.0.1/24, 192.168.0.16/31");
|
||||
const list = controller.parseTrustedProxies();
|
||||
check(list, "10.0.0.0");
|
||||
check(list, "10.0.0.1");
|
||||
check(list, "10.0.0.10");
|
||||
check(list, "10.0.0.255");
|
||||
check(list, "10.0.1.0", false);
|
||||
check(list, "192.168.0.15", false);
|
||||
check(list, "192.168.0.16");
|
||||
check(list, "192.168.0.17");
|
||||
check(list, "192.168.0.18", false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const { Controller, WsServer } = require("@clusterio/controller");
|
||||
const { ControllerConfig } = require("@clusterio/lib");
|
||||
|
||||
describe("controller/src/WsServer", function() {
|
||||
describe("class WsServer", function() {
|
||||
/** @type {WsServer} */
|
||||
let wsServer;
|
||||
before(function() {
|
||||
let config = new ControllerConfig("controller");
|
||||
config.set("controller.trusted_proxies", "127.0.0.0/8, ::1");
|
||||
let controller = new Controller({}, [], "", config);
|
||||
wsServer = controller.wsServer;
|
||||
});
|
||||
describe(".remoteAddr()", function() {
|
||||
it("should resolve X-Forward-For header for trusted proxies", function() {
|
||||
const ipv4 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "127.0.0.1",
|
||||
remoteFamily: "IPv4",
|
||||
},
|
||||
headers: { "x-forwarded-for": "1.2.3.4" },
|
||||
});
|
||||
assert.equal(ipv4, "1.2.3.4");
|
||||
const ipv6 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "::1",
|
||||
remoteFamily: "IPv6",
|
||||
},
|
||||
headers: { "x-forwarded-for": "2000::1" },
|
||||
});
|
||||
assert.equal(ipv6, "2000::1");
|
||||
});
|
||||
it("should pick the last address in X-Forward-For header for trusted proxies", function() {
|
||||
const ipv4 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "127.0.0.1",
|
||||
remoteFamily: "IPv4",
|
||||
},
|
||||
headers: { "x-forwarded-for": "1.0.0.0, 2.0.0.0, 1.2.3.4" },
|
||||
});
|
||||
assert.equal(ipv4, "1.2.3.4");
|
||||
const ipv6 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "::1",
|
||||
remoteFamily: "IPv6",
|
||||
},
|
||||
headers: { "x-forwarded-for": "2001::, 2002::, 2000::1" },
|
||||
});
|
||||
assert.equal(ipv6, "2000::1");
|
||||
});
|
||||
it("should not resolve X-Forward-For header for untrusted proxies", function() {
|
||||
const ipv4 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "1.2.3.4",
|
||||
remoteFamily: "IPv4",
|
||||
},
|
||||
headers: { "x-forwarded-for": "127.0.0.1" },
|
||||
});
|
||||
assert.equal(ipv4, "1.2.3.4");
|
||||
const ipv6 = wsServer.remoteAddr({
|
||||
socket: {
|
||||
remoteAddress: "2000::1",
|
||||
remoteFamily: "IPv6",
|
||||
},
|
||||
headers: { "x-forwarded-for": "::1" },
|
||||
});
|
||||
assert.equal(ipv6, "2000::1");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user