Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fde4a36b93 | ||
|
|
d275238dc4 |
2 changed files with 89 additions and 0 deletions
|
|
@ -24,6 +24,10 @@
|
|||
# ⚠️ That ingest location deliberately does not carry `swarmAuthRequest` — a
|
||||
# pusher handed its `error_page 401 =302` follows the redirect and POSTs at a
|
||||
# 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,
|
||||
lib,
|
||||
|
|
@ -202,6 +206,37 @@ in
|
|||
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
|
||||
# reasoning as `swarm-ui.nix`'s own copy (measured against the
|
||||
# pinned authelia binary, not copied from an example).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue