glue-matrix-bao-token: the store's first reader
Fetches an opaque 32-byte value and writes it where hive-matrix.nix already looks, so the homeserver never learns the store exists. Chosen over authelia's OIDC secret deliberately: that one needs a .secret AND a matching .digest, so shipping it first would debug 'can a reader authenticate and get bytes back' and 'did we write authelia's file format right' at once, with an SSO outage as the failure mode. Here the failure is narrow -- new agent accounts cannot be provisioned, existing ones untouched. Every failure path keeps the local token: no such key, sealed store, unreachable store, empty value. The activation script's mint-if-absent is untouched, so a hive with no store behaves exactly as it does today. matrixMachine is a literal because hive-matrix.nix declares no `machine` option -- `matrixCfg.machine` parses cleanly and fails at module-system resolution, which is the kind of error only reading the target module catches.
This commit is contained in:
parent
9bbc74ee51
commit
0e5eb1e8d6
2 changed files with 104 additions and 0 deletions
|
|
@ -24,6 +24,7 @@
|
|||
./hive-tls.nix
|
||||
./otel.nix
|
||||
./glue-bao-tls.nix
|
||||
./glue-matrix-bao-token.nix
|
||||
./swarm-authelia.nix
|
||||
./swarm-bao.nix
|
||||
./swarm-ca.nix
|
||||
|
|
|
|||
103
nix/host-modules/glue-matrix-bao-token.nix
Normal file
103
nix/host-modules/glue-matrix-bao-token.nix
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Glue: the matrix registration token comes from the secret store.
|
||||
#
|
||||
# The store's first reader, and deliberately a small one. It fetches an opaque
|
||||
# 32-byte value and writes it where ./hive-matrix.nix already looks — the
|
||||
# homeserver never learns the store exists, and its config is unchanged.
|
||||
#
|
||||
# ⚠️ Why this credential first. It has no second file and no format: authelia's
|
||||
# OIDC secret needs a `.secret` *and* a matching `.digest`, so shipping that
|
||||
# one first would debug "can a reader authenticate and get bytes back" and
|
||||
# "did we write authelia's file format right" at the same time, with an SSO
|
||||
# outage as the failure mode. Here the failure is narrow — new agent accounts
|
||||
# cannot be provisioned, existing ones are untouched, nothing crash-loops.
|
||||
#
|
||||
# ⚠️ The fallback is today's behaviour, not a new one. `hive-matrix.nix`'s
|
||||
# activation script still mints a token when the file is absent; this unit
|
||||
# overwrites it with the swarm's copy when the store has one. A store that is
|
||||
# empty or unreachable leaves a working hive with a local token.
|
||||
#
|
||||
# 📌 LIMIT, stated rather than hidden: this gates on the store running HERE.
|
||||
# A hive reading from a store on another machine needs the same unit with that
|
||||
# machine's address and a client leaf issued out of band — the mechanism is
|
||||
# identical, only `-address` and the cert's provenance differ. Deferred until
|
||||
# there is a second hive to test it against, rather than shipped untested.
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
hyperhiveCfg = config.services.hyperhive;
|
||||
deployCfg = hyperhiveCfg.deploy;
|
||||
baoCfg = hyperhiveCfg.swarm.bao;
|
||||
matrixCfg = hyperhiveCfg.swarm.matrix;
|
||||
|
||||
# Owned by ./glue-bao-tls.nix, which mints them. Named here rather than
|
||||
# shared through a `let`: a cross-module binding would make these two files
|
||||
# one file with a gap in the middle, and the whole point of a glue module is
|
||||
# that it can be deleted on its own.
|
||||
pkiDir = "/var/lib/swarm-bao-pki";
|
||||
|
||||
# Where the token lives in the store. A path, not a convention to guess at:
|
||||
# whoever writes it and whoever reads it must agree, and the agreement
|
||||
# belongs in one visible place.
|
||||
tokenPath = "secret/swarm/matrix/registration-token";
|
||||
|
||||
# A literal, not an option — ./hive-matrix.nix names its container
|
||||
# `containers.hive-matrix` directly and declares no `machine` to derive it
|
||||
# from, which the trust-bundle call in that file already says out loud.
|
||||
# ⚠️ `matrixCfg.machine` parses fine and fails at module-system resolution,
|
||||
# so this is the kind of mistake only reading the target module catches.
|
||||
matrixMachine = "hive-matrix";
|
||||
in
|
||||
{
|
||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.bao.enable && deployCfg.matrix.enable) {
|
||||
systemd.services.swarm-bao-matrix-token = {
|
||||
description = "fetch the matrix registration token from the swarm secret store";
|
||||
after = [
|
||||
"swarm-bao-pki.service"
|
||||
"container@${baoCfg.machine}.service"
|
||||
];
|
||||
wants = [ "container@${baoCfg.machine}.service" ];
|
||||
requires = [ "swarm-bao-pki.service" ];
|
||||
before = [ "container@${matrixMachine}.service" ];
|
||||
wantedBy = [ "container@${matrixMachine}.service" ];
|
||||
path = [
|
||||
deployCfg.bao.package
|
||||
pkgs.coreutils
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
environment = {
|
||||
BAO_ADDR = "https://${baoCfg.domain}:${toString baoCfg.port}";
|
||||
BAO_CACERT = "${pkiDir}/ca.pem";
|
||||
BAO_CLIENT_CERT = "${pkiDir}/client.pem";
|
||||
BAO_CLIENT_KEY = "${pkiDir}/client-key.pem";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
# A sealed or uninitialised store answers on the port and times out on
|
||||
# every read, so "the store is up" is not the same as "the store can
|
||||
# answer" -- bound the wait rather than hanging the boot behind it.
|
||||
if ! token="$(bao kv get -field=value ${lib.escapeShellArg tokenPath} 2>/dev/null)"; then
|
||||
echo "swarm-bao holds no ${tokenPath}, or is sealed/unreachable." >&2
|
||||
echo "Keeping the token hive-matrix already has." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$token" ]; then
|
||||
echo "swarm-bao returned an empty ${tokenPath}; keeping the local token." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
umask 077
|
||||
printf '%s\n' "$token" > ${lib.escapeShellArg (toString matrixCfg.registrationTokenFile)}
|
||||
chmod 0600 ${lib.escapeShellArg (toString matrixCfg.registrationTokenFile)}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue