mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(security): grant private AI endpoints by exact origin, dialled pinned
ALLOW_LOCAL_AI_PRIVATE_HOSTS=true let any user of the instance point the Local provider or the OpenAI-compatible gateway at any private address the server could reach, the metadata range included, and a granted host was dialled with no connect-time pin at all. AI_PRIVATE_ORIGINS grants a private AI endpoint with the exact-origin grammar NOTIFICATION_PRIVATE_ORIGINS and NIGHTSCOUT_PRIVATE_ORIGINS use. A granted call goes through the pinned operator-approved dispatcher: redirects forbidden, and metadata, link-local and unspecified answers dropped at dial time even for a granted name. Everything else stays on the public pin. The Local client (buffered and streaming) and the gateway and admin-key tags of the OpenAI client all take their policy from one function. The host-list form of ALLOW_LOCAL_AI_PRIVATE_HOSTS keeps working with the same pinning; a never-grantable literal or a host:port entry is refused with a one-time warning. Its "true" form grants nothing any more and is reported in the boot readiness summary with the replacement. A failed lab-report scan no longer puts the endpoint's response body into the wide event; status and model stay. The response to the caller never carried it.
This commit is contained in:
1 parent
f7b3ce6ee0
commit
1a674cf4b0
20 files changed
+508
-181
No files matched your search
@@ -140,16 +140,28 @@ Base URL `http://<vllm-host>:8000/v1`, API key `your-shared-secret`,
|
||||
model `Qwen/Qwen2.5-14B-Instruct`. The local-client encrypts the
|
||||
API key at rest like every other provider credential.
|
||||
|
||||
**SSRF guard.** Local endpoints route through the same validation
|
||||
as every other outbound URL HealthLog hits. The guard rejects
|
||||
private-IP-range targets by default. `ALLOW_LOCAL_AI_PRIVATE_HOSTS`
|
||||
opens the escape hatch and accepts two forms: a comma-separated host
|
||||
allowlist (e.g. `ALLOW_LOCAL_AI_PRIVATE_HOSTS=ollama.lan,10.0.0.5`),
|
||||
which permits only those exact hostnames, or the legacy
|
||||
`ALLOW_LOCAL_AI_PRIVATE_HOSTS=true`, which permits any private host —
|
||||
including the cloud-metadata endpoint, so prefer the host list. Leave
|
||||
it unset on public-facing instances unless you specifically run an
|
||||
internal endpoint.
|
||||
**Private endpoints.** Any user of the instance can type a base URL,
|
||||
so HealthLog refuses one on a private address unless the operator has
|
||||
listed it. List the endpoint's exact origin in `AI_PRIVATE_ORIGINS`,
|
||||
comma-separated, the same way `NOTIFICATION_PRIVATE_ORIGINS` works for
|
||||
notification relays:
|
||||
|
||||
```bash
|
||||
AI_PRIVATE_ORIGINS="http://ollama.lan:11434,http://10.0.0.5:4000"
|
||||
```
|
||||
|
||||
An origin is `scheme://host[:port]` and nothing else: no path, no
|
||||
wildcard, no range. A listed endpoint is called through a pinned
|
||||
connection that refuses to follow redirects, and the cloud-metadata
|
||||
address, link-local addresses and the unspecified address stay
|
||||
unreachable even when listed or when a listed name resolves to one.
|
||||
|
||||
`ALLOW_LOCAL_AI_PRIVATE_HOSTS`, the older setting, still works as a
|
||||
host list (`ollama.lan,10.0.0.5`, any port), with the same pinning.
|
||||
Its `true` form used to open every private host on the network to
|
||||
every user and no longer grants anything since v1.39.3; the boot
|
||||
summary names it when it is still set. Replace it with the one origin
|
||||
your endpoint listens on.
|
||||
|
||||
**Model sizing.** Roughly: 7-8B for 8 GB GPU / Apple Silicon base
|
||||
(briefings fine, deeper Coach reasoning gets terse), 14-24B for
|
||||
@@ -186,9 +198,9 @@ therefore never be sent to a host you typed into a different form,
|
||||
and that property is structural rather than a comment — the OPENAI
|
||||
arm has no code path that reads the gateway's base URL.
|
||||
|
||||
**SSRF guard.** The gateway's base URL passes the same floor as the
|
||||
Local provider's: a public host always, a private one only when the
|
||||
operator allowlisted it via `ALLOW_LOCAL_AI_PRIVATE_HOSTS` (documented
|
||||
**Private endpoints.** The gateway's base URL passes the same floor
|
||||
as the Local provider's: a public host always, a private one only when
|
||||
the operator listed its origin in `AI_PRIVATE_ORIGINS` (documented
|
||||
under Local endpoints above). LAN gateways are the normal case for LiteLLM and vLLM, so
|
||||
the escape hatch matters here as much as it does for Ollama.
|
||||
|
||||
@@ -439,12 +451,12 @@ upstream error.
|
||||
- **"No AI provider configured."** Either set up at least one BYOK
|
||||
provider in `/settings/ai`, or configure an admin-shared OpenAI
|
||||
key in the admin panel.
|
||||
- **Local endpoint rejected as "internal/private host".** Set
|
||||
`ALLOW_LOCAL_AI_PRIVATE_HOSTS` to the endpoint's exact hostname
|
||||
(e.g. `ollama.lan`; comma-separate several) in the container
|
||||
environment, or to `true` to allow any private host. Prefer the host
|
||||
list; leave it unset on internet-facing instances unless you
|
||||
specifically want to allow private-range targets.
|
||||
- **Local endpoint rejected as "internal/private host".** Add the
|
||||
endpoint's exact origin to `AI_PRIVATE_ORIGINS` in the container
|
||||
environment (e.g. `http://ollama.lan:11434`; comma-separate several)
|
||||
and restart. If it stopped working after an update and you had
|
||||
`ALLOW_LOCAL_AI_PRIVATE_HOSTS=true`, that form no longer grants
|
||||
anything; list the origin instead.
|
||||
- **Codex disconnects after a few weeks of inactivity.** ChatGPT
|
||||
refresh tokens lapse after extended idle periods. Reconnect via
|
||||
the **Connect ChatGPT** button — HealthLog re-runs the device-code
|
||||
|
||||
Reference in new issue
Block a user