nix/hive-matrix: read register token via systemd LoadCredential (#644 / iris)
Per iris's recommendation on #644 [comment 8043](http://localhost:3000/hyperhive/hyperhive/issues/644#issuecomment-8043): swap the `chown root:tuwunel + chmod 0640 + pinned GID 10042` shape (shipped via #649) for systemd's `LoadCredential=` mechanism. How it works: systemd reads the host-side file at service start, copies it into a per-service credentials dir (`/run/credentials/tuwunel.service/registration_token`) owned by the dynamic user with mode 0400. Service reads from there. All the namespace mapping happens transparently inside systemd — keeps `DynamicUser=true` + `PrivateUsers=true` intact. Net diff from current shape: - DROP `users.groups.tuwunel.gid = 10042;` from BOTH host AND container - DROP `chown root:tuwunel "$tokenFile"; chmod 0640 "$tokenFile"` from activation script; replace with `chmod 0600` (root:root) - DROP `[ "var" "users" ]` activation dep on `users` (no longer needs the group to exist before chown) - ADD `systemd.services.tuwunel.serviceConfig.LoadCredential = [...]` inside the container config - CHANGE `registration_token_file` from the bind-mount path to `/run/credentials/tuwunel.service/registration_token` - KEEP the bind mount + activation-script token generation (load credential reads the bind-mounted host file at service start) Verified via `nix eval`: - host: no `users.groups.tuwunel` (was: gid = 10042) - container: tuwunel group exists with `gid = null` (auto-allocated; no longer pinned to match host since it doesn't need to) - container: tuwunel.service.serviceConfig.LoadCredential = `["registration_token:/var/lib/hyperhive/matrix-register-token"]` - container: services.matrix-tuwunel.settings.global.registration_token_file = `/run/credentials/tuwunel.service/registration_token` `/run/credentials/<service>/<id>` is a systemd-stable path (documented in `man systemd.exec` → LoadCredential); safe to hardcode.
This commit is contained in:
parent
c5d466c5c5
commit
2e40e1782a
1 changed files with 30 additions and 35 deletions
|
|
@ -250,18 +250,6 @@ in
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
# Pin the `tuwunel` group at a fixed GID on BOTH the host and the
|
|
||||||
# hive-matrix container. The registration token file lives on the
|
|
||||||
# host bind-mounted into the container; for tuwunel's non-root
|
|
||||||
# user inside the container to read it, the file gets `chown
|
|
||||||
# root:tuwunel` + mode `0640` in the activation script below. That
|
|
||||||
# ownership only works if the numeric GID resolves to the same
|
|
||||||
# name on both sides of the bind — without an explicit pin, the
|
|
||||||
# host's auto-allocated GID for `tuwunel` (if any) almost
|
|
||||||
# certainly wouldn't match the container's. 10042 sits well
|
|
||||||
# outside nixos's auto-allocated system-user range (200..399).
|
|
||||||
users.groups.tuwunel.gid = 10042;
|
|
||||||
|
|
||||||
# Generate the registration token at system activation time, BEFORE
|
# Generate the registration token at system activation time, BEFORE
|
||||||
# the hive-matrix container would otherwise start with an empty
|
# the hive-matrix container would otherwise start with an empty
|
||||||
# bind-mount target (argus nit on #565: nspawn creates an empty
|
# bind-mount target (argus nit on #565: nspawn creates an empty
|
||||||
|
|
@ -271,15 +259,14 @@ in
|
||||||
# doesn't exist. 32-byte hex = 64 chars, same shape hive-c0re's
|
# doesn't exist. 32-byte hex = 64 chars, same shape hive-c0re's
|
||||||
# `matrix::ensure_register_token` would produce.
|
# `matrix::ensure_register_token` would produce.
|
||||||
#
|
#
|
||||||
# Ownership: tuwunel inside the hive-matrix container runs as its
|
# Ownership: plain `root:root 0600` — tuwunel inside the container
|
||||||
# own non-root user (nixpkgs's `services.matrix-tuwunel`), so a
|
# runs as a hardened dynamic user (#644) and reads the token via
|
||||||
# 0600 root-owned file denies it open(2) and tuwunel boots loop-
|
# systemd's `LoadCredential=` mechanism (see container config
|
||||||
# fails with `Permission denied (os error 13)` (#644). Fix:
|
# below), so it never needs direct read access on the host-side
|
||||||
# `chown root:tuwunel` + `chmod 0640` so only the tuwunel group
|
# file. No `chown :tuwunel` / `chmod 0640` / GID-pin gymnastics
|
||||||
# gains read access (no world-readable footgun, per mara). The
|
# required (per iris on #644 8043, dropping the shape #649
|
||||||
# `tuwunel` group GID is pinned to 10042 above so the host's name
|
# shipped with).
|
||||||
# → number lookup matches what the container sees.
|
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" ] ''
|
||||||
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" "users" ] ''
|
|
||||||
tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)}
|
tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)}
|
||||||
if [ ! -s "$tokenFile" ]; then
|
if [ ! -s "$tokenFile" ]; then
|
||||||
mkdir -p "$(dirname "$tokenFile")"
|
mkdir -p "$(dirname "$tokenFile")"
|
||||||
|
|
@ -287,11 +274,10 @@ in
|
||||||
echo >> "$tokenFile"
|
echo >> "$tokenFile"
|
||||||
echo "hive-matrix: generated registration token at $tokenFile"
|
echo "hive-matrix: generated registration token at $tokenFile"
|
||||||
fi
|
fi
|
||||||
# Always re-apply ownership + mode (covers existing 0600 root-
|
# Always re-apply 0600 (idempotent on already-correct files;
|
||||||
# owned files from pre-#644 deployments; `chown` + `chmod` are
|
# also normalises any 0640 / world-readable carry-over from
|
||||||
# both idempotent).
|
# pre-LoadCredential deployments).
|
||||||
chown root:tuwunel "$tokenFile"
|
chmod 0600 "$tokenFile"
|
||||||
chmod 0640 "$tokenFile"
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
containers.hive-matrix = {
|
containers.hive-matrix = {
|
||||||
|
|
@ -315,14 +301,6 @@ in
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
system.stateVersion = "26.05";
|
system.stateVersion = "26.05";
|
||||||
# Mirror the host's pinned `tuwunel` GID so the bind-mounted
|
|
||||||
# registration token (chowned `root:tuwunel` on the host)
|
|
||||||
# resolves to the same group inside the container. Without
|
|
||||||
# this pin nixos auto-allocates whatever's free, the two
|
|
||||||
# sides diverge, and tuwunel's user falls back to the
|
|
||||||
# "other" mode bits (= no read) on the file. See the host-
|
|
||||||
# side `users.groups.tuwunel.gid` above.
|
|
||||||
users.groups.tuwunel.gid = 10042;
|
|
||||||
services.matrix-tuwunel = {
|
services.matrix-tuwunel = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = cfg.package;
|
package = cfg.package;
|
||||||
|
|
@ -346,12 +324,29 @@ in
|
||||||
# `yes_i_am_very_very_sure_…_open_registration_…` flag
|
# `yes_i_am_very_very_sure_…_open_registration_…` flag
|
||||||
# keeps the server closed to anyone without the token.
|
# keeps the server closed to anyone without the token.
|
||||||
allow_registration = true;
|
allow_registration = true;
|
||||||
registration_token_file = toString cfg.registrationTokenFile;
|
# Read the registration token via systemd's
|
||||||
|
# `LoadCredential=` mechanism (wired below) instead of
|
||||||
|
# the bind-mount path directly. systemd copies the host-
|
||||||
|
# owned 0600 root:root file into a per-service
|
||||||
|
# credentials dir owned by tuwunel's dynamic user with
|
||||||
|
# mode 0400 — keeps `DynamicUser=true` + `PrivateUsers=true`
|
||||||
|
# intact, no host-side `chown :tuwunel` / GID-pin
|
||||||
|
# gymnastics required (#644 / iris on 8043).
|
||||||
|
registration_token_file = "/run/credentials/tuwunel.service/registration_token";
|
||||||
# E2EE disabled in initial rollout per operator call
|
# E2EE disabled in initial rollout per operator call
|
||||||
# (#548) — re-enabling tracked at #551.
|
# (#548) — re-enabling tracked at #551.
|
||||||
allow_encryption = false;
|
allow_encryption = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
# `LoadCredential=<id>:<host-path>` makes systemd copy the
|
||||||
|
# bind-mounted host file into `/run/credentials/tuwunel.service/<id>`
|
||||||
|
# owned by the service's (dynamic) user with mode 0400 at
|
||||||
|
# service start. The hardcoded path in `registration_token_file`
|
||||||
|
# above is the systemd-stable credentials dir; see
|
||||||
|
# `man systemd.exec` → LoadCredential.
|
||||||
|
systemd.services.tuwunel.serviceConfig.LoadCredential = [
|
||||||
|
"registration_token:${toString cfg.registrationTokenFile}"
|
||||||
|
];
|
||||||
environment.systemPackages = [ cfg.package ];
|
environment.systemPackages = [ cfg.package ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue