swarm metrics store: the read API needs Grafana's bearer or an operator session
The metrics vhost forwarded the store's read API (query, series, labels,
export, vmui, ...) with no credential, so anything that could reach
metrics.<swarm> could read every hive's and agent's metrics. mara ruled on
checked by an auth_request responder, and operators use their authelia
session.
- swarm-metrics-auth (new crate): auth_request responder on the metrics
store's host. Compares the presented bearer, in constant time, with the
one stored at swarm/services/<grafana client id>/metrics/bearer, read
under its own bao leaf and cached for 60s. Static user, the post-#5264
swarm-logs-auth unit shape.
- swarm-bao-grafana-metrics-bearer: on Grafana's host, creates that bearer
in bao if absent (create without update, Grafana's existing leaf) and
writes "Bearer <value>" into the container; the datasource sends it as
its Authorization header via $__file{}.
- The vhost: read locations take auth_request through a map (a bearer goes
to swarm-metrics-auth, anything else to authelia, no login redirect);
vmui takes an authelia session with the login redirect; forceSSL like
the logs vhost. The authelia subrequest location is renamed from
/__metrics_push_authz to /__metrics_authelia since it now serves reads.
- Authelia: the collector keeps every path; everyone else is refused the
collector's push and /metrics paths; group admins is admitted to the
rest; then deny.
- bao: a swarm-metrics-auth cert role and policy (read on the one leaf),
its leaf minted by glue-bao-tls, the CN reserved as a hive name, and
Grafana's grant gains create on the bearer leaf.
Fixes #3596
This commit is contained in:
parent
9f0fb71fee
commit
6aab2ebb65
24 changed files with 1126 additions and 200 deletions
|
|
@ -908,15 +908,17 @@ let
|
|||
# the objects they write.
|
||||
#
|
||||
# Grafana's leaf also creates its admin password (`swarm-bao-grafana-admin`
|
||||
# in ./swarm-grafana.nix). `create` without `update`, so a stored password is
|
||||
# never replaced.
|
||||
# in ./swarm-grafana.nix) and the bearer its metrics datasource presents
|
||||
# (`swarm-bao-grafana-metrics-bearer`). `create` without `update`, so a stored
|
||||
# value is never replaced.
|
||||
grafanaOidcReaders = [
|
||||
{
|
||||
name = "swarm-grafana-oidc";
|
||||
cn = baoDeploy.grafanaOidcCommonName;
|
||||
policyText =
|
||||
readStanza "${credentialMountPath}/data/swarm/services/${hyperhiveCfg.swarm.grafana.oidc.clientId}/oidc/client"
|
||||
+ createStanza "${credentialMountPath}/data/${grafanaAdminLeaf}";
|
||||
+ createStanza "${credentialMountPath}/data/${grafanaAdminLeaf}"
|
||||
+ createStanza "${credentialMountPath}/data/${grafanaMetricsBearerLeaf}";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -982,6 +984,12 @@ let
|
|||
# of `adminPasswordStorePath` in ./swarm-grafana.nix.
|
||||
grafanaAdminLeaf = "swarm/services/${hyperhiveCfg.swarm.grafana.oidc.clientId}/admin/password";
|
||||
|
||||
# The bearer Grafana's metrics datasource presents at the metrics store.
|
||||
# Grafana's leaf creates it, the store's responder reads it. The nix half of
|
||||
# `metricsBearerStorePath` in ./swarm-grafana.nix and of `--store-path` in
|
||||
# ./swarm-victoriametrics.nix.
|
||||
grafanaMetricsBearerLeaf = "swarm/services/${hyperhiveCfg.swarm.grafana.oidc.clientId}/metrics/bearer";
|
||||
|
||||
createStanza = path: ''
|
||||
path "${path}" {
|
||||
capabilities = ["create", "read"]
|
||||
|
|
@ -999,6 +1007,16 @@ let
|
|||
}
|
||||
];
|
||||
|
||||
# The metrics store's responder, which checks the bearer Grafana presents
|
||||
# against the one stored for it, and reads nothing else.
|
||||
metricsAuthReaders = [
|
||||
{
|
||||
name = "swarm-metrics-auth";
|
||||
cn = baoDeploy.metricsAuthCommonName;
|
||||
policyText = readStanza "${credentialMountPath}/data/${grafanaMetricsBearerLeaf}";
|
||||
}
|
||||
];
|
||||
|
||||
# The CI runner's token fetch. The one reader of the path, which the
|
||||
# controller writes and cannot read back.
|
||||
ciRunnerReaders = [
|
||||
|
|
@ -1859,6 +1877,19 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
metricsAuthCommonName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "swarm-metrics-auth";
|
||||
description = ''
|
||||
Subject the store's `swarm-metrics-auth` cert-auth role accepts: the
|
||||
identity the metrics store's Grafana auth responder presents to read
|
||||
the bearer Grafana's datasource sends,
|
||||
`swarm/services/<grafana client id>/metrics/bearer`, and nothing else.
|
||||
|
||||
⚠️ Reserved as a hive name by ./swarm.nix, like its siblings.
|
||||
'';
|
||||
};
|
||||
|
||||
ciRunnerCommonName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "swarm-ci-runner";
|
||||
|
|
@ -3274,6 +3305,8 @@ in
|
|||
|
||||
systemd.services.swarm-bao-logs-auth-policy = readerPolicyUnit "write the log store responder's agent-credential-reader bao policy and cert-auth role" logsAuthReaders;
|
||||
|
||||
systemd.services.swarm-bao-metrics-auth-policy = readerPolicyUnit "write the metrics store responder's Grafana-bearer-reader bao policy and cert-auth role" metricsAuthReaders;
|
||||
|
||||
systemd.services.swarm-bao-ci-runner-policy = readerPolicyUnit "write the CI runner's registration-token-reader bao policy and cert-auth role" ciRunnerReaders;
|
||||
|
||||
systemd.services.swarm-bao-review-classifier-policy = readerPolicyUnit "write the review classifier's API-key-reader bao policy and cert-auth role" reviewClassifierReaders;
|
||||
|
|
@ -3408,6 +3441,7 @@ in
|
|||
"swarm-bao-hive-leaf-policy.service"
|
||||
"swarm-bao-ci-runner-policy.service"
|
||||
"swarm-bao-logs-auth-policy.service"
|
||||
"swarm-bao-metrics-auth-policy.service"
|
||||
"swarm-bao-forge-publisher-policy.service"
|
||||
]
|
||||
++ granterAfter;
|
||||
|
|
|
|||
Loading…
Reference in a new issue