swarm-secret-client: hives no longer read swarm/services/*
The hive policy rendered by policy::render granted every hive cert read on secret/data/swarm/services/*, which holds each swarm service's OIDC client secret. No reader of that tree authenticates with a hive cert: grafana, the otel collector and the store forwarder each read with their own leaf, and the bao UI's secret is read by the store's granter. render() now emits two stanzas: swarm/agents/* and the hive's own swarm/hives/<hive>/*. Kind::Service had no other caller and is removed. Docs and comments that described the grant as a decision are rewritten to the current state. Closes #4960
This commit is contained in:
parent
3bfdf0dfe0
commit
2505008b83
13 changed files with 82 additions and 136 deletions
|
|
@ -759,7 +759,7 @@ let
|
|||
#
|
||||
# 🩸 Each of the four reads exactly ONE path in the store, and until this
|
||||
# split each did it holding `deploy.bao.clientCertFile` — the hive's own leaf,
|
||||
# whose policy (`swarm-secret-client`'s `policy::render`) grants read on
|
||||
# whose policy (`swarm-secret-client`'s `policy::render`) granted read on
|
||||
# `swarm/agents/*`, `swarm/hives/<hive>/*` AND `swarm/services/*`. Four
|
||||
# principals presenting one certificate are ONE principal to bao, so the union
|
||||
# of what the four need was the only grant expressible: the unit that fetches
|
||||
|
|
@ -1142,8 +1142,7 @@ let
|
|||
# own and lands it in a collector's container — because that route is
|
||||
# proven and there is no second one worth inventing.
|
||||
#
|
||||
# Where the publisher on authelia's host leaves it. The `services` segment
|
||||
# is `swarm-secret-client`'s `path::Kind::Service`: a swarm service's client
|
||||
# Where the publisher on authelia's host leaves it. A swarm service's client
|
||||
# is registered once for the whole swarm, so its secret is one value. The
|
||||
# `swarm-forwarder-oidc` role's grant is this one path, so any other answers
|
||||
# 403 however correct it looks.
|
||||
|
|
@ -2288,8 +2287,8 @@ in
|
|||
environment = {
|
||||
BAO_ADDR = "https://${cfg.domain}:${toString cfg.port}";
|
||||
# 🩸 Its OWN leaf, not `clientCertFile`: the hive's grant reads every
|
||||
# agent's credential and every service's OIDC secret, and this unit
|
||||
# needs one path. The pair is asserted set above.
|
||||
# agent's credential, and this unit needs one path. The pair is
|
||||
# asserted set above.
|
||||
BAO_CLIENT_CERT = baoDeploy.forwarderOidcClientCertFile;
|
||||
BAO_CLIENT_KEY = baoDeploy.forwarderOidcClientKeyFile;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,8 +238,7 @@ let
|
|||
hostSecretPath = "/var/lib/nixos-containers/${cfg.machine}${secretPath}";
|
||||
hostSecretDir = builtins.dirOf hostSecretPath;
|
||||
|
||||
# Where the publisher on authelia's host leaves this client's secret. The
|
||||
# `services` segment is `swarm-secret-client`'s `path::Kind::Service` — a
|
||||
# Where the publisher on authelia's host leaves this client's secret. A
|
||||
# swarm service's client is registered once for the whole swarm, so its
|
||||
# secret is one value and not one per hive. Both ends compose this from the
|
||||
# same swarm-wide client id, so a rename cannot leave one of them behind.
|
||||
|
|
|
|||
|
|
@ -272,10 +272,8 @@ let
|
|||
# Where the publisher on authelia's host leaves this client's secret —
|
||||
# composed from the same swarm-wide `clientId` the registration in
|
||||
# ./glue-swarm-otel-oidc-client.nix uses, so a rename cannot leave one of
|
||||
# them behind. The `services` segment is `swarm-secret-client`'s
|
||||
# `path::Kind::Service`, the same prefix ./swarm-grafana.nix reads its own
|
||||
# client secret under — one write grant in ./swarm-bao.nix and one hive read
|
||||
# grant in `policy::render` already cover it.
|
||||
# them behind. The same prefix ./swarm-grafana.nix reads its own client
|
||||
# secret under; the `swarm-otel-oidc` role in ./swarm-bao.nix grants the read.
|
||||
storeSecretPath = "secret/swarm/services/${cfg.clientId}/oidc/client";
|
||||
|
||||
# The operator-configured upstream, named once: the same exporter carries
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ let
|
|||
|
||||
# The swarm's own services, as opposed to its hives. One client for the whole
|
||||
# swarm rather than one per hive, so one value in the store rather than a copy
|
||||
# each: `swarm/services/<id>/oidc/client`, under the `services` kind
|
||||
# `swarm-secret-client`'s `path::Kind` declares.
|
||||
# each: `swarm/services/<id>/oidc/client`.
|
||||
#
|
||||
# The ids come from `swarm.*`, which is identical on every host — that is what
|
||||
# lets this host name a service's client while running none of them, and it is
|
||||
|
|
@ -267,9 +266,8 @@ in
|
|||
fi
|
||||
'') serviceClientIds}
|
||||
|
||||
# The controller's own client, under `controller/` rather than
|
||||
# `services/`: every hive reads `services/*`, and this client may read
|
||||
# every hive's status. The nix half of
|
||||
# The controller's own client, under `controller/`, which no hive
|
||||
# reads: this client may read every hive's status. The nix half of
|
||||
# `swarm_secret_client::queue::controller_client_path`.
|
||||
src=${lib.escapeShellArg "${deployCfg.authelia.hostClientSecretDir}/${controllerClientId}.secret"}
|
||||
if [ -s "$src" ]; then
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ let
|
|||
# ── the four readers that used to share the hive's own leaf ──────────────
|
||||
#
|
||||
# 🩸 Until this split all four presented `deploy.bao.clientCertFile`, whose
|
||||
# policy grants read on `swarm/agents/*`, `swarm/hives/<hive>/*` AND
|
||||
# policy granted read on `swarm/agents/*`, `swarm/hives/<hive>/*` AND
|
||||
# `swarm/services/*`. Four principals behind one certificate are one
|
||||
# principal to bao, so the only expressible grant was the union: the unit
|
||||
# fetching Grafana's OIDC secret could fetch every agent credential in the
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ let
|
|||
{
|
||||
# The controller reads this exact path
|
||||
# (`swarm_secret_client::queue::controller_client_path`, pinned by that
|
||||
# crate's own test). Not under `services/`, which every hive reads: this
|
||||
# crate's own test). Under `controller/`, which no hive reads: this
|
||||
# client may read every hive's status.
|
||||
name = "the publisher writes the controller's client under controller/, never services/";
|
||||
ok =
|
||||
|
|
|
|||
Loading…
Reference in a new issue