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
|
# ⚠️ 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).
|
||||||
|
|
|
||||||
|
|
@ -1785,6 +1785,60 @@ let
|
||||||
&& !(lib.hasInfix "error_page" l)
|
&& !(lib.hasInfix "error_page" l)
|
||||||
&& lib.hasInfix "error_page" browser;
|
&& 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
|
# Defining an exporter and REFERENCING it are two separate lists, and
|
||||||
# the second is where the original gate also lived. An exporter no
|
# the second is where the original gate also lived. An exporter no
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue