From f022e978133a3a91eaa29683eda6c19267fdb8f3 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 14 Aug 2026 10:56:34 +0200 Subject: [PATCH] feat(3150): deliver the OIDC client secret from authelia to the homeserver Runs on the host: the two containers share a network namespace but not a filesystem root, so this is the only place both trees are addressable. A copy rather than a bindMounts entry. nixos-container refuses to start when a bind source is missing, and the secret does not exist until authelia's first boot has minted it. The registration token dodges that with an activation script that pre-creates the file; that is unavailable here, because tuwunel requires the secret to exist and be non-empty, so a placeholder would satisfy the mount and then stop the homeserver. Bounded wait then fail, never a silent skip: authelia's container can be up while its generator is still minting. --- nix/host-modules/hive-matrix.nix | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index 23db1bde..93b6973d 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -660,6 +660,66 @@ in lib.mkDefault matrixSecretPath ); + # The delivery. It runs on the HOST because that is the only place both + # container trees are addressable: they share this host's network + # namespace, which makes them feel co-located, but their filesystem + # roots are separate — the homeserver cannot open a path inside + # authelia's tree however local the port looks. + # + # ⚠️ Deliberately a copy and not a `bindMounts` entry. + # nixos-container refuses to start when a bind source is missing, and + # this secret does not exist until authelia's first boot has minted it + # — so binding it would make the homeserver wait on a file that waits + # on a container that starts after it. On a fresh hive that is a + # permanent stall presenting as "matrix is broken", several layers from + # its cause. + # + # The registration token above dodges that with an activation script + # that pre-creates the file. ⚠️ That dodge is NOT available here: + # tuwunel requires the secret file to exist *and be non-empty*, so a + # zero-byte placeholder would satisfy the bind mount and then stop the + # homeserver from starting. + systemd.services.hive-matrix-oidc-secret = lib.mkIf ssoLocal { + description = "deliver the homeserver's OIDC client secret from authelia"; + after = [ "container@${autheliaCfg.machine}.service" ]; + requires = [ "container@${autheliaCfg.machine}.service" ]; + before = [ "container@hive-matrix.service" ]; + wantedBy = [ "container@hive-matrix.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + SyslogIdentifier = "hive-matrix-oidc-secret"; + }; + path = [ pkgs.coreutils ]; + script = '' + set -euo pipefail + + src=${lib.escapeShellArg "${autheliaCfg.hostClientSecretDir}/${cfg.sso.clientId}.secret"} + dst=${lib.escapeShellArg "/var/lib/nixos-containers/hive-matrix${toString cfg.sso.clientSecretFile}"} + + # authelia's container is up, but its first-boot generator may + # still be minting. Bounded wait, then fail: a silent skip here + # produces a homeserver whose SSO login dead-ends, which is the + # failure this whole design is trying not to ship. + for _ in $(seq 1 60); do + [ -s "$src" ] && break + sleep 2 + done + if [ ! -s "$src" ]; then + echo "authelia has not minted $src after 120s" >&2 + exit 1 + fi + + # root-owned 0400, and deliberately NOT the forge's `stat -c %u` + # uid discovery: that reads the service's state dir to learn which + # uid to hand the file to, and tuwunel runs under `DynamicUser`, so + # there is no stable uid to discover. It never reads this path + # directly anyway — `LoadCredential` does, as root, before the + # sandbox and the dynamic user exist. + install -D -m 0400 -o root -g root "$src" "$dst" + ''; + }; + # ⚠️ Deliberately NO `networking.hosts` entry for authelia's name, and # the difference from hive-forge (which needs one) is worth stating: # that container resolves through the host's resolvers, where the swarm