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.
This commit is contained in:
parent
59ecefe0e1
commit
f022e97813
1 changed files with 60 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue