swarm-bao: refuse a remote reader that named seven of the eight leaves
The four-way client-cert split gives each store reader its own leaf, and three of the four readers render only where their own leaf exists. On a host that mints its own PKI glue-bao-tls.nix defaults all eight, so there is nothing to do; on a hand-configured remote-store hive, omitting one pair used to mean that unit silently did not render — a privilege- narrowing unit absent from a green build, with the missing unit as the only evidence. Each of the three now asserts its own pair, shaped after swarm-grafana.nix's haveClientIdentity assertion and named to the pair it needs. What differs from Grafana's is the gate: these fire only where the host demonstrably reads the store (it holds deploy.bao.clientCertFile and clientKeyFile) and the consumer is on. A host with no store identity is the supported no-store deployment and still evaluates; the collector's no-secret degrade is untouched, because that host holds no clientCertFile either. Also rewords three passive-voice sentences in docs/swarm/secrets.md that vale flagged, and documents what the refusal costs and where it stays silent.
This commit is contained in:
parent
f1445b4c8b
commit
d3e4951cc8
6 changed files with 627 additions and 276 deletions
|
|
@ -242,12 +242,14 @@ let
|
|||
# A reader of the store is defined by holding a certificate the store
|
||||
# accepts, never by standing next to it — the rule
|
||||
# ./glue-matrix-bao-token.nix states in full. Unlike Grafana's identical-
|
||||
# looking flag, this one stays outside `assertions`: the store-reading unit
|
||||
# below simply does not render without it, the same choice
|
||||
# looking flag, this one does not gate an assertion on its own: the
|
||||
# store-reading unit below simply does not render without it, the same choice
|
||||
# ./glue-matrix-bao-token.nix and ./glue-queue-agent-credential.nix make for
|
||||
# their own optional readers, because a collector with no client identity is
|
||||
# their own readers, because a collector with no client identity is
|
||||
# `haveCollectorSecret = false` above, and that is already a supported,
|
||||
# merely degraded shape rather than a service with no way in at all.
|
||||
# merely degraded shape rather than a service with no way in at all. What
|
||||
# IS asserted is narrower and lives in `assertions` below — see
|
||||
# `hiveReaderIdentity`.
|
||||
#
|
||||
# 🩸 The collector's OWN leaf, not `clientCertFile` — the hive's, which four
|
||||
# units used to share. Bao matches a cert-auth role on the CN, so one leaf for
|
||||
|
|
@ -257,6 +259,16 @@ let
|
|||
haveClientIdentity =
|
||||
baoDeploy.otelOidcClientCertFile != null && baoDeploy.otelOidcClientKeyFile != null;
|
||||
|
||||
# Does this host read the store at all — the hive's own leaf, which is the
|
||||
# one thing a remote-store deployment has always had to place by hand. It is
|
||||
# what separates the degrade the comment above describes from the mistake the
|
||||
# assertion below reports: a collector on a host holding NO store identity is
|
||||
# the supported shape, and one on a host that demonstrably reads the store
|
||||
# named seven of the eight options and stopped. Before the four-way split
|
||||
# that second host rendered this unit off `clientCertFile`, so it is a silent
|
||||
# regression rather than a choice anyone made.
|
||||
hiveReaderIdentity = baoDeploy.clientCertFile != null && baoDeploy.clientKeyFile != null;
|
||||
|
||||
# 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
|
||||
|
|
@ -732,11 +744,14 @@ in
|
|||
# value.
|
||||
#
|
||||
# ⚠️ Renders only where `haveClientIdentity` holds, unlike
|
||||
# ./swarm-grafana.nix's equivalent unit. That module asserts the identity
|
||||
# because a Grafana with none has no way in at all; this collector without
|
||||
# one is `haveCollectorSecret = false` above — already a supported,
|
||||
# merely degraded shape, so the unit that would fetch a credential simply
|
||||
# does not exist rather than refusing the build for want of one.
|
||||
# ./swarm-grafana.nix's equivalent unit. That module refuses any host that
|
||||
# runs Grafana without the identity, because a Grafana with none has no way
|
||||
# in at all; this collector without one is `haveCollectorSecret = false`
|
||||
# above — already a supported, merely degraded shape, so the unit that would
|
||||
# fetch a credential simply does not exist rather than refusing the build
|
||||
# for want of one. The one case that IS refused is the host that already
|
||||
# holds the hive's leaf and is missing only this pair, which is a silent
|
||||
# regression rather than that degrade — `hiveReaderIdentity` above.
|
||||
systemd.services.swarm-bao-otel-oidc = lib.mkIf haveClientIdentity {
|
||||
description = "fetch the swarm collector's OIDC client secret from the swarm secret store";
|
||||
# Every one of these names a unit that exists only where the store runs.
|
||||
|
|
@ -860,6 +875,45 @@ in
|
|||
systemd.services."container@${cfg.machine}" = caTrust.containerOrdering;
|
||||
|
||||
assertions = [
|
||||
{
|
||||
# Shaped after ./swarm-grafana.nix's `haveClientIdentity` assertion —
|
||||
# the same refusal, named to this principal's own pair. What differs is
|
||||
# the `!hiveReaderIdentity ||` guard, and it is what keeps the degrade
|
||||
# the flag's own comment describes intact: Grafana refuses any host
|
||||
# that runs it without a leaf, because a Grafana with no SSO has no way
|
||||
# in at all, while a collector with none still receives telemetry. So
|
||||
# this fires only where the host already reads the store and is missing
|
||||
# this one pair — which before the four-way split rendered the unit off
|
||||
# `clientCertFile`, and now silently does not.
|
||||
assertion = !hiveReaderIdentity || haveClientIdentity;
|
||||
message = ''
|
||||
This host reads the swarm secret store (services.hyperhive.deploy.bao.clientCertFile
|
||||
is set) and runs the swarm collector, so it needs the collector's own
|
||||
client identity: set both
|
||||
|
||||
services.hyperhive.deploy.bao.otelOidcClientCertFile
|
||||
services.hyperhive.deploy.bao.otelOidcClientKeyFile
|
||||
|
||||
swarm-bao-otel-oidc.service fetches the collector's OIDC client
|
||||
secret out of the store, and without these it is not rendered at all
|
||||
— so this collector would authenticate to nothing, quietly, on a host
|
||||
that has everything it needs to fetch the secret.
|
||||
|
||||
A collector on a host holding NO store identity is a different and
|
||||
supported shape: it runs without a client secret and still receives
|
||||
telemetry. That is not this host.
|
||||
|
||||
⚠️ The collector's OWN leaf, not deploy.bao.clientCertFile. That one
|
||||
is the hive's, and its grant reads every secret in the store; this
|
||||
role reads the one path this collector's client secret lives at.
|
||||
Pointing this option at the hive's leaf would evaluate, deploy and
|
||||
log in — and undo the split.
|
||||
|
||||
On a hive that runs the store, glue-bao-tls.nix supplies both as
|
||||
defaults and there is nothing to do. Elsewhere the leaf is issued
|
||||
from that CA out of band and named here — see docs/swarm/secrets.md.
|
||||
'';
|
||||
}
|
||||
# ⚠️ An assertion that this collector has "somewhere to send" was REMOVED
|
||||
# rather than relaxed: it read the store's *per-host* enable, so it
|
||||
# rejected at eval the very deployment the stores are reached by domain
|
||||
|
|
|
|||
Loading…
Reference in a new issue