mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:21:35 +07:00
fix(gateway): align trusted-proxy loopback validation
This commit is contained in:
@@ -114,8 +114,8 @@ function resolveTailscaleClientIp(req?: IncomingMessage): string | undefined {
|
|||||||
|
|
||||||
export function isLocalDirectRequest(
|
export function isLocalDirectRequest(
|
||||||
req?: IncomingMessage,
|
req?: IncomingMessage,
|
||||||
trustedProxies?: string[],
|
_trustedProxies?: string[],
|
||||||
allowRealIpFallback = false,
|
_allowRealIpFallback = false,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (!req) {
|
if (!req) {
|
||||||
return false;
|
return false;
|
||||||
@@ -132,12 +132,7 @@ export function isLocalDirectRequest(
|
|||||||
if (!hasForwarded) {
|
if (!hasForwarded) {
|
||||||
return isLoopbackAddress(req.socket?.remoteAddress);
|
return isLoopbackAddress(req.socket?.remoteAddress);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// When forwarded headers are present, resolveRequestClientIp intentionally fails closed
|
|
||||||
// if the proxy chain is missing or invalid. Do not fall back to the raw socket address here,
|
|
||||||
// or proxied requests can be reclassified as local-direct.
|
|
||||||
const clientIp = resolveRequestClientIp(req, trustedProxies, allowRealIpFallback) ?? "";
|
|
||||||
return isLoopbackAddress(clientIp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTailscaleUser(req?: IncomingMessage): TailscaleUser | null {
|
function getTailscaleUser(req?: IncomingMessage): TailscaleUser | null {
|
||||||
|
|||||||
@@ -76,18 +76,6 @@ describe("resolveGatewayRuntimeConfig", () => {
|
|||||||
expectedMessage:
|
expectedMessage:
|
||||||
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured",
|
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "loopback binding without loopback trusted proxy",
|
|
||||||
cfg: {
|
|
||||||
gateway: {
|
|
||||||
bind: "loopback" as const,
|
|
||||||
auth: TRUSTED_PROXY_AUTH,
|
|
||||||
trustedProxies: ["10.0.0.1"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedMessage:
|
|
||||||
"gateway auth mode=trusted-proxy with bind=loopback requires gateway.trustedProxies to include 127.0.0.1, ::1, or a loopback CIDR",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "lan binding without trusted proxies",
|
name: "lan binding without trusted proxies",
|
||||||
cfg: {
|
cfg: {
|
||||||
@@ -106,6 +94,22 @@ describe("resolveGatewayRuntimeConfig", () => {
|
|||||||
expectedMessage,
|
expectedMessage,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows loopback binding with non-loopback trusted proxies", async () => {
|
||||||
|
const result = await resolveGatewayRuntimeConfig({
|
||||||
|
cfg: {
|
||||||
|
gateway: {
|
||||||
|
bind: "loopback",
|
||||||
|
auth: TRUSTED_PROXY_AUTH,
|
||||||
|
trustedProxies: ["10.0.0.1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
port: 18789,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.authMode).toBe("trusted-proxy");
|
||||||
|
expect(result.bindHost).toBe("127.0.0.1");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("token/password auth modes", () => {
|
describe("token/password auth modes", () => {
|
||||||
|
|||||||
@@ -11,12 +11,7 @@ import {
|
|||||||
} from "./auth.js";
|
} from "./auth.js";
|
||||||
import { normalizeControlUiBasePath } from "./control-ui-shared.js";
|
import { normalizeControlUiBasePath } from "./control-ui-shared.js";
|
||||||
import { resolveHooksConfig } from "./hooks.js";
|
import { resolveHooksConfig } from "./hooks.js";
|
||||||
import {
|
import { isLoopbackHost, isValidIPv4, resolveGatewayBindHost } from "./net.js";
|
||||||
isLoopbackHost,
|
|
||||||
isTrustedProxyAddress,
|
|
||||||
isValidIPv4,
|
|
||||||
resolveGatewayBindHost,
|
|
||||||
} from "./net.js";
|
|
||||||
import { mergeGatewayTailscaleConfig } from "./startup-auth.js";
|
import { mergeGatewayTailscaleConfig } from "./startup-auth.js";
|
||||||
|
|
||||||
export type GatewayRuntimeConfig = {
|
export type GatewayRuntimeConfig = {
|
||||||
@@ -152,16 +147,6 @@ export async function resolveGatewayRuntimeConfig(params: {
|
|||||||
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP",
|
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (isLoopbackHost(bindHost)) {
|
|
||||||
const hasLoopbackTrustedProxy =
|
|
||||||
isTrustedProxyAddress("127.0.0.1", trustedProxies) ||
|
|
||||||
isTrustedProxyAddress("::1", trustedProxies);
|
|
||||||
if (!hasLoopbackTrustedProxy) {
|
|
||||||
throw new Error(
|
|
||||||
"gateway auth mode=trusted-proxy with bind=loopback requires gateway.trustedProxies to include 127.0.0.1, ::1, or a loopback CIDR",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user