4.7 KiB
R-1100 — one sign-in for the dashboard and FileBrowser: the spike (2026-10-18 evening)
Measured on scratch 9202 (demo-hp, Tier 0): FileBrowser Quantum gtstef/filebrowser:1.5.6-stable (source read at tag
v1.5.6-stable, commit 5d9b4df2), Traefik v3.7.13, controller 0.331.0-rc.1. Nothing was built; every change on 9202 was
removed (03-teardown.txt). Evidence: 01-strip.txt (every request and answer), 02-teardown-users.txt.
What FileBrowser's proxy auth is (source, at the pinned tag)
- The config key is
auth.methods.proxy(backend/common/settings/auth.go:58, jsonproxy— notproxyAuth, as the row said), withenabledandheader(auth.go:75-78: „FileBrowser blindly accepts the header value as username"). - Any request that carries the header is that user (
backend/http/middleware.go:302-307on login,:384-401on every API call,:494-520getProxyUser). A name FileBrowser has never seen is created on the spot (backend/http/auth.go:81-106), with the user defaults. A name that exists as a password user is refused (auth.go:107-108,ErrWrongLoginMethod) — so the household'sadmincannot be reached by a header; a header user is a separate FileBrowser user.
What was measured
| Request | Answer |
|---|---|
a route with no middleware, forged X-Felhom-User: spike-stranger |
200 — the drive's listing; FileBrowser created the user |
Traefik forwardAuth that answers 200 and sets NO user header, authResponseHeaders: [X-Felhom-User]; forged header (also lower-case, also twice) |
401 from FileBrowser („no token present") — Traefik removed the client's header |
forwardAuth that sets X-Felhom-User: household-sso only for a valid cookie; cookie ok + forged attacker |
200 as household-sso (FileBrowser's own request log names the user) — the auth answer replaced the forged value |
| the same, wrong cookie + forged header | 401 from the auth step; FileBrowser never reached |
from inside the Nextcloud container, straight to http://filebrowser:80, forged header |
200 — no Traefik on that path |
So: Traefik can pass the header only for a valid session and strips one a client sends — proven. But the header is
also trusted from any container that reaches FileBrowser's port, and every app sits on traefik-public with it.
The three things a build must solve
- The session cannot be read on
files.<domain>. The dashboard cookiefelhom_sessionhas noDomain(controller/internal/web/auth.go:197-205), so a browser never sends it tofiles.<domain>, and a forward-auth there sees nothing. Do not widen it toDomain=<domain>: every app's subdomain — third-party code — would then receive the dashboard session. Instead a files-only ticket: „Fájlok ↗" goes through the dashboard, which mints a one-time, ~60-second token;files.<domain>/_felhom/sso?t=…(a Traefik route to the controller) swaps it for a host-only, HttpOnly, Secure cookie forfiles.<domain>; the forward-auth checks that cookie and answers the header. - Any app container can forge the header (measured). Two cheap closes, either enough, both better:
(a) the header name is a per-box secret (
X-Felhom-User-<32 hex>), written only into FileBrowser's config and Traefik's dynamic file — an app that does not know the name cannot send it; (b) FileBrowser offtraefik-public: Traefik joinsfelhom-filebrowserinstead (and OnlyOffice too, for itsserver.internalUrl, R-1018). (b) changes the R-1126 gate's shape; (a) does not. - Header users are new FileBrowser users. The household's files permissions for that user must be set by the box (the
user defaults decide today); shares the household made as
adminstayadmin's; logout needslogoutRedirectUrlto the dashboard.
Unchanged by it
- The password reveal (R-513) stays:
adminremains a password user — the fallback when the ticket path fails, and the operator's. The „Fájlok ↗" button would no longer need it. - The R-1126 gate keeps checking
admin's default login; header users do not touch it.
Cost and recommendation
Controller only (no agent, no hub): a ticket endpoint and a forward-auth endpoint, one Traefik route + middleware, the FileBrowser config block with a secret header name, the header user's permissions, tests for each refusal (forged header via Traefik; forged header from an app container — with a secret name; stale/replayed ticket; wrong host). About one release. Recommendation: build it with (1) the ticket cookie and (2a) the secret header name; do not ship header trust without 2a — the measured app-container path would hand every app the household's files.