mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
fix(gateway): trusted-proxy auth rejected when bind=loopback (#20097)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 8de62f1a8f
Co-authored-by: xinhuagu <562450+xinhuagu@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Gateway/Auth: allow trusted-proxy mode with loopback bind for same-host reverse-proxy deployments, while still requiring configured `gateway.trustedProxies`. (#20097) thanks @xinhuagu.
|
||||
- Gateway/Auth: allow authenticated clients across roles/scopes to call `health` while preserving role and scope enforcement for non-health methods. (#19699) thanks @Nachx639.
|
||||
- Gateway/Hooks: include transform export name in hook-transform cache keys so distinct exports from the same module do not reuse the wrong cached transform function. (#13855) thanks @mcaxtr.
|
||||
- Gateway/Control UI: return 404 for missing static-asset paths instead of serving SPA fallback HTML, while preserving client-route fallback behavior for extensionless and non-asset dotted paths. (#12060) thanks @mcaxtr.
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("promptGatewayConfig", () => {
|
||||
requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],
|
||||
allowUsers: ["nick@example.com"],
|
||||
});
|
||||
expect(result.config.gateway?.bind).toBe("lan");
|
||||
expect(result.config.gateway?.bind).toBe("loopback");
|
||||
expect(result.config.gateway?.trustedProxies).toEqual(["10.0.1.10", "192.168.1.5"]);
|
||||
});
|
||||
|
||||
@@ -141,7 +141,7 @@ describe("promptGatewayConfig", () => {
|
||||
userHeader: "x-remote-user",
|
||||
// requiredHeaders and allowUsers should be undefined when empty
|
||||
});
|
||||
expect(result.config.gateway?.bind).toBe("lan");
|
||||
expect(result.config.gateway?.bind).toBe("loopback");
|
||||
expect(result.config.gateway?.trustedProxies).toEqual(["10.0.0.1"]);
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ describe("promptGatewayConfig", () => {
|
||||
tailscaleMode: "serve",
|
||||
textQueue: ["18789", "x-forwarded-user", "", "", "10.0.0.1"],
|
||||
});
|
||||
expect(result.config.gateway?.bind).toBe("lan");
|
||||
expect(result.config.gateway?.bind).toBe("loopback");
|
||||
expect(result.config.gateway?.tailscale?.mode).toBe("off");
|
||||
expect(result.config.gateway?.tailscale?.resetOnExit).toBe(false);
|
||||
});
|
||||
|
||||
@@ -142,10 +142,8 @@ export async function promptGatewayConfig(
|
||||
authMode = "password";
|
||||
}
|
||||
|
||||
if (authMode === "trusted-proxy" && bind === "loopback") {
|
||||
note("Trusted proxy auth requires network bind. Adjusting bind to lan.", "Note");
|
||||
bind = "lan";
|
||||
}
|
||||
// trusted-proxy + loopback is valid when the reverse proxy runs on the same
|
||||
// host (e.g. cloudflared, nginx, Caddy). trustedProxies must include 127.0.0.1.
|
||||
if (authMode === "trusted-proxy" && tailscaleMode !== "off") {
|
||||
note(
|
||||
"Trusted proxy auth is incompatible with Tailscale serve/funnel. Disabling Tailscale.",
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("resolveGatewayRuntimeConfig", () => {
|
||||
expect(result.bindHost).toBe("0.0.0.0");
|
||||
});
|
||||
|
||||
it("should reject loopback binding with trusted-proxy auth mode", async () => {
|
||||
it("should allow loopback binding with trusted-proxy auth mode", async () => {
|
||||
const cfg = {
|
||||
gateway: {
|
||||
bind: "loopback" as const,
|
||||
@@ -40,7 +40,28 @@ describe("resolveGatewayRuntimeConfig", () => {
|
||||
userHeader: "x-forwarded-user",
|
||||
},
|
||||
},
|
||||
trustedProxies: ["192.168.1.1"],
|
||||
trustedProxies: ["127.0.0.1"],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await resolveGatewayRuntimeConfig({
|
||||
cfg,
|
||||
port: 18789,
|
||||
});
|
||||
expect(result.bindHost).toBe("127.0.0.1");
|
||||
});
|
||||
|
||||
it("should reject loopback trusted-proxy without trustedProxies configured", async () => {
|
||||
const cfg = {
|
||||
gateway: {
|
||||
bind: "loopback" as const,
|
||||
auth: {
|
||||
mode: "trusted-proxy" as const,
|
||||
trustedProxy: {
|
||||
userHeader: "x-forwarded-user",
|
||||
},
|
||||
},
|
||||
trustedProxies: [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -49,7 +70,9 @@ describe("resolveGatewayRuntimeConfig", () => {
|
||||
cfg,
|
||||
port: 18789,
|
||||
}),
|
||||
).rejects.toThrow("gateway auth mode=trusted-proxy makes no sense with bind=loopback");
|
||||
).rejects.toThrow(
|
||||
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured",
|
||||
);
|
||||
});
|
||||
|
||||
it("should reject trusted-proxy without trustedProxies configured", async () => {
|
||||
|
||||
@@ -117,11 +117,6 @@ export async function resolveGatewayRuntimeConfig(params: {
|
||||
}
|
||||
|
||||
if (authMode === "trusted-proxy") {
|
||||
if (isLoopbackHost(bindHost)) {
|
||||
throw new Error(
|
||||
"gateway auth mode=trusted-proxy makes no sense with bind=loopback; use bind=lan or bind=custom with gateway.trustedProxies configured",
|
||||
);
|
||||
}
|
||||
if (trustedProxies.length === 0) {
|
||||
throw new Error(
|
||||
"gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP",
|
||||
|
||||
Reference in New Issue
Block a user