auth: allow local loopback connections to bypass trusted-proxy auth#51070
auth: allow local loopback connections to bypass trusted-proxy auth#51070dragonforce2010 wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR extends the existing Key observations:
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/gateway/auth.test.ts
Line: 653-693
Comment:
**Missing IPv6 loopback test case**
The two new tests only exercise `remoteAddress: "127.0.0.1"`. The gateway also binds to `::1` when available (see `resolveGatewayListenHosts` in `net.ts`), so a CLI that connects via `[::1]` should receive the same bypass. Adding a third test with `remoteAddress: "::1"` and `host: "[::1]:18789"` would confirm the bypass works symmetrically on IPv6 loopback.
```ts
it("allows local direct IPv6 loopback connections to bypass trusted-proxy auth", async () => {
const res = await authorizeGatewayConnect({
auth: {
mode: "trusted-proxy",
allowTailscale: false,
trustedProxy: trustedProxyConfig,
},
connectAuth: null,
trustedProxies: ["10.0.0.1"],
req: {
socket: { remoteAddress: "::1" },
headers: {
host: "[::1]:18789",
},
} as never,
});
expect(res.ok).toBe(true);
expect(res.method).toBe("none");
});
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "auth: allow local lo..." |
| it("allows local direct loopback connections to bypass trusted-proxy auth", async () => { | ||
| const res = await authorizeGatewayConnect({ | ||
| auth: { | ||
| mode: "trusted-proxy", | ||
| allowTailscale: false, | ||
| trustedProxy: trustedProxyConfig, | ||
| }, | ||
| connectAuth: null, | ||
| trustedProxies: ["10.0.0.1"], | ||
| req: { | ||
| socket: { remoteAddress: "127.0.0.1" }, | ||
| headers: { | ||
| host: "127.0.0.1:18789", | ||
| }, | ||
| } as never, | ||
| }); | ||
|
|
||
| expect(res.ok).toBe(true); | ||
| expect(res.method).toBe("none"); | ||
| }); | ||
|
|
||
| it("does not bypass trusted-proxy auth for non-loopback connections", async () => { | ||
| const res = await authorizeGatewayConnect({ | ||
| auth: { | ||
| mode: "trusted-proxy", | ||
| allowTailscale: false, | ||
| trustedProxy: trustedProxyConfig, | ||
| }, | ||
| connectAuth: null, | ||
| trustedProxies: ["10.0.0.1"], | ||
| req: { | ||
| socket: { remoteAddress: "192.168.1.50" }, | ||
| headers: { | ||
| host: "192.168.1.50:18789", | ||
| }, | ||
| } as never, | ||
| }); | ||
|
|
||
| expect(res.ok).toBe(false); | ||
| expect(res.reason).toBe("trusted_proxy_untrusted_source"); | ||
| }); |
There was a problem hiding this comment.
Missing IPv6 loopback test case
The two new tests only exercise remoteAddress: "127.0.0.1". The gateway also binds to ::1 when available (see resolveGatewayListenHosts in net.ts), so a CLI that connects via [::1] should receive the same bypass. Adding a third test with remoteAddress: "::1" and host: "[::1]:18789" would confirm the bypass works symmetrically on IPv6 loopback.
it("allows local direct IPv6 loopback connections to bypass trusted-proxy auth", async () => {
const res = await authorizeGatewayConnect({
auth: {
mode: "trusted-proxy",
allowTailscale: false,
trustedProxy: trustedProxyConfig,
},
connectAuth: null,
trustedProxies: ["10.0.0.1"],
req: {
socket: { remoteAddress: "::1" },
headers: {
host: "[::1]:18789",
},
} as never,
});
expect(res.ok).toBe(true);
expect(res.method).toBe("none");
});Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/auth.test.ts
Line: 653-693
Comment:
**Missing IPv6 loopback test case**
The two new tests only exercise `remoteAddress: "127.0.0.1"`. The gateway also binds to `::1` when available (see `resolveGatewayListenHosts` in `net.ts`), so a CLI that connects via `[::1]` should receive the same bypass. Adding a third test with `remoteAddress: "::1"` and `host: "[::1]:18789"` would confirm the bypass works symmetrically on IPv6 loopback.
```ts
it("allows local direct IPv6 loopback connections to bypass trusted-proxy auth", async () => {
const res = await authorizeGatewayConnect({
auth: {
mode: "trusted-proxy",
allowTailscale: false,
trustedProxy: trustedProxyConfig,
},
connectAuth: null,
trustedProxies: ["10.0.0.1"],
req: {
socket: { remoteAddress: "::1" },
headers: {
host: "[::1]:18789",
},
} as never,
});
expect(res.ok).toBe(true);
expect(res.method).toBe("none");
});
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c14cd23c50
ℹ️ 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 (localDirect) { | ||
| return { ok: true, method: "none" }; |
There was a problem hiding this comment.
Don't bypass trusted-proxy auth for all loopback clients
This turns every direct 127.0.0.1/::1 request into { ok: true, method: "none" } whenever gateway.auth.mode="trusted-proxy", which means any unprivileged local user/process on the host can skip the proxy's identity checks and hit operator-only surfaces like src/gateway/tools-invoke-http.ts:159 and src/gateway/sessions-history-http.ts:158 with no headers or shared secret. That is a real auth regression for same-host proxy deployments, and it also contradicts the repo's documented guarantee that loopback auth exists to block other local processes (docs/help/faq.md:1350).
Useful? React with 👍 / 👎.
|
This bypasses trusted-proxy auth entirely for loopback connections, not just calls via the CLI, returning The trusted-proxy mode exists so the reverse proxy handles identity for every connection. This creates a path with no identity — no user, no audit trail. A few concerns:
I get that local CLI commands don't go through the proxy and lack identity headers. A local service account token would solve that without opening up a full auth bypass. Here's how it maps out: Current state (no PR):
With this PR:
With a local service account token instead:
The token would work similarly to token auth mode but scoped to loopback only — auto-generated, file-permission-restricted ( |
|
Also can you add a description to the PR @dragonforce2010 ? |
|
Just came across #17746, and this is similar to what I'm proposing. |
|
Closing this as duplicate or superseded after Codex automated review. Close PR #51070 as superseded by the open service-identity/trusted-proxy RFC #69066. Current main intentionally keeps trusted-proxy loopback requests fail-closed, with tests and docs enforcing that contract, and the PR's blanket Best possible solution: Do not merge the blanket loopback What I checked:
So I’m closing this here and keeping the remaining discussion on the canonical linked item. Codex Review notes: model gpt-5.5, reasoning high; reviewed against 4ad8b613c9b6. |
No description provided.