mirror of https://github.com/openclaw/openclaw.git
fix(infra): make browser relay bind address configurable
Add browser.relayBindHost config option so the Chrome extension relay server can bind to a non-loopback address (e.g. 0.0.0.0 for WSL2). Defaults to 127.0.0.1 when unset, preserving current behavior. Closes #39214 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0692f71c6f
commit
436ae8a07c
|
|
@ -36,6 +36,7 @@ export type ResolvedBrowserConfig = {
|
|||
profiles: Record<string, BrowserProfileConfig>;
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
extraArgs: string[];
|
||||
relayBindHost?: string;
|
||||
};
|
||||
|
||||
export type ResolvedBrowserProfile = {
|
||||
|
|
@ -291,6 +292,7 @@ export function resolveBrowserConfig(
|
|||
? cfg.extraArgs.filter((a): a is string => typeof a === "string" && a.trim().length > 0)
|
||||
: [];
|
||||
const ssrfPolicy = resolveBrowserSsrFPolicy(cfg);
|
||||
const relayBindHost = cfg?.relayBindHost?.trim() || undefined;
|
||||
|
||||
return {
|
||||
enabled,
|
||||
|
|
@ -312,6 +314,7 @@ export function resolveBrowserConfig(
|
|||
profiles,
|
||||
ssrfPolicy,
|
||||
extraArgs,
|
||||
relayBindHost,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1168,4 +1168,36 @@ describe("chrome extension relay server", () => {
|
|||
);
|
||||
await new Promise<void>((resolve) => blocker.close(() => resolve()));
|
||||
});
|
||||
|
||||
it(
|
||||
"respects bindHost override to bind on a non-loopback address",
|
||||
async () => {
|
||||
const port = await getFreePort();
|
||||
cdpUrl = `http://127.0.0.1:${port}`;
|
||||
const relay = await ensureChromeExtensionRelayServer({
|
||||
cdpUrl,
|
||||
bindHost: "0.0.0.0",
|
||||
});
|
||||
expect(relay.port).toBe(port);
|
||||
|
||||
// Relay should be reachable on loopback (0.0.0.0 accepts all interfaces).
|
||||
const res = await fetch(`http://127.0.0.1:${port}/`);
|
||||
expect(res.status).toBe(200);
|
||||
},
|
||||
RELAY_TEST_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
it(
|
||||
"defaults bindHost to cdpUrl host when not specified",
|
||||
async () => {
|
||||
const port = await getFreePort();
|
||||
cdpUrl = `http://127.0.0.1:${port}`;
|
||||
const relay = await ensureChromeExtensionRelayServer({ cdpUrl });
|
||||
expect(relay.host).toBe("127.0.0.1");
|
||||
|
||||
const res = await fetch(`http://127.0.0.1:${port}/`);
|
||||
expect(res.status).toBe(200);
|
||||
},
|
||||
RELAY_TEST_TIMEOUT_MS,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -223,11 +223,13 @@ export function getChromeExtensionRelayAuthHeaders(url: string): Record<string,
|
|||
|
||||
export async function ensureChromeExtensionRelayServer(opts: {
|
||||
cdpUrl: string;
|
||||
bindHost?: string;
|
||||
}): Promise<ChromeExtensionRelayServer> {
|
||||
const info = parseBaseUrl(opts.cdpUrl);
|
||||
if (!isLoopbackHost(info.host)) {
|
||||
throw new Error(`extension relay requires loopback cdpUrl host (got ${info.host})`);
|
||||
}
|
||||
const bindHost = opts.bindHost ?? info.host;
|
||||
|
||||
const existing = relayRuntimeByPort.get(info.port);
|
||||
if (existing) {
|
||||
|
|
@ -962,7 +964,7 @@ export async function ensureChromeExtensionRelayServer(opts: {
|
|||
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.listen(info.port, info.host, () => resolve());
|
||||
server.listen(info.port, bindHost, () => resolve());
|
||||
server.once("error", reject);
|
||||
});
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,10 @@ export function createProfileAvailability({
|
|||
|
||||
if (isExtension) {
|
||||
if (!httpReachable) {
|
||||
await ensureChromeExtensionRelayServer({ cdpUrl: profile.cdpUrl });
|
||||
await ensureChromeExtensionRelayServer({
|
||||
cdpUrl: profile.cdpUrl,
|
||||
bindHost: current.resolved.relayBindHost,
|
||||
});
|
||||
if (!(await isHttpReachable(PROFILE_ATTACH_RETRY_TIMEOUT_MS))) {
|
||||
throw new Error(
|
||||
`Chrome extension relay for profile "${profile.name}" is not reachable at ${profile.cdpUrl}.`,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ export async function ensureExtensionRelayForProfiles(params: {
|
|||
if (!profile || profile.driver !== "extension") {
|
||||
continue;
|
||||
}
|
||||
await ensureChromeExtensionRelayServer({ cdpUrl: profile.cdpUrl }).catch((err) => {
|
||||
await ensureChromeExtensionRelayServer({
|
||||
cdpUrl: profile.cdpUrl,
|
||||
bindHost: params.resolved.relayBindHost,
|
||||
}).catch((err) => {
|
||||
params.onWarn(`Chrome extension relay init failed for profile "${name}": ${String(err)}`);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,4 +66,10 @@ export type BrowserConfig = {
|
|||
* Example: ["--window-size=1920,1080", "--disable-infobars"]
|
||||
*/
|
||||
extraArgs?: string[];
|
||||
/**
|
||||
* Bind address for the Chrome extension relay server.
|
||||
* Default: "127.0.0.1". Set to "0.0.0.0" for WSL2 or other environments where
|
||||
* the relay must be reachable from a different network namespace.
|
||||
*/
|
||||
relayBindHost?: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ export const OpenClawSchema = z
|
|||
)
|
||||
.optional(),
|
||||
extraArgs: z.array(z.string()).optional(),
|
||||
relayBindHost: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue