mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix: Hook ingress token unlocks password-mode gateway auth
This commit is contained in:
@@ -43,6 +43,7 @@ For webhook ingress, it warns when:
|
||||
- overrides are enabled without `hooks.allowedSessionKeyPrefixes`
|
||||
|
||||
If Gateway password auth is supplied only at startup, pass the same value to `openclaw security audit --auth password --password <password>` so the audit can check it against `hooks.token`.
|
||||
Password-mode reuse is an audit finding for compatibility; rotate one of the secrets instead of expecting Gateway startup to reject that configuration.
|
||||
|
||||
It also warns when sandbox Docker settings are configured while sandbox mode is off, when `gateway.nodes.denyCommands` uses ineffective pattern-like/unknown entries (exact node command-name matching only, not shell-text filtering), when `gateway.nodes.allowCommands` explicitly enables dangerous node commands, when global `tools.profile="minimal"` is overridden by agent tool profiles, when write/edit tools are disabled but `exec` is still available without a constraining sandbox filesystem boundary, when open groups expose runtime/filesystem tools without sandbox/workspace guards, and when installed plugin tools may be reachable under permissive tool policy.
|
||||
It also flags `gateway.allowRealIpFallback=true` (header-spoofing risk if proxies are misconfigured) and `discovery.mdns.mode="full"` (metadata leakage via mDNS TXT records).
|
||||
|
||||
@@ -688,7 +688,7 @@ Validation and safety notes:
|
||||
|
||||
- `hooks.enabled=true` requires a non-empty `hooks.token`.
|
||||
- `hooks.token` must be distinct from `gateway.auth.token` / `OPENCLAW_GATEWAY_TOKEN`; reusing the Gateway token fails startup validation.
|
||||
- `openclaw security audit` also flags `hooks.token` reuse of active Gateway password auth (`gateway.auth.password` / `OPENCLAW_GATEWAY_PASSWORD`, or `--auth password --password <password>`) as a critical finding.
|
||||
- `openclaw security audit` also flags `hooks.token` reuse of active Gateway password auth (`gateway.auth.password` / `OPENCLAW_GATEWAY_PASSWORD`, or `--auth password --password <password>`) as a critical finding; password-mode reuse stays startup-compatible and should be repaired by rotating one of the secrets.
|
||||
- `hooks.path` cannot be `/`; use a dedicated subpath such as `/hooks`.
|
||||
- If `hooks.allowRequestSessionKey=true`, constrain `hooks.allowedSessionKeyPrefixes` (for example `["hook:"]`).
|
||||
- If a mapping or preset uses a templated `sessionKey`, set `hooks.allowedSessionKeyPrefixes` and `hooks.allowRequestSessionKey=true`. Static mapping keys do not require that opt-in.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import type { ResolvedGatewayAuth } from "./auth.js";
|
||||
|
||||
export type GatewayAuthSharedSecretLabel = "gateway auth token" | "gateway auth password";
|
||||
|
||||
type ActiveGatewaySharedSecret = {
|
||||
label: GatewayAuthSharedSecretLabel;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
function listActiveGatewaySharedSecrets(auth: ResolvedGatewayAuth): ActiveGatewaySharedSecret[] {
|
||||
if (auth.mode === "token") {
|
||||
return [{ label: "gateway auth token", value: auth.token }];
|
||||
}
|
||||
if (auth.mode === "password" || auth.mode === "trusted-proxy") {
|
||||
return [{ label: "gateway auth password", value: auth.password }];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function findGatewayAuthLabelMatchingHooksToken(params: {
|
||||
hooksToken?: string;
|
||||
auth: ResolvedGatewayAuth;
|
||||
}): GatewayAuthSharedSecretLabel | undefined {
|
||||
const hooksToken = normalizeOptionalString(params.hooksToken);
|
||||
if (!hooksToken) {
|
||||
return undefined;
|
||||
}
|
||||
return listActiveGatewaySharedSecrets(params.auth).find(
|
||||
(candidate) => normalizeOptionalString(candidate.value) === hooksToken,
|
||||
)?.label;
|
||||
}
|
||||
@@ -11,10 +11,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { AgentToolsConfig } from "../config/types.tools.js";
|
||||
import { resolveGatewayAuth, type ResolvedGatewayAuth } from "../gateway/auth.js";
|
||||
import { resolveAllowedAgentIds } from "../gateway/hooks-policy.js";
|
||||
import {
|
||||
findGatewayAuthLabelMatchingHooksToken,
|
||||
type GatewayAuthSharedSecretLabel,
|
||||
} from "../gateway/hooks-token-auth-reuse.js";
|
||||
import {
|
||||
DEFAULT_DANGEROUS_NODE_COMMANDS,
|
||||
listDangerousPluginNodeCommands,
|
||||
@@ -50,6 +46,12 @@ export type GatewayHttpNoAuthAuditOptions = {
|
||||
gatewayAuthOverride?: Pick<GatewayAuthConfig, "mode" | "token" | "password">;
|
||||
};
|
||||
|
||||
type GatewayAuthSharedSecretLabel = "gateway auth token" | "gateway auth password";
|
||||
type ActiveGatewaySharedSecret = {
|
||||
label: GatewayAuthSharedSecretLabel;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -93,6 +95,25 @@ function formatHooksTokenReuseDetail(reusedGatewayAuthLabel: GatewayAuthSharedSe
|
||||
return "hooks.token matches gateway.auth token; compromise of hooks expands blast radius to the Gateway API.";
|
||||
}
|
||||
|
||||
function listActiveGatewaySharedSecrets(auth: ResolvedGatewayAuth): ActiveGatewaySharedSecret[] {
|
||||
if (auth.mode === "token") {
|
||||
return [{ label: "gateway auth token", value: auth.token }];
|
||||
}
|
||||
if (auth.mode === "password" || auth.mode === "trusted-proxy") {
|
||||
return [{ label: "gateway auth password", value: auth.password }];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function findGatewayAuthLabelMatchingHooksToken(params: {
|
||||
hooksToken: string;
|
||||
auth: ResolvedGatewayAuth;
|
||||
}): GatewayAuthSharedSecretLabel | undefined {
|
||||
return listActiveGatewaySharedSecrets(params.auth).find(
|
||||
(candidate) => normalizeOptionalString(candidate.value) === params.hooksToken,
|
||||
)?.label;
|
||||
}
|
||||
|
||||
function findHooksTokenGatewayAuthReuseLabel(params: {
|
||||
hooksToken: string;
|
||||
configGatewayAuth: ResolvedGatewayAuth;
|
||||
|
||||
Reference in New Issue
Block a user