mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 19:08:27 +00:00
refactor(gateway): harden proxy client ip resolution
This commit is contained in:
@@ -146,45 +146,51 @@ function stripOptionalPort(ip: string): string {
|
||||
return ip;
|
||||
}
|
||||
|
||||
export function parseForwardedForClientIp(
|
||||
forwardedFor?: string,
|
||||
trustedProxies?: string[],
|
||||
): string | undefined {
|
||||
const entries = forwardedFor
|
||||
?.split(",")
|
||||
.map((entry) => entry.trim())
|
||||
.filter((entry) => entry.length > 0);
|
||||
if (!entries?.length) {
|
||||
function parseIpLiteral(raw: string | undefined): string | undefined {
|
||||
const trimmed = raw?.trim();
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!trustedProxies?.length) {
|
||||
const raw = entries.at(-1);
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
return normalizeIp(stripOptionalPort(raw));
|
||||
const stripped = stripOptionalPort(trimmed);
|
||||
const normalized = normalizeIp(stripped);
|
||||
if (!normalized || net.isIP(normalized) === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
||||
const normalized = normalizeIp(stripOptionalPort(entries[index]));
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
if (!isTrustedProxyAddress(normalized, trustedProxies)) {
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function parseRealIp(realIp?: string): string | undefined {
|
||||
const raw = realIp?.trim();
|
||||
if (!raw) {
|
||||
return parseIpLiteral(realIp);
|
||||
}
|
||||
|
||||
function resolveForwardedClientIp(params: {
|
||||
forwardedFor?: string;
|
||||
trustedProxies?: string[];
|
||||
}): string | undefined {
|
||||
const { forwardedFor, trustedProxies } = params;
|
||||
if (!trustedProxies?.length) {
|
||||
return undefined;
|
||||
}
|
||||
return normalizeIp(stripOptionalPort(raw));
|
||||
|
||||
const forwardedChain: string[] = [];
|
||||
for (const entry of forwardedFor?.split(",") ?? []) {
|
||||
const normalized = parseIpLiteral(entry);
|
||||
if (normalized) {
|
||||
forwardedChain.push(normalized);
|
||||
}
|
||||
}
|
||||
if (forwardedChain.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Walk right-to-left and return the first untrusted hop.
|
||||
for (let index = forwardedChain.length - 1; index >= 0; index -= 1) {
|
||||
const hop = forwardedChain[index];
|
||||
if (!isTrustedProxyAddress(hop, trustedProxies)) {
|
||||
return hop;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,11 +258,13 @@ export function isTrustedProxyAddress(ip: string | undefined, trustedProxies?: s
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGatewayClientIp(params: {
|
||||
export function resolveClientIp(params: {
|
||||
remoteAddr?: string;
|
||||
forwardedFor?: string;
|
||||
realIp?: string;
|
||||
trustedProxies?: string[];
|
||||
/** Default false: only trust X-Real-IP when explicitly enabled. */
|
||||
allowRealIpFallback?: boolean;
|
||||
}): string | undefined {
|
||||
const remote = normalizeIp(params.remoteAddr);
|
||||
if (!remote) {
|
||||
@@ -268,10 +276,17 @@ export function resolveGatewayClientIp(params: {
|
||||
// Fail closed when traffic comes from a trusted proxy but client-origin headers
|
||||
// are missing or invalid. Falling back to the proxy's own IP can accidentally
|
||||
// treat unrelated requests as local/trusted.
|
||||
return (
|
||||
parseForwardedForClientIp(params.forwardedFor, params.trustedProxies) ??
|
||||
parseRealIp(params.realIp)
|
||||
);
|
||||
const forwardedIp = resolveForwardedClientIp({
|
||||
forwardedFor: params.forwardedFor,
|
||||
trustedProxies: params.trustedProxies,
|
||||
});
|
||||
if (forwardedIp) {
|
||||
return forwardedIp;
|
||||
}
|
||||
if (params.allowRealIpFallback) {
|
||||
return parseRealIp(params.realIp);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isLocalGatewayAddress(ip: string | undefined): boolean {
|
||||
|
||||
Reference in New Issue
Block a user