From 47de0ce1dba2c7d03281ebfd7fef348b76d74a7c Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Sun, 25 Jan 2026 05:57:35 +0000 Subject: [PATCH] feat(security): add audit findings for trusted-proxy mode - Add critical finding when trusted-proxy auth is enabled - Flag missing trustedProxies configuration - Flag missing userHeader configuration - Warn when allowUsers is empty (allows any authenticated user) Part of #1560 Co-Authored-By: Claude Sonnet 4.5 --- src/security/audit.ts | 67 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/src/security/audit.ts b/src/security/audit.ts index f003423c6da..63e56b8cd15 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -346,23 +346,64 @@ function collectGatewayConfigFindings( }); } - const chatCompletionsEnabled = cfg.gateway?.http?.endpoints?.chatCompletions?.enabled === true; - const responsesEnabled = cfg.gateway?.http?.endpoints?.responses?.enabled === true; - if (chatCompletionsEnabled || responsesEnabled) { - const enabledEndpoints = [ - chatCompletionsEnabled ? "/v1/chat/completions" : null, - responsesEnabled ? "/v1/responses" : null, - ].filter((value): value is string => Boolean(value)); + // Trusted-proxy auth mode findings + if (auth.mode === "trusted-proxy") { + const trustedProxies = cfg.gateway?.trustedProxies ?? []; + const trustedProxyConfig = cfg.gateway?.auth?.trustedProxy; + findings.push({ - checkId: "gateway.http.session_key_override_enabled", - severity: remotelyExposed ? "warn" : "info", - title: "HTTP APIs accept explicit session key override headers", + checkId: "gateway.trusted_proxy_auth", + severity: "critical", + title: "Trusted-proxy auth mode enabled", detail: - `${enabledEndpoints.join(", ")} support x-openclaw-session-key. ` + - "Any authenticated caller can route requests into arbitrary sessions.", + 'gateway.auth.mode="trusted-proxy" delegates authentication to a reverse proxy. ' + + "Ensure your proxy (Pomerium, Caddy, nginx) handles auth correctly and that gateway.trustedProxies " + + "only contains IPs of your actual proxy servers.", remediation: - "Treat HTTP API credentials as full-trust, disable unused endpoints, and avoid sharing tokens across tenants.", + "Verify: (1) Your proxy terminates TLS and authenticates users. " + + "(2) gateway.trustedProxies is restricted to proxy IPs only. " + + "(3) Direct access to the Gateway port is blocked by firewall. " + + "See /gateway/trusted-proxy-auth for setup guidance.", }); + + if (trustedProxies.length === 0) { + findings.push({ + checkId: "gateway.trusted_proxy_no_proxies", + severity: "critical", + title: "Trusted-proxy auth enabled but no trusted proxies configured", + detail: + 'gateway.auth.mode="trusted-proxy" but gateway.trustedProxies is empty. ' + + "All requests will be rejected.", + remediation: "Set gateway.trustedProxies to the IP(s) of your reverse proxy.", + }); + } + + if (!trustedProxyConfig?.userHeader) { + findings.push({ + checkId: "gateway.trusted_proxy_no_user_header", + severity: "critical", + title: "Trusted-proxy auth missing userHeader config", + detail: + 'gateway.auth.mode="trusted-proxy" but gateway.auth.trustedProxy.userHeader is not configured.', + remediation: + "Set gateway.auth.trustedProxy.userHeader to the header name your proxy uses " + + '(e.g., "x-forwarded-user", "x-pomerium-claim-email").', + }); + } + + const allowUsers = trustedProxyConfig?.allowUsers ?? []; + if (allowUsers.length === 0) { + findings.push({ + checkId: "gateway.trusted_proxy_no_allowlist", + severity: "warn", + title: "Trusted-proxy auth allows all authenticated users", + detail: + "gateway.auth.trustedProxy.allowUsers is empty, so any user authenticated by your proxy can access the Gateway.", + remediation: + "Consider setting gateway.auth.trustedProxy.allowUsers to restrict access to specific users " + + '(e.g., ["nick@example.com"]).', + }); + } } if (bind !== "loopback" && !cfg.gateway?.auth?.rateLimit) {