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:
parent
6f071f7963
commit
54f6061d41
7 changed files with 145 additions and 99 deletions
|
|
@ -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;
|
||||
}
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue