fix: tighten gateway bind auth diagnostics

This commit is contained in:
Peter Steinberger
2026-01-08 07:42:50 +01:00
parent debfce5a77
commit 5565dcd447
8 changed files with 196 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ import type { Command } from "commander";
import {
CONFIG_PATH_CLAWDBOT,
loadConfig,
readConfigFileSnapshot,
resolveGatewayPort,
} from "../config/config.js";
import {
@@ -70,6 +71,26 @@ function describeUnknownError(err: unknown): string {
return "Unknown error";
}
function extractGatewayMiskeys(parsed: unknown): {
hasGatewayToken: boolean;
hasRemoteToken: boolean;
} {
if (!parsed || typeof parsed !== "object") {
return { hasGatewayToken: false, hasRemoteToken: false };
}
const gateway = (parsed as Record<string, unknown>).gateway;
if (!gateway || typeof gateway !== "object") {
return { hasGatewayToken: false, hasRemoteToken: false };
}
const hasGatewayToken = "token" in (gateway as Record<string, unknown>);
const remote = (gateway as Record<string, unknown>).remote;
const hasRemoteToken =
remote && typeof remote === "object"
? "token" in (remote as Record<string, unknown>)
: false;
return { hasGatewayToken, hasRemoteToken };
}
function renderGatewayServiceStopHints(): string[] {
switch (process.platform) {
case "darwin":
@@ -368,7 +389,7 @@ export function registerGatewayCli(program: Command) {
);
} else {
defaultRuntime.error(
"Gateway start blocked: set gateway.mode=local (or pass --allow-unconfigured).",
`Gateway start blocked: set gateway.mode=local (current: ${mode ?? "unset"}) or pass --allow-unconfigured.`,
);
}
defaultRuntime.exit(1);
@@ -390,6 +411,72 @@ export function registerGatewayCli(program: Command) {
return;
}
const snapshot = await readConfigFileSnapshot().catch(() => null);
const miskeys = extractGatewayMiskeys(snapshot?.parsed);
const authModeFromConfig = cfg.gateway?.auth?.mode;
const tokenValue =
opts.token ??
cfg.gateway?.auth?.token ??
process.env.CLAWDBOT_GATEWAY_TOKEN;
const passwordValue =
opts.password ??
cfg.gateway?.auth?.password ??
process.env.CLAWDBOT_GATEWAY_PASSWORD;
const resolvedAuthMode =
authMode ??
authModeFromConfig ??
(passwordValue ? "password" : tokenValue ? "token" : "none");
const authHints: string[] = [];
if (miskeys.hasGatewayToken) {
authHints.push(
'Found "gateway.token" in config. Use "gateway.auth.token" instead.',
);
}
if (miskeys.hasRemoteToken) {
authHints.push(
'"gateway.remote.token" is for remote CLI calls; it does not enable local gateway auth.',
);
}
if (resolvedAuthMode === "token" && !tokenValue) {
defaultRuntime.error(
[
"Gateway auth is set to token, but no token is configured.",
"Set gateway.auth.token (or CLAWDBOT_GATEWAY_TOKEN), or pass --token.",
...authHints,
]
.filter(Boolean)
.join("\n"),
);
defaultRuntime.exit(1);
return;
}
if (resolvedAuthMode === "password" && !passwordValue) {
defaultRuntime.error(
[
"Gateway auth is set to password, but no password is configured.",
"Set gateway.auth.password (or CLAWDBOT_GATEWAY_PASSWORD), or pass --password.",
...authHints,
]
.filter(Boolean)
.join("\n"),
);
defaultRuntime.exit(1);
return;
}
if (bind !== "loopback" && resolvedAuthMode === "none") {
defaultRuntime.error(
[
`Refusing to bind gateway to ${bind} without auth.`,
"Set gateway.auth.token (or CLAWDBOT_GATEWAY_TOKEN) or pass --token.",
...authHints,
]
.filter(Boolean)
.join("\n"),
);
defaultRuntime.exit(1);
return;
}
try {
await runGatewayLoop({
runtime: defaultRuntime,