Pr 33819 fallback fixes#54718
Conversation
Greptile SummaryThis PR introduces a local-loopback shared-secret fallback for Key changes:
One subtlety worth being aware of: when Confidence Score: 5/5
Reviews (1): Last reviewed commit: "fix(gateway): honor configured trusted-p..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e739d4c75
ℹ️ 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".
| if (auth.password && connectAuth?.password) { | ||
| if (!safeEqualSecret(connectAuth.password, auth.password)) { | ||
| limiter?.recordFailure(ip, rateLimitScope); | ||
| return { ok: false, reason: "password_mismatch" }; | ||
| } |
There was a problem hiding this comment.
Check token before password in trusted-proxy fallback
In authorizeSharedSecretFallback, the password branch runs first and returns password_mismatch immediately, so the token branch is never reached when both shared secrets are configured. This breaks local trusted-proxy fallback for normal bearer-token HTTP requests, because the HTTP entry points pass connectAuth as { token, password: token } (for example in src/gateway/server-http.ts and src/gateway/http-auth-helpers.ts), which means a valid token is rejected whenever auth.password is set and differs from the token.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. I updated the trusted-proxy shared-secret fallback to prefer token auth before password auth so bearer-token HTTP requests are not rejected when both shared secrets are configured, and added targeted coverage for that path.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1846c7d9b
ℹ️ 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".
| const localLoopbackWithoutProxyHeaders = | ||
| Boolean(req) && | ||
| isLoopbackAddress(req?.socket?.remoteAddress) && | ||
| !hasConfiguredTrustedProxyHeaders(req, auth.trustedProxy); |
There was a problem hiding this comment.
Restrict fallback to true local-direct trusted-proxy requests
The localLoopbackWithoutProxyHeaders gate only checks whether req.socket.remoteAddress is loopback and configured proxy headers are absent, so requests forwarded through a same-host reverse proxy (remote 127.0.0.1) can still enter shared-secret fallback even when the actual client is external (for example via x-forwarded-for). In trusted-proxy mode this widens auth from “proxy-vouched identity only” to bearer token/password for proxied external traffic whenever shared secrets are configured, which can bypass intended trusted-proxy enforcement; this branch should be gated by proxy-aware local-direct detection (for example isLocalDirectRequest) rather than raw socket loopback.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. I tightened the fallback gate to require a true local-direct request rather than raw loopback socket detection, so same-host proxied external traffic no longer qualifies for shared-secret fallback. I also added regression coverage for that proxied-external case.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b280776e68
ℹ️ 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".
| trustedProxies, | ||
| params.allowRealIpFallback === true, |
There was a problem hiding this comment.
Use proxy-agnostic local detection before trusted-proxy fallback
localDirect is computed via isLocalDirectRequest, which in turn relies on resolveRequestClientIp; that helper intentionally returns undefined when the remote is in trustedProxies and forwarding headers are absent. In the common bind=loopback trusted-proxy setup, src/gateway/server-runtime-config.ts requires loopback entries in gateway.trustedProxies, so direct loopback requests hit this fail-closed path and localDirect becomes false, meaning the new shared-secret fallback branch is never reached. This makes the advertised local-direct token/password fallback unavailable in the primary same-host proxy deployment and requests still fail with trusted-proxy header errors.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. The previous gate reused isLocalDirectRequest too literally for the loopback trusted-proxy case, and that helper fails closed when a trusted proxy is configured without forwarding headers. I updated it so true same-host direct loopback requests with no forwarding headers still count as local-direct, while proxied external traffic with forwarding headers still does not qualify for shared-secret fallback.
b280776 to
78378d2
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78378d2e51
ℹ️ 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".
| if (isLocalishHost(req.headers?.host) && isLoopbackAddress(remoteAddr) && !hasForwarded) { | ||
| return true; |
There was a problem hiding this comment.
Reject loopback requests carrying proxy-forwarded context
This early return marks a request as localDirect before proxy-aware IP resolution whenever Host is localish and the socket is loopback, but hasForwarded only checks x-forwarded-for/x-real-ip/x-forwarded-host. A same-host reverse proxy that forwards external traffic with other headers (for example x-forwarded-proto plus identity headers, or rewritten loopback Host) can therefore be misclassified as local, which then changes downstream security decisions (e.g. auth bypass path in authorizeGatewayConnect and local-client behavior used by WebSocket/origin/pairing flows). The fresh evidence is that this new short-circuit at src/gateway/auth.ts:138-139 bypasses resolveRequestClientIp entirely for those requests.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. I reverted the broad isLocalDirectRequest short-circuit and moved the special handling into a trusted-proxy-specific fallback gate instead. The fallback now only applies to true same-host loopback requests with a localish Host and no forwarding/proxy context headers or configured trusted-proxy headers, and I added regression coverage for loopback requests carrying proxy-forwarding context.
24fc413 to
33f3b46
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33f3b469c0
ℹ️ 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".
| const limiter = params.rateLimiter; | ||
| const ip = | ||
| params.clientIp ?? | ||
| resolveRequestClientIp(req, trustedProxies, params.allowRealIpFallback === true) ?? |
There was a problem hiding this comment.
Reintroduce localDirect before tailscale auth gate
This refactor removed the localDirect = isLocalDirectRequest(...) initialization, but the later gate still evaluates !localDirect in authorizeGatewayConnect. On ws-control-ui requests where auth.allowTailscale is true, that expression throws at runtime before token/password auth runs, so control-UI authentication can fail with a server error instead of returning an auth result.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I restored the missing initialization and verified the control-UI path no longer throws before normal auth evaluation. The current branch now keeps the intended behavior:
- local direct requests continue through normal token/password auth
- trusted-proxy logic stays scoped to the proxy-aware path
- control-UI requests with
auth.allowTailscaleno longer fail with a server error from an uninitializedlocalDirectreference
33f3b46 to
f7558d3
Compare
|
For clarity: this is the canonical PR for the trusted-proxy fallback/header-handling fix originally proposed in #33819. #33819 contained the earlier iteration, but this branch is the narrowed current-base version intended for review and merge. The authored fix sequence remains:
|
|
Updating this PR with current status and pushing back on the "superseded" framing. What actually happened: PR #54536 ("fix(gateway/auth): local trusted-proxy fallback to require token auth") was merged into main. That fix is a direct subset of what this PR proposes. The core idea — that same-host loopback requests in trusted-proxy mode should be able to fall back to token/shared-secret auth — was validated and merged. This PR got the concept right first. On the RFC #69066 "superseded" claim: The RFC lists this PR as something Phase 2 would supersede. But the RFC's own Phase 0 recommendation was to merge the two most-validated in-flight PRs as "complementary opt-ins" — and one of those was effectively this approach. The direction this PR takes (token fallback for loopback trusted-proxy) is exactly what #54536 shipped. Current state:
What I'm asking: Before this gets closed as "superseded by RFC Phase 2", please consider that:
Happy to rebase and address any remaining review feedback. The real-world deployment case (nginx same-host reverse proxy + token auth + internal TLS) is documented and working — this PR makes it work without config gymnastics. |
2e94067 to
4c4346a
Compare
nginx-on-the-same-box reverse proxies arrive with socket.remoteAddress 127.0.0.1 but carry a non-local Host header and standard forwarding headers. The previous loopback guard in authorizeTrustedProxy rejected all loopback sources unconditionally, breaking this legitimate same-host proxy pattern. Fix: permit loopback addresses through the trusted-proxy header-validation path when the request appears proxied (non-local-ish Host + forwarding context present). Plain loopback connections and requests that spoof a non-local Host without forwarding context are still rejected as trusted_proxy_loopback_source. The token fallback for direct node connections (isLocalDirectRequest path) is unchanged — it operates before the trusted-proxy path and handles the openclaw-node use-case where a node connects directly to the gateway on the same host. Update tests to reflect correct behavior: - loopback + non-local host + forwarding headers => trusted-proxy auth succeeds - loopback + non-local host + forwarding headers + missing user => trusted_proxy_user_missing - loopback + local host + any forwarding context => trusted_proxy_loopback_source - direct loopback + valid token => ok (token method) - direct loopback + wrong token => token_mismatch (not loopback_source)
|
Codex review: found issues before merge. Summary Reproducibility: yes. The PR diff and current main tests provide source-level reproductions for token fallback, loopback proxy headers, local-direct detection, and config-guard ordering; I did not run tests because this was a required read-only review. Next step before merge Security Review findings
Review detailsBest possible solution: Keep the explicit current model unless maintainers revise it: password-only local-direct fallback, Do we have a high-confidence way to reproduce the issue? Yes. The PR diff and current main tests provide source-level reproductions for token fallback, loopback proxy headers, local-direct detection, and config-guard ordering; I did not run tests because this was a required read-only review. Is this the best way to solve the issue? No. The PR is broader than the maintained contract, and the safer path is to preserve explicit loopback opt-in and password-only fallback unless maintainers approve a service-identity/token design. Full review comments:
Overall correctness: patch is incorrect Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 14b5f73e2a5e. |
PR Fallback Fixes
This note exists as a clean fallback summary for the branch and PR flow when the GitHub compare page is flaky or the web UI does not render the diff reliably.
Branch Context
openclaw/openclawmainrickstonerz/openclawshltpr-33819-fallback-fixesWhat This Branch Contains
This branch carries the trusted-proxy local fallback fixes for gateway authentication behavior.
Included commits:
3e739d4fix(gateway): honor configured trusted-proxy headers in local fallback15a4fb4fix(gateway): tighten trusted-proxy local fallback3ee7c48fix(gateway): allow local shared-secret auth in trusted-proxy modeIf The Compare Page Misbehaves
If GitHub fails to render the comparison cleanly, verify the branch locally with:
If the compare URL still shows the correct base/head branches and commit list, the PR can still be created even if the UI reports rendering errors.
Purpose Of This Fallback Note
This is here to preserve the exact branch/commit context so the PR can be described accurately without relying on a flaky compare page.