swarm-victorialogs: add a machine-facing query location on the gateway

An agent can reach the log store only through the gateway, and the only
location that exists for a reader is `/`, which is the operator's browser
route. That route ends in `error_page 401 =302`, and an unauthenticated
caller which follows the redirect gets authelia's login page as HTTP 200
with an HTML body — so a client that checks the status code records a query
that succeeded and matched no logs. Measured on the live gateway; it is the
root cause behind four operator round-trips already.

So the query API gets its own location, `^~ /select/logsql/`, with bare
`auth_request` and no login fallback: an unauthenticated caller gets a 401
it cannot mistake for an empty result. `^~` keeps it ahead of the `/`
catch-all and of any regex location added later, and it stops short of
`/select/vmui/`, which is the browser UI and stays on the browser route.

The query is forwarded unmodified — no filter parameter is injected, so any
authenticated caller reads the whole swarm's logs. That is the rule mara
set: read permissions are a later thing, and this location is where one
attaches when it exists.

Refs #3870
This commit is contained in:
atlas 2026-09-13 14:14:47 +02:00 committed by mara
commit d275238dc4

View file

@ -24,6 +24,10 @@
# ⚠️ That ingest location deliberately does not carry `swarmAuthRequest` — a # ⚠️ That ingest location deliberately does not carry `swarmAuthRequest` — a
# pusher handed its `error_page 401 =302` follows the redirect and POSTs at a # pusher handed its `error_page 401 =302` follows the redirect and POSTs at a
# login page, which answers 200. See the location itself. # login page, which answers 200. See the location itself.
#
# The query API has a machine route of the same shape at `^~ /select/logsql/`,
# for the same reason, and with no filtering of what an authenticated caller
# may read — see that location.
{ {
pkgs, pkgs,
lib, lib,
@ -202,6 +206,37 @@ in
auth_request /__hive_authelia; auth_request /__hive_authelia;
''; '';
}; };
# The read side of that same split: the route a caller holding a
# bearer token queries, as opposed to the `/` above which is the
# operator's browser. Bare `auth_request` for the ingest route's
# exact reason — `error_page 401 =302` hands an unauthenticated
# machine caller authelia's login page as a **200 with an HTML
# body**, so a client that reads the status code records a
# successful query that returned no rows. 401 is the only answer
# here that a caller cannot mistake for an empty result.
#
# `^~` so it outranks the `/` catch-all and stays ahead of any
# regex location a later change adds. The prefix is the whole
# LogsQL query API and nothing else — `query`, `tail`, `hits`,
# `facets`, the `stats_query*` and the `field_*`/`stream_*`
# routes — while the browser UI sits beside it under
# `/select/vmui`. Both read off the pinned build, not the docs.
#
# The query is forwarded unmodified — no scoping parameter is
# injected, so any authenticated caller reads the whole swarm's
# logs. That is the rule in force, not an omission: read
# permissions are a later thing, and this location is where one
# attaches when it exists.
"^~ /select/logsql/" = {
# No URI part, so the request path and its query string reach
# the store as the caller sent them.
proxyPass = "http://127.0.0.1:${toString cfg.port}";
extraConfig = ''
auth_request /__hive_authelia;
'';
};
# The subrequest itself — same target, same header set, same # The subrequest itself — same target, same header set, same
# reasoning as `swarm-ui.nix`'s own copy (measured against the # reasoning as `swarm-ui.nix`'s own copy (measured against the
# pinned authelia binary, not copied from an example). # pinned authelia binary, not copied from an example).