matrix: drop the per-hive appservice and its unused token
Every hive's homeserver loaded a per-hive `hyperhive` appservice
registration, and the swarm minted and delivered an as_token for it
through bao. No client presented that token: swarm-controller creates
every `@hive-<hive>` sender and every agent account with the swarm
appservice token (hive_sender.rs, agent_token.rs), and no Rust code read
the per-hive one.
Removed, with everything that existed only to serve it:
- hive-matrix.nix: the `hyperhive` registration, its activation-time
render, the host bind mount of /var/lib/hyperhive/matrix-appservice,
its LoadCredential entry, and the internal options
`deploy.matrix.appserviceTokenFile` / `appserviceRegistrationScript`.
tuwunel now loads only the swarm registration.
- glue-matrix-bao-token.nix and its `swarm-bao-matrix-token` unit.
- glue-bao-tls.nix: the matrix-token leaf and its defaults.
- swarm-bao.nix: the `swarm-matrix-token-<hive>` roles and policies
(`swarm-bao-matrix-token-policy`), and the options
`deploy.bao.matrixTokenClient{Cert,Key}File` and
`matrixTokenCommonNamePrefix`, now mkRemovedOptionModule.
- swarm-secret-publisher.nix: the per-hive token mint and its state dir.
- swarm.nix: the reserved `<matrixTokenCommonNamePrefix>-<hive>` CN.
- swarm-secret-client: `matrix::appservice_token_path`.
The swarm registration (swarm-matrix-ctl, swarm-controller) is untouched.
Comments and docs that pointed at the removed glue file now point at
glue-queue-agent-credential.nix, which has the same reader shape.
Refs #4428
This commit is contained in:
parent
e4b170f8c6
commit
48067d07e8
31 changed files with 362 additions and 1233 deletions
|
|
@ -1,7 +1,7 @@
|
|||
# The unit that puts swarm-level secrets into the swarm's secret store, so
|
||||
# whoever needs one can read it there: a hive its agents' credential, a
|
||||
# service or the controller its own, a hive its appservice token. OIDC secrets
|
||||
# it only copies; the appservice token it MINTS, having no swarm-side producer.
|
||||
# service or the controller its own. It mints nothing: every value is an OIDC
|
||||
# client secret authelia minted, and this unit copies it.
|
||||
#
|
||||
# ⚠️ "Whoever", including a reader on THIS host. A swarm service's secret goes
|
||||
# into the store even when the service runs beside authelia, because its
|
||||
|
|
@ -21,13 +21,11 @@
|
|||
# ⚠️ ITS OWN STORE IDENTITY, not swarm-controller's. That principal may rewrite
|
||||
# every hive's policy and login role; a unit that writes a handful of secrets
|
||||
# has no business holding it. ./swarm-bao.nix grants this one `create`/`update`
|
||||
# under the hive prefix and nothing else — no `read`, which is why the mint
|
||||
# below keeps its own record of what it published.
|
||||
# under the hive prefix and nothing else — no `read`.
|
||||
#
|
||||
# ⚠️ THE SECRET NEVER REACHES `argv`. `bao` is an external binary, so every
|
||||
# argument is world-readable in /proc for the life of the call — the value is
|
||||
# passed as `@<path>` and read by bao itself. Same rule governs the mint: it
|
||||
# redirects into a file and never holds the token in a shell variable.
|
||||
# passed as `@<path>` and read by bao itself.
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
|
|
@ -41,7 +39,8 @@ let
|
|||
cfg = deployCfg.swarm-secret-publisher;
|
||||
|
||||
# A reader 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.
|
||||
# standing next to it — the rule ./glue-queue-agent-credential.nix states in
|
||||
# full.
|
||||
# `deploy.bao.enable` here would be the co-location assumption itself.
|
||||
haveClientIdentity = cfg.baoClientCertFile != null && cfg.baoClientKeyFile != null;
|
||||
|
||||
|
|
@ -86,21 +85,6 @@ let
|
|||
# under its own certificate.
|
||||
controllerClientId = hyperhiveCfg.swarm.controller.queueClientId;
|
||||
controllerSecretPath = "secret/swarm/controller/swarm-controller/oidc/client";
|
||||
|
||||
# Where this unit keeps the appservice tokens it minted, and the whole reason
|
||||
# a re-publish is idempotent. The store cannot be that record: ./swarm-bao.nix
|
||||
# grants this principal `create`/`update` and deliberately no `read`, so "does
|
||||
# this hive already have a token?" is a question it can only ask locally.
|
||||
# Without the file every run would mint a fresh value and rotate the swarm's
|
||||
# token for no reason — and a rotation is not free, because the homeserver
|
||||
# only reloads the registration naming it when its container restarts.
|
||||
#
|
||||
# `StateDirectory=` below is what creates it, so the name there is relative
|
||||
# and this unit owns the tree. Losing it mints once more and republishes;
|
||||
# readers pick that up on their next start, and nothing that already
|
||||
# registered breaks — the token authenticates the appservice, it does not
|
||||
# live inside any account it created.
|
||||
matrixTokenDir = "/var/lib/swarm-secret-publish/matrix-appservice-token";
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.deploy.swarm-secret-publisher = {
|
||||
|
|
@ -112,9 +96,7 @@ in
|
|||
Publish the OIDC client secrets this host mints into the swarm's
|
||||
secret store, so a hive that does not run authelia can read its
|
||||
agents' credential and a swarm service or the controller can read its
|
||||
own — from wherever it runs, this host included. Also mints each hive's matrix
|
||||
appservice token, which has no other swarm-side producer, and
|
||||
publishes it the same way.
|
||||
own — from wherever it runs, this host included.
|
||||
|
||||
Defaults to whether this host mints the OIDC secrets, which is the
|
||||
only half of the question that is a property of *this* host.
|
||||
|
|
@ -128,9 +110,8 @@ in
|
|||
|
||||
Turning it off leaves every hive but this one without its agents'
|
||||
credential, the swarm controller unable to start, the swarm's Grafana
|
||||
without any login at all, its collector pushing unauthenticated, and
|
||||
every hive falling back to the appservice token its own first boot
|
||||
minted — so two hives never agree on one. Each secret has exactly one route and this is the producer's
|
||||
without any login at all, and its collector pushing unauthenticated.
|
||||
Each secret has exactly one route and this is the producer's
|
||||
end of it, so the honest reason to set it false is a deployment
|
||||
delivering those secrets by some other mechanism it owns.
|
||||
'';
|
||||
|
|
@ -190,12 +171,6 @@ in
|
|||
TimeoutStartSec = 60;
|
||||
Restart = "on-failure";
|
||||
RestartSec = 30;
|
||||
# The minted appservice tokens, see `matrixTokenDir` above. Named
|
||||
# relative to /var/lib because that is the form systemd creates; the
|
||||
# mode is narrowed from systemd's 0755 default because the directory
|
||||
# listing is the roster of hives and every file in it is a secret.
|
||||
StateDirectory = "swarm-secret-publish/matrix-appservice-token";
|
||||
StateDirectoryMode = "0700";
|
||||
};
|
||||
environment = {
|
||||
BAO_ADDR = "https://${hyperhiveCfg.swarm.bao.domain}:${toString hyperhiveCfg.swarm.bao.port}";
|
||||
|
|
@ -222,7 +197,6 @@ in
|
|||
|
||||
published=0
|
||||
skipped=0
|
||||
tokens=0
|
||||
|
||||
${lib.concatMapStringsSep "\n" (hive: ''
|
||||
src=${lib.escapeShellArg "${deployCfg.authelia.hostClientSecretDir}/${agentClientId hive}.secret"}
|
||||
|
|
@ -281,43 +255,7 @@ in
|
|||
skipped=$((skipped + 1))
|
||||
fi
|
||||
|
||||
# The matrix appservice token, per hive. Unlike the two loops above
|
||||
# there is nothing to copy: authelia never minted this one, and each
|
||||
# homeserver's own host minted its own — which is exactly why two hives
|
||||
# never converged on a value the swarm has to agree on. The mint moves
|
||||
# here because this unit already holds a store identity and already
|
||||
# writes under the hive prefix; ../host-modules/hive-matrix.nix keeps
|
||||
# its activation mint as the first-boot fallback for a hive that has
|
||||
# never reached the store.
|
||||
#
|
||||
# 32 bytes of urandom as lowercase hex, the shape that file has
|
||||
# generated all along. Written with no trailing newline, unlike that
|
||||
# file: what goes into the store is the value, and
|
||||
# ./glue-matrix-bao-token.nix adds the newline the hive's token file
|
||||
# carries.
|
||||
${lib.concatMapStringsSep "\n" (hive: ''
|
||||
src=${lib.escapeShellArg "${matrixTokenDir}/${hive}"}
|
||||
if [ ! -s "$src" ]; then
|
||||
# The subshell keeps the umask local — the loops above rely on
|
||||
# bao's own file handling and should not inherit one.
|
||||
( umask 077; head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "$src" )
|
||||
echo "minted a matrix appservice token for ${hive}" >&2
|
||||
fi
|
||||
# `value=@$src` for the same reason as both loops above: bao opens
|
||||
# the file itself, so the token is never an argument of this process.
|
||||
#
|
||||
# Put on every run, not only on the run that minted — a put of the
|
||||
# value already there changes nothing for any reader, while a mint
|
||||
# whose publish failed must not be left as a token this host holds
|
||||
# and no hive can reach. The `-s` test above is what keeps the VALUE
|
||||
# stable across those runs: rotating on every boot would hand the
|
||||
# homeserver a registration its running container does not carry.
|
||||
bao kv put ${lib.escapeShellArg "secret/swarm/hives/${hive}/matrix/appservice-token"} \
|
||||
value=@"$src"
|
||||
tokens=$((tokens + 1))
|
||||
'') hiveNames}
|
||||
|
||||
echo "published $published client secret(s), skipped $skipped, and $tokens matrix appservice token(s)"
|
||||
echo "published $published client secret(s), skipped $skipped"
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue