Watch
0
0
Fork
You've already forked hyperhive
0

hive telemetry collector: fetch its OIDC secret from the store under the hive's own leaf

The hive collector's `oauth2client` secret came from a `clientSecretFile`
defaulting to authelia's own minted file on the IdP's host, with a
`hive-otel-client-secret` oneshot that only waited up to 120s for that
file to exist; any other hive had to copy the file across by hand.

`swarm-bao-hive-otel-oidc` now logs in with the hive's leaf
(`deploy.bao.clientCertFile`), reads `swarm/hives/<hive>/oidc/client`
(written by the publisher, granted by the hive leaf's
`swarm/hives/<hive>/*` read), writes it root 0400 when it differs, and
restarts the collector when the file is newer than its last start.
Retries every 30s for 24h (`lib/store-retry.nix`), like the matrix and
forge fetches. `clientSecretFile` defaults to that file wherever the hive
leaf is set and stays as an override; an overridden file renders no fetch.

otelcol-contrib 0.151.0's oauth2client re-reads `client_secret_file` on
every token fetch, but the path is `LoadCredential`'s start-time copy, so a
changed secret still needs a restart. `lib/refresh-consumer.nix` gains
`refresh_unit` for a host unit; `refresh_consumer` now calls it with
`--machine`.

nats-authelia's "consumer of the host client-secret dir" case pointed at
the collector's old default; it now reads the publisher's path unit.

Refs #5015
This commit is contained in:
atlas 2026-10-10 01:09:20 +02:00
commit 7c50568db1
7 changed files with 282 additions and 114 deletions

View file

@ -1,7 +1,7 @@
# Shared shell steps for a host oneshot that fetches a credential into a
# file a service inside a container loads at start (`LoadCredential`, or a
# config file expanded once while parsing). Such a service never sees a
# value that lands after it started — late or rotated — until it restarts.
# file a service loads at start (`LoadCredential`, or a config file expanded
# once while parsing). Such a service never sees a value that lands after it
# started — late or rotated — until it restarts.
#
# The file's mtime is the record of a change, so write the file only when
# `secret_differs` says so. The restart decision is then re-derived from
@ -16,7 +16,8 @@
# if secret_differs "$path" "$secret"; then
# atomic_write_secret 0400 root:root "$path" "$secret"
# fi
# refresh_consumer my-machine my.service "$path"
# refresh_consumer my-machine my.service "$path" # a unit in a container
# refresh_unit my.service "$path" # a host unit
# '';
#
# Requires `systemd` and `coreutils` on the caller's `path`.
@ -30,22 +31,20 @@
[ ! -f "$1" ] || [ "$(< "$1")" != "$2" ]
}
# <machine> <unit> <path>. Nothing while `container@<machine>` is not
# active (a container that starts later loads the file as it starts), or
# when <unit> last entered `active` after <path> was last written.
# Otherwise a running consumer is restarted and a failed one — it may
# have hit its start limit without the credential — is reset and started;
# one stopped on purpose stays stopped. `--no-block` because a caller
# ordered `Before=` its consumer must not wait on a job that waits on the
# caller (see `swarm-services-cert`'s propagation in ../hive-tls.nix).
refresh_consumer() {
local machine="$1" unit="$2" path="$3" started started_us=0 written_us
if ! systemctl is-active --quiet "container@$machine.service"; then
return 0
fi
# <unit> <path> [systemctl option…]. Nothing when <unit> last entered
# `active` after <path> was last written. Otherwise a running <unit> is
# restarted and a failed one — it may have hit its start limit without the
# credential — is reset and started; one stopped on purpose stays stopped.
# The options are passed to every `systemctl` call, which is how
# `refresh_consumer` reaches into a container. `--no-block` because a
# caller ordered `Before=` its consumer must not wait on a job that waits
# on the caller (see `swarm-services-cert`'s propagation in ../hive-tls.nix).
refresh_unit() {
local unit="$1" path="$2" started started_us=0 written_us
shift 2
# Empty for a unit that has never been active, which `date` would
# otherwise read as today's midnight.
started="$(systemctl --machine="$machine" show --timestamp=us+utc -p ActiveEnterTimestamp --value "$unit")"
started="$(systemctl "$@" show --timestamp=us+utc -p ActiveEnterTimestamp --value "$unit")"
if [ -n "$started" ]; then
started_us="$(date -u -d "$started" +%s%6N)"
fi
@ -53,13 +52,24 @@
if [ "$written_us" -le "$started_us" ]; then
return 0
fi
if systemctl --machine="$machine" is-failed --quiet "$unit"; then
echo "$path changed after $unit in $machine last started, and $unit had failed — starting it"
systemctl --machine="$machine" reset-failed "$unit"
systemctl --machine="$machine" start --no-block "$unit"
if systemctl "$@" is-failed --quiet "$unit"; then
echo "$path changed after $unit last started, and $unit had failed — starting it"
systemctl "$@" reset-failed "$unit"
systemctl "$@" start --no-block "$unit"
else
echo "$path changed after $unit in $machine last started — restarting it if it runs"
systemctl --machine="$machine" try-restart --no-block "$unit"
echo "$path changed after $unit last started — restarting it if it runs"
systemctl "$@" try-restart --no-block "$unit"
fi
}
# <machine> <unit> <path>. `refresh_unit` inside `container@<machine>`, and
# nothing while that container is not active: a container that starts
# later loads the file as it starts.
refresh_consumer() {
local machine="$1" unit="$2" path="$3"
if ! systemctl is-active --quiet "container@$machine.service"; then
return 0
fi
refresh_unit "$unit" "$path" --machine="$machine"
}
''