Refactor ip range checks and support ipv6

This commit is contained in:
Jesús Ramos 2023-03-30 10:56:46 +00:00
parent c8ef1d6ccf
commit 287c2d1bbc
3 changed files with 41 additions and 44 deletions

View File

@ -18,6 +18,7 @@
"dockerode": "^3.3.4",
"follow-redirects": "^1.15.2",
"i18next": "^21.9.2",
"ipaddr.js": "^2.0.1",
"js-yaml": "^4.1.0",
"json-rpc-2.0": "^1.4.1",
"memory-cache": "^0.2.0",

View File

@ -25,6 +25,9 @@ dependencies:
i18next:
specifier: ^21.9.2
version: 21.10.0
ipaddr.js:
specifier: ^2.0.1
version: 2.0.1
js-yaml:
specifier: ^4.1.0
version: 4.1.0
@ -1924,6 +1927,11 @@ packages:
engines: {node: '>= 0.10'}
dev: false
/ipaddr.js@2.0.1:
resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==}
engines: {node: '>= 10'}
dev: false
/is-arguments@1.1.1:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}

View File

@ -2,51 +2,46 @@ import { getClientIp } from "@supercharge/request-ip";
import { getSettings } from "utils/config/config";
function checkIPRange(ip, ipSpace) {
// Check if the given IP is in the ipSpace address space using
// CIDR notation. If ipSpace is a plain IPv4 we just compare them.
const ipSpaceParts = ipSpace.split("/");
if (ipSpaceParts.length === 1) {
if (ip === ipSpace) {
return true;
const ipaddr = require("ipaddr.js");
function checkSingleIPRange(ip, cidr) {
try {
const ipAddr = ipaddr.process(ip);
// If the CIDR is not in the format of x.x.x.x/y, we assume it is a single IP
if (!cidr.includes("/")) {
const cidrIP = ipaddr.process(cidr);
// If both are IPv6, we need to normalize the strings
if (ipAddr.kind() === "ipv6" && cidrIP.kind() === "ipv6") {
return (ipAddr.toNormalizedString() === cidrIP.toNormalizedString());
}
return ipAddr.toString() === cidrIP.toString();
}
} else if (ipSpaceParts.length === 2) {
const ipParts = ip.split(".");
const ipNum = parseInt(ipParts[0], 10) * 256 * 256 * 256 + parseInt(ipParts[1], 10) * 256 * 256 + parseInt(ipParts[2], 10) * 256 + parseInt(ipParts[3], 10);
const ipSpaceNum = parseInt(ipSpaceParts[0].split(".")[0], 10) * 256 * 256 * 256 + parseInt(ipSpaceParts[0].split(".")[1], 10) * 256 * 256 + parseInt(ipSpaceParts[0].split(".")[2], 10) * 256 + parseInt(ipSpaceParts[0].split(".")[3], 10);
const mask = 32 - parseInt(ipSpaceParts[1], 10);
// eslint-disable-next-line no-bitwise
const maskNum = 0xffffffff << mask;
// eslint-disable-next-line no-bitwise
if ((ipNum & maskNum) === (ipSpaceNum & maskNum)) {
return true;
}
} else {
throw new Error(`Invalid ipSpace: ${ipSpace}`);
// Otherwise, we assume it is a CIDR range
const range = ipaddr.parseCIDR(cidr);
return ipAddr.match(range);
} catch (e) {
return false;
}
}
function checkIPRange(ip, range) {
if (typeof range === "string") {
return checkSingleIPRange(ip, range);
}
if (Array.isArray(range)) {
return range.find((cidr) => checkSingleIPRange(ip, cidr)) !== undefined;
}
return false;
}
function isRequestProxied(remoteAddress) {
export function isRequestProxied(remoteAddress) {
const settings = getSettings();
// Check if trustedproxies is set
const trustedProxies = settings?.trustedproxies;
// If trustedproxies is set, check if the client IP
// is in the trustedproxies address space using CIDR notation.
if (trustedProxies) {
// Get the connection IP and strip IPv6 from the hybrid IPv4-IPv6 socket
const ip = remoteAddress.replace(/^.*:/, '');
for (let i = 0; i < trustedProxies.length; i += 1) {
const proxy = trustedProxies[i].trim();
const inRange = checkIPRange(ip, proxy);
if (inRange) {
return true;
}
}
}
return false;
// is in the trustedproxies address space.
return trustedProxies ? checkIPRange(remoteAddress, trustedProxies) : false;
}
export function getRealClientIP(req) {
@ -63,17 +58,10 @@ export function isInLocalScope(req) {
const localScope = settings?.localscope;
// If localscope is set, check if the client IP
// is in the localscope address space using CIDR notation.
// is in the localscope address space.
if (localScope) {
const ip = getRealClientIP(req);
for (let i = 0; i < localScope.length; i += 1) {
const localIP = localScope[i].trim();
const inRange = checkIPRange(ip, localIP)
if (inRange) {
return true;
}
}
return checkIPRange(ip, localScope);
}
return false;
}