Watch
0
0
Fork
You've already forked hyperhive
0

swarm-otel: scrape grafana through its published name

Grafana's own metrics were scraped over loopback through the
`grafana-metrics` nginx vhost, declared only where grafana and the
collector share a host, so a collector on any other host had no job for
it. Its target is now declared on every host as
`https://<swarm.grafana.domain>/metrics`, next to the other published
targets, and grafana's vhost serves `= /metrics` behind the collector's
auth subrequest (`/__grafana_metrics_authz`, the forge's shape). The
rules derived from `publishedScrapeTargets` admit the collector's client
there and deny every other subject.

The loopback scrape is gone with the vhost that served it and its
`swarm.grafana.metricsPort` option, so a single host still scrapes
grafana once, now by name. A config that sets that option now fails
evaluation with "option does not exist".

On the published vhost `/metrics` was answered with 403 for everyone.
It now answers the collector's token, and a plain session gets authelia's
`deny`. The design note that ruled the published vhost out for scraping
predates the collector's credential; it is replaced by the location's
own comment and a line on `swarm.grafana.domain`.

Part of #5205
This commit is contained in:
atlas 2026-10-14 19:13:53 +02:00 • committed by mara
commit 54f6061d41
7 changed files with 145 additions and 99 deletions

View file

@ -109,12 +109,6 @@ let
&& lib.hasInfix option a.message
) m.assertions;
cases = [
{
name = "grafana declares no scrape target on a host with no collector";
ok =
!(grafanaOldPath.services.hyperhive.deploy.swarm-otel.enable)
&& !(grafanaOldPath.services.hyperhive.swarm.otel.scrapeTargets ? grafana);
}
{
# The socket's directory is its access control, so it must stay dedicated
# to this one socket. `readOnly` is what makes that inexpressible: a
@ -124,22 +118,30 @@ let
ok =
!(builtins.tryEval grafanaSocketDirSet.services.hyperhive.deploy.grafana.socketDir).success
&&
grafanaOldPath.services.nginx.virtualHosts."grafana-metrics".locations."= /metrics".proxyPass
grafanaOldPath.services.nginx.virtualHosts.${grafanaOldPath.services.hyperhive.swarm.grafana.domain}.locations."= /metrics".proxyPass
== "http://unix:/run/swarm-grafana/grafana.sock:/metrics";
}
{
# Grafana serves `/metrics` unauthenticated, so the published name must
# answer it itself; the scrape reads the loopback vhost above instead.
# Grafana serves `/metrics` unauthenticated, so the published name gates
# it on the collector's auth subrequest: no `error_page 401 =302` (a
# scraper would parse a login page as metrics) and no `/__hive_authelia`.
# `=` is what catches the normalised variants and still leaves the rest
# of Grafana proxied.
name = "grafana's published vhost refuses /metrics and still proxies everything else";
name = "grafana's published vhost serves /metrics only behind the collector's auth subrequest";
ok =
let
g = grafanaOldPath.services.hyperhive.swarm.grafana;
locs = grafanaOldPath.services.nginx.virtualHosts.${g.domain}.locations;
checksOf = l: lib.filter (lib.hasPrefix "auth_request") (lib.splitString "\n" l.extraConfig);
authz = locs."= /__grafana_metrics_authz";
in
locs."= /metrics".return == "403"
&& locs."= /metrics".proxyPass == null
locs ? "= /metrics"
&& locs."= /metrics".return == null
&& checksOf locs."= /metrics" == [ "auth_request /__grafana_metrics_authz;" ]
&& !(lib.hasInfix "error_page" locs."= /metrics".extraConfig)
&& authz.proxyPass == "https://auth.t.local/api/authz/auth-request"
&& lib.hasInfix "internal;" authz.extraConfig
&& !(grafanaOldPath.services.nginx.virtualHosts ? "grafana-metrics")
&& lib.hasPrefix "http://unix:" locs."/".proxyPass;
}
{