matrix: pin tuwunel gid 10042, chown root:tuwunel + 0640 on token (#644 mara veto on 0644)
This commit is contained in:
parent
75648a7594
commit
88b4634906
2 changed files with 44 additions and 20 deletions
|
|
@ -123,13 +123,16 @@ pub fn ensure_register_token() -> Result<String> {
|
|||
}
|
||||
std::fs::write(path, format!("{token}\n"))
|
||||
.with_context(|| format!("write registration token to {}", path.display()))?;
|
||||
// Mode 0644: tuwunel inside the hive-matrix container runs as its
|
||||
// own dynamic user (not root), so the bind-mounted file needs to
|
||||
// be world-readable for tuwunel to load it. 0600 root-owned would
|
||||
// block tuwunel with `Permission denied (os error 13)` (#644). On
|
||||
// a single-tenant host the trade-off is acceptable; multi-tenant
|
||||
// would need group-readable with explicit gid matching.
|
||||
let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o644));
|
||||
// Mode 0640: tuwunel inside the hive-matrix container runs as a
|
||||
// non-root user; the file gets `chown :tuwunel` via the activation
|
||||
// script in `nix/modules/hive-matrix.nix` so the tuwunel group
|
||||
// gains read. 0600 would block tuwunel with `Permission denied
|
||||
// (os error 13)` (#644); 0644 would world-read the token (mara
|
||||
// veto: footgun). 0640 with a pinned group is the sweet spot.
|
||||
// The host-side activation script also chowns + chmods on every
|
||||
// boot, so this is best-effort: hive-c0re may write the file
|
||||
// before the chown lands, but the next activation reconciles it.
|
||||
let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o640));
|
||||
tracing::info!(path = %path.display(), "matrix: generated registration token");
|
||||
Ok(token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,6 +241,18 @@ 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
|
||||
# the hive-matrix container would otherwise start with an empty
|
||||
# bind-mount target (argus nit on #565: nspawn creates an empty
|
||||
|
|
@ -250,16 +262,15 @@ in
|
|||
# doesn't exist. 32-byte hex = 64 chars, same shape hive-c0re's
|
||||
# `matrix::ensure_register_token` would produce.
|
||||
#
|
||||
# Mode 0644 (world-readable): tuwunel inside the hive-matrix
|
||||
# container runs as its own dynamic user, which doesn't match the
|
||||
# host's root UID, so a 0600 root-owned bind mount blocks tuwunel
|
||||
# from reading the file (`Permission denied (os error 13)` —
|
||||
# observed on #644). 0644 trades "any user on the host can read
|
||||
# the token" for "tuwunel can actually start". On a single-tenant
|
||||
# host that's an acceptable trade; tightening to a group-readable
|
||||
# mode with explicit gid matching is a follow-up if multi-tenant
|
||||
# hosts need it.
|
||||
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" ] ''
|
||||
# Ownership: tuwunel inside the hive-matrix container runs as its
|
||||
# own non-root user (nixpkgs's `services.matrix-tuwunel`), so a
|
||||
# 0600 root-owned file denies it open(2) and tuwunel boots loop-
|
||||
# fails with `Permission denied (os error 13)` (#644). Fix:
|
||||
# `chown root:tuwunel` + `chmod 0640` so only the tuwunel group
|
||||
# gains read access (no world-readable footgun, per mara). The
|
||||
# `tuwunel` group GID is pinned to 10042 above so the host's name
|
||||
# → number lookup matches what the container sees.
|
||||
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" "users" ] ''
|
||||
tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)}
|
||||
if [ ! -s "$tokenFile" ]; then
|
||||
mkdir -p "$(dirname "$tokenFile")"
|
||||
|
|
@ -267,9 +278,11 @@ in
|
|||
echo >> "$tokenFile"
|
||||
echo "hive-matrix: generated registration token at $tokenFile"
|
||||
fi
|
||||
# Always re-apply the mode (covers existing 0600 files from
|
||||
# before #644 — `chmod` is idempotent).
|
||||
chmod 644 "$tokenFile"
|
||||
# Always re-apply ownership + mode (covers existing 0600 root-
|
||||
# owned files from pre-#644 deployments; `chown` + `chmod` are
|
||||
# both idempotent).
|
||||
chown root:tuwunel "$tokenFile"
|
||||
chmod 0640 "$tokenFile"
|
||||
'';
|
||||
|
||||
containers.hive-matrix = {
|
||||
|
|
@ -293,6 +306,14 @@ in
|
|||
{ ... }:
|
||||
{
|
||||
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 = {
|
||||
enable = true;
|
||||
package = cfg.package;
|
||||
|
|
|
|||
Loading…
Reference in a new issue