Skip to content

Enhance auth logic for trusted-proxy mode#45264

Closed
zhangning-agent wants to merge 1 commit intoopenclaw:mainfrom
zhangning-agent:fix-trust-proxy-localdirect
Closed

Enhance auth logic for trusted-proxy mode#45264
zhangning-agent wants to merge 1 commit intoopenclaw:mainfrom
zhangning-agent:fix-trust-proxy-localdirect

Conversation

@zhangning-agent
Copy link
Copy Markdown
Contributor

Add fallback for local requests in token verification logic.

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: In gateway.auth.mode: trusted-proxy, loopback CLI/sub-agent requests are immediately rejected because they cannot provide the configured proxy headers (e.g., userHeader).
  • Why it matters: Local CLI tools, Sub-agent operations, and internal scripts completely break and cannot communicate with the local gateway when it runs behind a reverse proxy under trusted-proxy mode.
  • What changed: Added a transparent fallback mechanism in authorizeGatewayConnect for trusted-proxy mode. If a request verifies as localDirect (loopback) and a local token is configured, it falls back to token verification rather than strictly failing on proxy headers.
  • What did NOT change (scope boundary): External/remote connections in trusted-proxy mode still strictly require proxy headers. The underlying token and trusted-proxy validation logic itself remains unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

Local openclaw CLI commands and Sub-agent sessions will now work seamlessly (via the configured openclaw.json token) when the gateway is running locally behind trusted-proxy mode, avoiding "trusted_proxy_user_missing" connection errors.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

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

Steps

  1. Configure openclaw.json with gateway.auth.mode: trusted-proxy and a valid token.
  2. Start the gateway locally openclaw gateway run.
  3. Attempt to run a local CLI command, e.g., openclaw status.

Expected

  • The CLI command successfully authenticates against the loopback gateway using the local token and returns the status.

Actual

  • Before this change, the CLI failed with unauthorized or trusted_proxy_user_missing because the CLI could not emulate the proxy header. Now it successfully falls back to token validation.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets (CLI successfully hitting the loopback WS using the fallback branch).
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: Starting the gateway behind a reverse proxy (trusted-proxy mode) and executing SDK/CLI commands loopback natively over port 18789.
  • Edge cases checked: Attempting to force local headers without a token still gets correctly rejected; external connections without proxy headers continue to be securely dropped.
  • What you did not verify: n/a

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:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert this PR cleanly from main.
  • Files/config to restore: src/gateway/auth.ts
  • Known bad symptoms reviewers should watch for: openclaw status failing on users who rely heavily on local proxy header injection for testing.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: None
    • Mitigation: The fallback enforces a strict isLocalDirect (loopback origin) combined with validating that a gateway.auth.token exists inside the configuration. The primary security model for trusted-proxy is uncompromised for network boundaries.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: XS labels Mar 13, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 13, 2026

Greptile Summary

This PR adds a local-token fallback to authorizeGatewayConnect in trusted-proxy mode, allowing loopback CLI and sub-agent requests to authenticate via gateway.auth.token instead of failing when the required proxy headers (e.g. userHeader) are absent. The change is narrow in scope and the security boundary (isLocalDirectRequest) is sound — but there is a logic gap that makes the fallback unreachable in certain configurations.

Key changes:

  • When authorizeTrustedProxy fails, the new guard at line 400 checks localDirect && auth.token before returning the failure; if both are true, execution falls through to the existing token-verification block.
  • The token-verification condition at line 443 is widened to also match auth.mode === "trusted-proxy" when localDirect is true.

Issues found:

  • Fallback is unreachable when trustedProxies is empty: The two early returns at lines 383–388 (trusted_proxy_config_missing, trusted_proxy_no_proxies_configured) run before authorizeTrustedProxy is called and before the fallback check. If the trustedProxies IP list is empty or not passed to the function (e.g. no reverse proxy in front during development), a loopback request with a valid token still fails — contradicting the stated goal of fixing local CLI connectivity.
  • Redundant auth.token guard: The && auth.token sub-expression on line 443 is a no-op because control only reaches that line in trusted-proxy mode when auth.token is already guaranteed truthy (line 400 returns early otherwise).

Confidence Score: 3/5

  • The fallback logic is partially safe but has an unreachable-code gap that leaves the bug unfixed for the empty-trustedProxies scenario.
  • The security boundary (isLocalDirectRequest requiring loopback + no forwarded headers + localish host) is solid and the token comparison uses a timing-safe helper. However, the early-return guards block the fallback before it can activate when trustedProxies is empty, meaning the fix doesn't fully deliver on its stated goal and may confuse users who encounter trusted_proxy_no_proxies_configured despite having a token configured.
  • src/gateway/auth.ts — specifically the early-return guards at lines 383–388 that prevent the fallback from running when trustedProxies is empty.

Comments Outside Diff (1)

  1. src/gateway/auth.ts, line 386-403 (link)

    Fallback unreachable when trustedProxies list is empty

    The two early-return guards at lines 383–388 fire before authorizeTrustedProxy is ever called, which means the new local-token fallback at line 400 is never evaluated in those paths. Concretely, if the trustedProxies IP list is empty or not passed by the caller (e.g. a developer running in trusted-proxy mode without a reverse proxy in front), a direct loopback request with a valid token still returns trusted_proxy_no_proxies_configured rather than falling through to token verification.

    The intended fix should also intercept these early exits:

        if (!auth.trustedProxy) {
          if (localDirect && auth.token) {
            // fall through to token verification below
          } else {
            return { ok: false, reason: "trusted_proxy_config_missing" };
          }
        } else if (!trustedProxies || trustedProxies.length === 0) {
          if (localDirect && auth.token) {
            // fall through to token verification below
          } else {
            return { ok: false, reason: "trusted_proxy_no_proxies_configured" };
          }
        } else {
          const result = authorizeTrustedProxy({
            req,
            trustedProxies,
            trustedProxyConfig: auth.trustedProxy,
          });
    
          if ("user" in result) {
            return { ok: true, method: "trusted-proxy", user: result.user };
          }
    
          if (!localDirect || !auth.token) {
            return { ok: false, reason: result.reason };
          }
          // Fall through to token verification for local requests fallback
        }

    Without this, the PR description's claim that "local CLI tools, Sub-agent operations, and internal scripts" can connect is only true when a real reverse proxy with its IPs listed in trustedProxies is already running — the no-proxy/development scenario still breaks.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/gateway/auth.ts
    Line: 386-403
    
    Comment:
    **Fallback unreachable when `trustedProxies` list is empty**
    
    The two early-return guards at lines 383–388 fire **before** `authorizeTrustedProxy` is ever called, which means the new local-token fallback at line 400 is never evaluated in those paths. Concretely, if the `trustedProxies` IP list is empty or not passed by the caller (e.g. a developer running in `trusted-proxy` mode without a reverse proxy in front), a direct loopback request with a valid token still returns `trusted_proxy_no_proxies_configured` rather than falling through to token verification.
    
    The intended fix should also intercept these early exits:
    
    ```typescript
        if (!auth.trustedProxy) {
          if (localDirect && auth.token) {
            // fall through to token verification below
          } else {
            return { ok: false, reason: "trusted_proxy_config_missing" };
          }
        } else if (!trustedProxies || trustedProxies.length === 0) {
          if (localDirect && auth.token) {
            // fall through to token verification below
          } else {
            return { ok: false, reason: "trusted_proxy_no_proxies_configured" };
          }
        } else {
          const result = authorizeTrustedProxy({
            req,
            trustedProxies,
            trustedProxyConfig: auth.trustedProxy,
          });
    
          if ("user" in result) {
            return { ok: true, method: "trusted-proxy", user: result.user };
          }
    
          if (!localDirect || !auth.token) {
            return { ok: false, reason: result.reason };
          }
          // Fall through to token verification for local requests fallback
        }
    ```
    
    Without this, the PR description's claim that "local CLI tools, Sub-agent operations, and internal scripts" can connect is only true when a real reverse proxy with its IPs listed in `trustedProxies` is already running — the no-proxy/development scenario still breaks.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/auth.ts
Line: 386-403

Comment:
**Fallback unreachable when `trustedProxies` list is empty**

The two early-return guards at lines 383–388 fire **before** `authorizeTrustedProxy` is ever called, which means the new local-token fallback at line 400 is never evaluated in those paths. Concretely, if the `trustedProxies` IP list is empty or not passed by the caller (e.g. a developer running in `trusted-proxy` mode without a reverse proxy in front), a direct loopback request with a valid token still returns `trusted_proxy_no_proxies_configured` rather than falling through to token verification.

The intended fix should also intercept these early exits:

```typescript
    if (!auth.trustedProxy) {
      if (localDirect && auth.token) {
        // fall through to token verification below
      } else {
        return { ok: false, reason: "trusted_proxy_config_missing" };
      }
    } else if (!trustedProxies || trustedProxies.length === 0) {
      if (localDirect && auth.token) {
        // fall through to token verification below
      } else {
        return { ok: false, reason: "trusted_proxy_no_proxies_configured" };
      }
    } else {
      const result = authorizeTrustedProxy({
        req,
        trustedProxies,
        trustedProxyConfig: auth.trustedProxy,
      });

      if ("user" in result) {
        return { ok: true, method: "trusted-proxy", user: result.user };
      }

      if (!localDirect || !auth.token) {
        return { ok: false, reason: result.reason };
      }
      // Fall through to token verification for local requests fallback
    }
```

Without this, the PR description's claim that "local CLI tools, Sub-agent operations, and internal scripts" can connect is only true when a real reverse proxy with its IPs listed in `trustedProxies` is already running — the no-proxy/development scenario still breaks.

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

---

This is a comment left during a code review.
Path: src/gateway/auth.ts
Line: 443

Comment:
**Redundant `auth.token` guard in condition**

The sub-expression `&& auth.token` is redundant here. When execution reaches this line in `trusted-proxy` mode, `auth.token` is already guaranteed to be truthy — the check at line 400 returns early if `!auth.token`, so control only falls through when `auth.token` is set. Dropping the duplicate guard keeps the condition easier to reason about:

```suggestion
  if (auth.mode === "token" || (auth.mode === "trusted-proxy" && localDirect)) {
```

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

Last reviewed commit: c4b3d5c

Comment thread src/gateway/auth.ts Outdated
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: c4b3d5c20f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@zhangning-agent zhangning-agent force-pushed the fix-trust-proxy-localdirect branch from 5ff7596 to c154260 Compare March 14, 2026 02:16
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: c154260f23

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@zhangning-agent zhangning-agent force-pushed the fix-trust-proxy-localdirect branch from c154260 to 7653efd Compare March 14, 2026 16:40
@zhangning-agent zhangning-agent requested a review from a team as a code owner March 16, 2026 04:07
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: e96a625ff7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@zhangning-agent zhangning-agent force-pushed the fix-trust-proxy-localdirect branch from e96a625 to 3cfc79f Compare March 17, 2026 03:11
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: 3cfc79fe32

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@zhangning-agent zhangning-agent force-pushed the fix-trust-proxy-localdirect branch from e868d2a to 168675b Compare March 18, 2026 11:16
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: 168675b9f5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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: c2edd91c16

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
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: 4cc3c869d4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts Outdated
@zhangning-agent zhangning-agent force-pushed the fix-trust-proxy-localdirect branch from 4cc3c86 to 11373a3 Compare March 25, 2026 06:02
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: 11373a3d2d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/auth.ts
Comment on lines +402 to +403
if (localDirect && !hasProxyIdentityHeader) {
return { ok: true, method: "trusted-proxy", user: "local" };
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 Require secret validation for local trusted-proxy path

This branch grants ok: true for any localDirect request that omits the configured identity header, but it runs before authorizeTrustedProxy() and never checks a token/password, so any same-host process that can hit loopback with a localish Host can authenticate as user: "local" in trusted-proxy mode. That bypasses trustedProxy.userHeader/allowUsers, and downstream WebSocket policy treats method === "trusted-proxy" as shared auth (src/gateway/server/ws-connection/auth-context.ts:148-154), which can admit unauthorized operator control sessions.

Useful? React with 👍 / 👎.

@vincentkoc
Copy link
Copy Markdown
Member

Closing as unable to push to your branch. Tracking work on this PR and credit will be given to you #54536.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants