mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 01:11:16 +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(
|
||||
req?: IncomingMessage,
|
||||
trustedProxies?: string[],
|
||||
allowRealIpFallback = false,
|
||||
_trustedProxies?: string[],
|
||||
_allowRealIpFallback = false,
|
||||
): boolean {
|
||||
if (!req) {
|
||||
return false;
|
||||
@@ -132,12 +132,7 @@ export function isLocalDirectRequest(
|
||||
if (!hasForwarded) {
|
||||
return isLoopbackAddress(req.socket?.remoteAddress);
|
||||
}
|
||||
|
||||
// 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);
|
||||
return false;
|
||||
}
|
||||
|
||||
function getTailscaleUser(req?: IncomingMessage): TailscaleUser | null {
|
||||
|
||||
@@ -76,18 +76,6 @@ describe("resolveGatewayRuntimeConfig", () => {
|
||||
expectedMessage:
|
||||
"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",
|
||||
cfg: {
|
||||
@@ -106,6 +94,22 @@ describe("resolveGatewayRuntimeConfig", () => {
|
||||
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", () => {
|
||||
|
||||
@@ -11,12 +11,7 @@ import {
|
||||
} from "./auth.js";
|
||||
import { normalizeControlUiBasePath } from "./control-ui-shared.js";
|
||||
import { resolveHooksConfig } from "./hooks.js";
|
||||
import {
|
||||
isLoopbackHost,
|
||||
isTrustedProxyAddress,
|
||||
isValidIPv4,
|
||||
resolveGatewayBindHost,
|
||||
} from "./net.js";
|
||||
import { isLoopbackHost, isValidIPv4, resolveGatewayBindHost } from "./net.js";
|
||||
import { mergeGatewayTailscaleConfig } from "./startup-auth.js";
|
||||
|
||||
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",
|
||||
);
|
||||
}
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user