Watch
0
0
Fork
You've already forked hyperhive
0

swarm-otel: scrape VictoriaLogs through its published name

VictoriaLogs' own metrics were scraped over loopback, declared only on
the host running the store, so a collector on any other host had no job
for it. Its target is now declared on every host as
`https://<swarm.victorialogs.domain>/metrics`, next to forgejo's and
authelia's, and the store's vhost serves `= /metrics` behind a bare
`auth_request` to authelia. The rules derived from
`publishedScrapeTargets` admit the collector's client there and deny every
other subject. The loopback entry is gone, so a single host still scrapes
the store once, now by name.

On that vhost `/metrics` was reachable through the `/` location by any
authelia session. The exact-match location now takes it, so a session
gets a `deny` on `/metrics` and nothing else on the name changes.

Part of #5205
This commit is contained in:
atlas 2026-10-13 10:44:25 +02:00 • committed by mara
commit 5fe04da559
3 changed files with 104 additions and 16 deletions

View file

@ -106,6 +106,7 @@ let
forgeMetricsUrl = "https://forge.t.local/metrics";
autheliaMetricsUrl = "https://auth.t.local/metrics";
logsMetricsUrl = "https://logs.t.local/metrics";
vmMetricsUrl = "https://metrics.t.local/metrics";
# The policy authelia applies to `subject` requesting `https://<domain><path>`,
@ -371,9 +372,10 @@ let
lib.sort lib.lessThan (lib.attrValues h.swarm.otel.publishedScrapeTargets) == [
autheliaMetricsUrl
forgeMetricsUrl
logsMetricsUrl
vmMetricsUrl
]
&& lib.length targets == 6
&& lib.length targets == 8
&& lib.all (t: policy collector t == "one_factor") targets
&& lib.all (t: policy "user:someone" t == "deny") targets
&& lib.all (t: policy "group:admins" t == "deny") targets;
@ -421,7 +423,7 @@ let
];
matched = row: builtins.match re row.path != null;
in
lib.length metricsRules == 4
lib.length metricsRules == 6
&& lib.all (r: r.resources == [ re ]) metricsRules
&& re == "^/metrics([?].*)?$"
&& lib.all (row: matched row == (row.nginx == "= /metrics")) table;
@ -479,6 +481,85 @@ let
name = "a single-host swarm passes the loopback-vs-published job name assertion";
ok = !(refusedOver allLocal "is declared as both a loopback");
}
{
name = "a collector on a host without the log store scrapes it by name";
ok =
let
s = otelSettings otelRemoteAuthelia;
job = lib.findFirst (
j: j.job_name == "victorialogs"
) null s.receivers.prometheus.config.scrape_configs;
in
!otelRemoteAuthelia.services.hyperhive.deploy.victorialogs.enable
&& job != null
&& job.static_configs == [ { targets = [ "logs.t.local" ]; } ]
&& job.metrics_path == "/metrics"
&& job.oauth2.endpoint_params.audience == logsMetricsUrl;
}
{
# `= /metrics` must carry the bare authelia check and nothing else: the
# agents' `/__swarm_logs_auth` would admit an agent's bearer, and the
# browser block's `error_page 401 =302` hands a scraper a login page.
# The other routes keep their own checks.
name = "the log store's vhost serves /metrics behind authelia alone, its other routes unchanged";
ok =
let
h = allLocal.services.hyperhive;
locs = allLocal.services.nginx.virtualHosts.${h.swarm.victorialogs.domain}.locations;
checksOf = l: lib.filter (lib.hasPrefix "auth_request") (lib.splitString "\n" l.extraConfig);
in
h.deploy.victorialogs.enable
&& locs ? "= /metrics"
&& locs."= /metrics".proxyPass == "http://127.0.0.1:${toString h.swarm.victorialogs.port}/metrics"
&& checksOf locs."= /metrics" == [ "auth_request /__hive_authelia;" ]
&& !(lib.hasInfix "error_page" locs."= /metrics".extraConfig)
&& lib.hasInfix "error_page 401 =302" locs."/".extraConfig
&& lib.elem "auth_request /__hive_authelia;" (checksOf locs."/")
&& checksOf locs."^~ /select/logsql/" == [ "auth_request /__swarm_logs_auth;" ];
}
{
# The `deny` that confines the log store's `/metrics` is scoped to that
# path, and comes after the collector's rule: every other path on the
# name still reaches `default_policy`, as it did with no rule at all.
name = "authelia confines the log store's /metrics to the collector and leaves its other paths alone";
ok =
let
h = autheliaOnly.services.hyperhive;
collector = "oauth2:client:${h.swarm.otel.clientId}";
rules =
autheliaOnly.containers.swarm-authelia.config.services.authelia.instances.swarm.settings.access_control.rules;
logsRules = lib.filter (r: r.domain == "logs.t.local") rules;
policy = subject: autheliaPolicy autheliaOnly subject "logs.t.local";
in
map (r: r.policy) logsRules == [
"one_factor"
"deny"
]
&& lib.all (r: r.resources == [ "^/metrics([?].*)?$" ]) logsRules
&& (lib.head logsRules).subject == [ collector ]
&& policy collector "/metrics" == "one_factor"
&& policy "user:someone" "/metrics" == "deny"
&& policy "user:someone" "/metrics?x=1" == "deny"
&& policy "user:someone" "/" == "one_factor"
&& policy "user:someone" "/select/logsql/query" == "one_factor"
&& policy "user:someone" "/insert/opentelemetry/v1/logs" == "one_factor";
}
{
# Single host: the store is scraped once, by its published name.
name = "a host running the log store and the collector scrapes it once, by name";
ok =
let
o = allLocal.services.hyperhive.swarm.otel;
jobs =
lib.filter (j: j.job_name == "victorialogs")
(otelSettings allLocal).receivers.prometheus.config.scrape_configs;
in
allLocal.services.hyperhive.deploy.victorialogs.enable
&& allLocal.services.hyperhive.deploy.swarm-otel.enable
&& !(o.scrapeTargets ? victorialogs)
&& lib.length jobs == 1
&& (lib.head jobs).static_configs == [ { targets = [ "logs.t.local" ]; } ];
}
{
# Read against the roster the fixture declares rather than against
# names spelled here: an arm naming `h1` passes on a single-hive