From fde4a36b93cec85cc070cf6b07f0cca1b710724e Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 13 Sep 2026 14:19:18 +0200 Subject: [PATCH] module-eval: pin the log store's machine query route and its absences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three cases beside the existing ingest ones, since the read route has the same trap and one property of its own. - the route exists and carries `auth_request`; - it has no login fallback — with the browser location as the positive control, so a pass means the two routes differ rather than that `error_page` and the login host are absent from the whole vhost; - it injects no filter, on `extraConfig` and on `proxyPass` both, because VictoriaLogs takes its filters as request parameters and those ride an upstream URI as easily as a directive. A filter arriving later is then a visible diff here rather than a quiet change of rule. Each absence arm leads with a clause that proves the location resolved and that `hasInfix` finds what is really in that string, so "not there" cannot be read off an unreadable path. Refs #3870 --- nix/module-eval.nix | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/nix/module-eval.nix b/nix/module-eval.nix index 25208745..2be72a41 100644 --- a/nix/module-eval.nix +++ b/nix/module-eval.nix @@ -1785,6 +1785,60 @@ let && !(lib.hasInfix "error_page" l) && lib.hasInfix "error_page" browser; } + { + # The read counterpart to the ingest location: an agent queries the log + # store with a bearer token, and the `/` catch-all is the browser's + # route. Riding it would mean inheriting the login redirect the next + # case is about, so the route has to exist separately to be gated + # separately. + name = "the log store's vhost has an authenticated machine query location"; + ok = + let + q = allLocal.services.nginx.virtualHosts."logs.t.local".locations."^~ /select/logsql/" or null; + in + q != null && lib.hasInfix "auth_request" q.extraConfig; + } + { + # Same trap as the ingest case, on the read side, where it is worse: a + # redirected pusher at least stores nothing visibly, while a redirected + # *reader* is handed a 200 carrying login HTML and records a query that + # succeeded and matched no logs. The browser clause is the positive + # control — that location really does redirect to the login host — so a + # pass means these two routes differ rather than that the strings are + # absent from the whole vhost. + name = "the machine query location answers 401 instead of redirecting to a login page"; + ok = + let + v = allLocal.services.nginx.virtualHosts; + q = v."logs.t.local".locations."^~ /select/logsql/".extraConfig; + browser = v."logs.t.local".locations."/".extraConfig; + in + !(lib.hasInfix "error_page" q) + && !(lib.hasInfix "auth.t.local" q) + && lib.hasInfix "error_page" browser + && lib.hasInfix "auth.t.local" browser; + } + { + # Read access is deliberately unscoped: an authenticated caller reads + # the whole swarm's logs until a permission system exists. Pinned so a + # scoping parameter arriving later is a visible diff here rather than a + # quiet change of rule — and pinned on `proxyPass` too, because + # VictoriaLogs takes its filters as request parameters, which ride an + # upstream URI as easily as a directive. The first clause is the + # control: it proves the location resolved and that `hasInfix` finds + # what is genuinely in this string, so the absences below mean absent + # rather than unreadable. + name = "the machine query location forwards the caller's query unmodified"; + ok = + let + q = allLocal.services.nginx.virtualHosts."logs.t.local".locations."^~ /select/logsql/"; + in + lib.hasInfix "auth_request" q.extraConfig + && !(lib.hasInfix "extra_filters" q.extraConfig) + && !(lib.hasInfix "extra_stream_filters" q.extraConfig) + && !(lib.hasInfix "$args" q.extraConfig) + && !(lib.hasInfix "?" q.proxyPass); + } { # Defining an exporter and REFERENCING it are two separate lists, and # the second is where the original gate also lived. An exporter no