Skip to content

fix(gateway/auth): local trusted-proxy fallback to require token auth#54536

Merged
vincentkoc merged 11 commits intomainfrom
vincentkoc-code/trusted-proxy-local-token-fallback
Mar 29, 2026
Merged

fix(gateway/auth): local trusted-proxy fallback to require token auth#54536
vincentkoc merged 11 commits intomainfrom
vincentkoc-code/trusted-proxy-local-token-fallback

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Problem: PR Enhance auth logic for trusted-proxy mode #45264 fixes local direct access in trusted-proxy mode, but its current branch allows verified loopback requests through with ok: true and user: "local" before any secret validation.
  • Why it matters: That turns trusted-proxy into a localhost auth bypass instead of a local token fallback, which weakens the operator boundary for same-host processes.
  • What changed: Local direct requests without proxy identity headers now fall back to normal token validation, while same-host proxy traffic with identity headers still uses trusted-proxy auth.
  • What did NOT change (scope boundary): External trusted-proxy behavior, allowlist enforcement, and same-host reverse proxy header validation remain unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause / Regression History (if applicable)

  • Root cause: The local-direct branch in trusted-proxy mode short-circuited to a synthetic trusted-proxy success result before proxy validation or token validation.
  • Missing detection / guardrail: Tests covered same-host proxy handling, but not the distinction between local transport detection and actual caller authentication.
  • Prior context (git blame, prior PR, issue, or refactor if known): Follow-up to Enhance auth logic for trusted-proxy mode #45264.
  • Why this regressed now: The initial fix correctly recognized that local direct callers cannot supply proxy-only headers, but implemented that as auth bypass instead of auth fallback.
  • If unknown, what was ruled out: N/A.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/gateway/auth.test.ts
  • Scenario the test should lock in: trusted-proxy local-direct requests require a valid token unless they are real proxied requests with identity headers.
  • Why this is the smallest reliable guardrail: The behavior lives entirely inside authorizeGatewayConnect(...).
  • Existing test that already covers this (if any): Same-host proxy identity-header handling was already partially covered.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • Local CLI/sub-agent requests in trusted-proxy mode now work only when they present the configured local token.
  • Same-host reverse proxy requests still authenticate via proxy identity headers.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (Yes)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:
    • This change restores the intended credential check for local fallback. It narrows access rather than expanding it.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: pnpm / local checkout
  • Model/provider: n/a
  • Integration/channel (if any): local gateway auth
  • Relevant config (redacted):
    {
      "gateway": {
        "auth": {
          "mode": "trusted-proxy",
          "token": "<token>",
          "trustedProxy": { "userHeader": "x-forwarded-user" }
        }
      }
    }

Steps

  1. Run gateway auth with mode: "trusted-proxy" and a configured token.
  2. Send a verified local-direct request without proxy identity headers.
  3. Retry with valid token, missing token, wrong token, and same-host proxy identity headers.

Expected

  • Local direct requests require token auth.
  • Same-host proxy requests continue to use trusted-proxy headers.
  • Missing proxy config still fails closed.

Actual

  • This branch matches those expectations.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: focused gateway auth test coverage for local direct token fallback, same-host proxy header path, and fail-closed config errors.
  • Edge cases checked: missing token, wrong token, no configured token, missing trusted-proxy config, missing trusted proxies.
  • What you did not verify: full manual gateway runtime repro outside the auth test surface.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A.

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR.
  • Files/config to restore: src/gateway/auth.ts, src/gateway/auth.test.ts
  • Known bad symptoms reviewers should watch for: local direct CLI/sub-agent requests in trusted-proxy mode failing despite a valid token.

Risks and Mitigations

  • Risk: Local direct callers without a token will now continue to fail instead of silently succeeding.
    • Mitigation: That is intentional; the fix restores a credential boundary while still solving the original local-direct header problem.

@vincentkoc vincentkoc self-assigned this Mar 25, 2026
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M maintainer Maintainer-authored PR labels Mar 25, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR closes a localhost auth-bypass in trusted-proxy mode introduced by #45264, where raw loopback requests were short-circuited to ok: true before any credential check. The fix correctly threads the needle: local-direct connections that carry no proxy identity header now fall back to normal shared-token validation, while same-host reverse-proxy traffic that presents the configured userHeader continues on the trusted-proxy path (with allowUsers / requiredHeaders still enforced).

Key changes:

  • isLocalDirectRequest now has a clean early-return for the no-forwarding-header + loopback-socket case, followed by the existing forwarded-chain logic for same-host proxy traffic.
  • A new authorizeTokenAuth helper deduplicates token validation between the trusted-proxy fallback and the existing token auth mode.
  • Rate-limiter variables are hoisted so the fallback path participates in rate-limiting.
  • Seven new test cases cover: valid token, missing token, wrong token, unconfigured token, same-host proxy with identity header, same-host proxy with a missing required header, and both fail-closed config scenarios.

One minor nit: the explanatory comment about not calling recordFailure for missing (vs wrong) credentials was dropped during the refactor to authorizeTokenAuth — worth restoring to preserve maintainer intent.

Confidence Score: 5/5

  • This PR is safe to merge — it strictly narrows the auth surface and is backed by thorough targeted tests.
  • The fix is minimal and surgical: it adds a guard in the trusted-proxy branch and extracts a shared helper, with no changes to any other auth mode or external proxy path. Seven new tests directly exercise every branch of the new fallback logic, including all fail-closed cases. The only open item is a non-blocking style suggestion to restore a one-line explanatory comment.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/auth.ts
Line: 390-392

Comment:
**Dropped "don't burn rate-limit slots" rationale**

The original token-auth block had an explicit comment explaining why `token_missing` intentionally skips `recordFailure`:

```
// Don't burn rate-limit slots for missing credentials — the client
// simply hasn't provided a token yet (e.g. bare browser open).
// Only actual *wrong* credentials should count as failures.
```

The behavior is preserved (neither `token_missing` nor `token_missing_config` calls `limiter?.recordFailure`), but the rationale has been lost in the refactor. Future maintainers may not understand why this asymmetry is intentional and could accidentally add a `recordFailure` call for missing-credential cases. Worth adding the comment back to `authorizeTokenAuth`.

```suggestion
  if (!params.connectToken) {
    // Don't burn rate-limit slots for missing credentials — the client
    // simply hasn't provided a token yet (e.g. bare browser open).
    // Only actual *wrong* credentials should count as failures.
    return { ok: false, reason: "token_missing" };
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Merge branch 'main' into vincentkoc-code..." | Re-trigger Greptile

Comment thread src/gateway/auth.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3786c202a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@TadasValaitiz
Copy link
Copy Markdown

Was waiting for this functionality 💯

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@vincentkoc vincentkoc changed the title Fix local trusted-proxy fallback to require token auth fix(gateway/auth): local trusted-proxy fallback to require token auth Mar 25, 2026
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot Bot commented Mar 25, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium DNS rebinding / Host-header bypass via weakened local-direct detection
Vulnerabilities

1. 🟡 DNS rebinding / Host-header bypass via weakened local-direct detection

Property Value
Severity Medium
CWE CWE-346
Location src/gateway/auth.ts:115-136

Description

isLocalDirectRequest() was changed to classify a request as “local-direct” solely based on req.socket.remoteAddress being loopback and the absence of a few “forwarded-ish” headers. It no longer requires a localish Host header.

This creates a DNS rebinding risk:

  • In server-http.ts, canRevealReadinessDetails() returns true (revealing detailed readiness info) whenever isLocalDirectRequest(...) is true.
  • A malicious website can DNS-rebind its domain to 127.0.0.1 and cause the victim’s browser to send requests to the local gateway with a non-local Host header (the attacker-controlled domain).
  • With the new logic, those requests are treated as local-direct and can receive privileged “local-only” responses, even though the Host is not local.

Vulnerable code:

const hasForwarded = Boolean(
  req.headers?.forwarded ||
  req.headers?.["x-forwarded-for"] ||
  req.headers?.["x-forwarded-proto"] ||
  req.headers?.["x-real-ip"] ||
  req.headers?.["x-forwarded-host"],
);

if (!hasForwarded) {
  return isLoopbackAddress(req.socket?.remoteAddress);
}

Because browsers do not normally set these forwarded headers, a rebinding request to 127.0.0.1 will satisfy !hasForwarded and pass the loopback check.

Recommendation

Reinstate a strict Host/origin locality check for “local-direct” classification to mitigate DNS rebinding.

For example:

import { isLocalishHost, isLoopbackAddress } from "./net.js";

export function isLocalDirectRequest(req?: IncomingMessage): boolean {
  if (!req) return false;

  const hasForwarded = Boolean(
    req.headers?.forwarded ||
    req.headers?.["x-forwarded-for"] ||
    req.headers?.["x-forwarded-proto"] ||
    req.headers?.["x-real-ip"] ||
    req.headers?.["x-forwarded-host"],
  );

  if (hasForwarded) return false;

  const host = typeof req.headers?.host === "string" ? req.headers.host : "";
  if (!isLocalishHost(host)) return false;

  return isLoopbackAddress(req.socket?.remoteAddress);
}

Additionally, consider requiring an unguessable local-only token even for readiness/details endpoints, or validating Origin/Host depending on the endpoint’s threat model.


Analyzed PR: #54536 at commit 719f304

Last updated on: 2026-03-25T16:57:26Z

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 07a5e809b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway/auth.ts
@rickstonerz
Copy link
Copy Markdown

rickstonerz commented Mar 27, 2026

Thanks for picking this up. I spent time cleaning up the macOS side from the earlier #33819 work, and the follow-up fixes are in #54718.

@vincentkoc vincentkoc merged commit 1738d54 into main Mar 29, 2026
8 checks passed
@vincentkoc vincentkoc deleted the vincentkoc-code/trusted-proxy-local-token-fallback branch March 29, 2026 08:05
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6166069ba3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway/auth.ts
Comment on lines +132 to +134
if (!hasForwarded) {
return isLoopbackAddress(req.socket?.remoteAddress);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reinstate strict local-direct classification checks

This now classifies any loopback socket without forwarding headers as local, regardless of Host or trusted-proxy/client-IP resolution, so a same-host reverse proxy that does not forward X-Forwarded-* can make external traffic look local. That broadens access in code paths that trust isLocalDirectRequest(...) for local-only behavior (for example readiness detail disclosure in src/gateway/server-http.ts:206-221, local-admin session-kill override in src/gateway/session-kill-http.ts:79-90, and WebSocket local-client policy in src/gateway/server/ws-connection/message-handler.ts:224-245). Previously, non-local Host loopback requests were treated as remote; this regression weakens that boundary.

Useful? React with 👍 / 👎.

Alix-007 pushed a commit to Alix-007/openclaw that referenced this pull request Mar 30, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
zhangning-agent added a commit to zhangning-agent/openclaw that referenced this pull request Apr 2, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@zhangjing-GitHub-Code
Copy link
Copy Markdown

Hello!
I want to know how you can get token and trusted-proxy work together.
It's impossible even on 2026.4.5 due to this commit, as the gateway fails at startup: Gateway failed to start: Error: gateway auth mode is trusted-proxy, but a shared token is also configured; remove gateway.auth.token / OPENCLAW_GATEWAY_TOKEN because trusted-proxy and token auth are mutually exclusive

Commit:
commit 6c679e5
Author: Jacob Tomlinson jacobtomlinson@users.noreply.github.com Date: Tue Mar 31 09:05:03 2026 -0700
Gateway: reject mixed trusted-proxy token config (#58371)
* Gateway: reject mixed trusted-proxy token config Co-authored-by: boy-hack w8ay@qq.com
* Gateway: fail closed for loopback trusted-proxy auth
---------
Co-authored-by: boy-hack w8ay@qq.com
@vincentkoc

@zhangjing-GitHub-Code
Copy link
Copy Markdown

Oh sorry, that commit is after your PR. What a pity!
Btw, I want to use exec approval while using trusted-proxy, and it's failing due to 1008 unauthorized.

lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…openclaw#54536)

* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: trustedProxy.loopbackUser for CLI/sub-agent access without proxy

5 participants