Skip to content

fix(proxy): never send loopback requests through the outbound proxy - #3517

Open
ntdat812 wants to merge 1 commit into
decolua:masterfrom
ntdat812:fix/loopback-proxy-bypass-3424
Open

ntdat812 wants to merge 1 commit into
decolua:masterfrom
ntdat812:fix/loopback-proxy-bypass-3424

Conversation

@ntdat812

Copy link
Copy Markdown
Contributor

Fixes #3424.

Root cause

proxyAwareFetch resolves a proxy for every target — HTTP_PROXY /
HTTPS_PROXY from the environment, or a connection-level proxy — with no
loopback exemption. NO_PROXY is honoured, but nothing sets it by default.

An outbound proxy resolves localhost against its own machine, so a request
to http://localhost:11434/api/chat cannot succeed through one. The executor
sees a connection error, markAccountUnavailable writes testStatus: "unavailable" with the generic "Provider error", and the model is locked for
30 s — exactly the reported behaviour, including the re-locking after clearing
locks by hand.

This also explains the reporter's evidence, which otherwise looks contradictory:

client proxy env result
curl http://localhost:11434/api/tags shell may not carry it works
fetch() in plain Node ignored — Node's fetch does not read proxy env works
9router applied explicitly by proxyFetch fails

The connection test passes for the same reason it is a different code path in
their setup, so the dashboard shows a healthy account whose requests all fail.

Change

isLoopbackTarget(url) (exported for tests) short-circuits both proxy
resolvers — the environment one and the connection one.

Covered: localhost and *.localhost, all of 127.0.0.0/8, ::1 (and its
expanded form), and IPv4-mapped loopback in both spellings — new URL()
re-serializes [::ffff:127.0.0.1] as ::ffff:7f00:1, so matching only the
dotted form would miss the value actually seen at runtime.

Deliberately not covered: LAN and CGNAT ranges. A remote Ollama at
192.168.1.10 can legitimately need the proxy, and NO_PROXY already handles
that case. Look-alike hosts (localhost.evil.com, 127.0.0.1.evil.com) are
pinned as still-proxied by tests.

Scope

This is the connectivity half of the report. The issue's suggestion 2 — that
markAccountUnavailable collapses a non-string error into the generic
"Provider error" and loses the diagnostics — is real and I have it as a separate
change, since it affects every provider rather than just this one. Suggestion 1
(a /health probe) does not appear in the current source: ollama-local's
connection test uses GET {baseUrl}/api/tags, which is what the reporter measured
as working.

Tests

tests/unit/loopback-proxy-bypass-3424.test.js (new, 18 cases): the predicate
across loopback spellings and non-loopback look-alikes, plus two behavioural
cases that import the module with globalThis.fetch stubbed and assert no proxy
dispatcher is attached — one with HTTP_PROXY set, one with a connection-level
proxy.

Mutation check — removing both guards fails exactly the two behavioural cases:

× does not attach a proxy dispatcher for a loopback target when HTTP_PROXY is set
× ignores a connection-level proxy for a loopback target

npx eslint clean on both changed files.

`proxyAwareFetch` applied HTTP_PROXY/HTTPS_PROXY, and a connection-level
proxy, to every target — loopback included. A proxy resolves `localhost`
against its own machine, so once those variables are set (the normal state
on a corporate Windows box) a local provider can never be reached: the
executor sees a connection error, `markAccountUnavailable` records
"Provider error", and the model is locked for 30 s.

That also matches the evidence in the report: `curl` and a bare `fetch()`
in Node both reached Ollama, because Node's fetch ignores the proxy
environment entirely. Only 9router applied it.

`isLoopbackTarget` covers localhost and *.localhost, 127.0.0.0/8, ::1, and
IPv4-mapped loopback in both spellings — `new URL()` re-serializes
`[::ffff:127.0.0.1]` as `::ffff:7f00:1`, so matching only the dotted form
would miss the value actually seen.

The bypass stops at loopback: a remote Ollama on a LAN address can
legitimately need the proxy, and NO_PROXY still covers that.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] ollama-local provider returns 503 Provider error despite Ollama being reachable

1 participant