Watch
0
0
Fork
You've already forked hyperhive
0

run the platform containers' root check through a build-independent path

This commit is contained in:
atlas 2026-10-30 11:13:16 +01:00
commit 29c57e058a
4 changed files with 34 additions and 11 deletions

View file

@ -418,7 +418,10 @@ until the next collection. A drop-in written by an older hive-priv has no
**Hyperhive's platform containers: warn only.** The forge, the matrix server and **Hyperhive's platform containers: warn only.** The forge, the matrix server and
the swarm services (15 containers, declared by their own modules) run the same the swarm services (15 containers, declared by their own modules) run the same
check as `hive-priv check-rootfs --warn-only <name>`, from `ExecStartPre=` on check as `hive-priv check-rootfs --warn-only <name>`, from `ExecStartPre=` on
their own `container@<name>` unit. It evaluates every rule, logs each violation their own `container@<name>` unit, through the symlink `/run/hive-rootfs-check`
that `hive-priv.nix` points at the current build, so a new hyperhive build does
not change the unit or restart the container. It evaluates every rule, logs each
violation
as a warning, and exits 0, so the container starts either way. The line reads as a warning, and exits 0, so the container starts either way. The line reads
`rootfs check (warn-only): <name>: <message>` with journald's warning priority. `rootfs check (warn-only): <name>: <message>` with journald's warning priority.
Strict refusal needs the live roots of all 15 to pass first, and the warnings Strict refusal needs the live roots of all 15 to pass first, and the warnings

View file

@ -9,13 +9,19 @@
}: }:
let let
controllerCfg = config.services.hyperhive.deploy.hive-controller; controllerCfg = config.services.hyperhive.deploy.hive-controller;
rootfsCheck = import ./lib/container-rootfs-check.nix { inherit config; };
in in
{ {
config = lib.mkIf config.services.hyperhive.deploy.hive-controller.enable { config = lib.mkIf config.services.hyperhive.deploy.hive-controller.enable {
# The parent of every agent socket dir. hive-priv is its only writer # The parent of every agent socket dir. hive-priv is its only writer
# (`EnsureAgentSocketDir`), and its unit lists it in `ReadWritePaths`, # (`EnsureAgentSocketDir`), and its unit lists it in `ReadWritePaths`,
# which fails the unit when the path is missing. # which fails the unit when the path is missing.
systemd.tmpfiles.rules = [ "d /run/hive-agent 0755 root root - -" ]; systemd.tmpfiles.rules = [
"d /run/hive-agent 0755 root root - -"
# The platform containers run their warn-only root check by this path
# (`lib/container-rootfs-check.nix`), so their units carry no store path.
"L+ ${rootfsCheck.hivePriv} - - - - ${controllerCfg.package}/bin/hive-priv"
];
# Socket unit for hive-priv — the narrow root helper that executes # Socket unit for hive-priv — the narrow root helper that executes
# privileged operations on behalf of hive-c0re. Systemd creates and # privileged operations on behalf of hive-c0re. Systemd creates and

View file

@ -23,12 +23,19 @@
# `container@<name>` unit (`nixos-containers.nix`, the `mapAttrsToList` that # `container@<name>` unit (`nixos-containers.nix`, the `mapAttrsToList` that
# builds `container@${name}`), so a setting on that unit reaches that # builds `container@${name}`), so a setting on that unit reaches that
# container only. # container only.
{ config }: #
{ # The binary is run through `hivePriv`, a path that does not change with the hyperhive
# build. `hive-priv.nix` points the symlink at the current package, so the
# unit text is the same across builds and a switch does not restart the
# container for it.
{ ... }:
rec {
hivePriv = "/run/hive-rootfs-check";
# Attributes for `systemd.services."container@<name>"`. # Attributes for `systemd.services."container@<name>"`.
warnOnly = name: { warnOnly = name: {
serviceConfig.ExecStartPre = [ serviceConfig.ExecStartPre = [
"-${config.services.hyperhive.deploy.hive-controller.package}/bin/hive-priv check-rootfs --warn-only ${name}" "-${hivePriv} check-rootfs --warn-only ${name}"
]; ];
}; };
} }

View file

@ -1814,7 +1814,7 @@ let
# their own unit, warn-only. Every container in these fixtures is one of # their own unit, warn-only. Every container in these fixtures is one of
# hyperhive's, so the names are read off the fixture: a container added # hyperhive's, so the names are read off the fixture: a container added
# without the check breaks the list, which is the point. The unit's own # without the check breaks the list, which is the point. The unit's own
# pre-start script is not a `hive-priv` entry, so the count is the check # pre-start script is not a `check-rootfs` entry, so the count is the check
# alone. # alone.
name = "every hyperhive container runs the root check warn-only from its own unit, and no hyperhive container is left out"; name = "every hyperhive container runs the root check warn-only from its own unit, and no hyperhive container is left out";
ok = ok =
@ -1824,13 +1824,13 @@ let
let let
pre = m.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ]; pre = m.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ];
in in
builtins.filter (lib.hasInfix "hive-priv") ( builtins.filter (lib.hasInfix "check-rootfs") (
map (p: builtins.unsafeDiscardStringContext (toString p)) pre map (p: builtins.unsafeDiscardStringContext (toString p)) pre
); );
carries = carries =
m: name: m: name:
checks m name == [ checks m name == [
(builtins.unsafeDiscardStringContext "-${m.services.hyperhive.deploy.hive-controller.package}/bin/hive-priv check-rootfs --warn-only ${name}") "-/run/hive-rootfs-check check-rootfs --warn-only ${name}"
]; ];
names = lib.attrNames allLocal.containers; names = lib.attrNames allLocal.containers;
in in
@ -1853,6 +1853,13 @@ let
&& lib.all (carries allLocal) names && lib.all (carries allLocal) names
&& withCi.containers ? swarm-ci-runner && withCi.containers ? swarm-ci-runner
&& lib.all (carries withCi) (lib.attrNames withCi.containers) && lib.all (carries withCi) (lib.attrNames withCi.containers)
# The line holds no store path, so a new hyperhive build leaves the
# unit text alone. Presence control: the lookup does find the line,
# and a line with a store path is caught by the same test.
&& lib.all (n: !(lib.hasInfix "/nix/store" (lib.head (checks allLocal n)))) names
&& lib.hasInfix "/nix/store" "-/nix/store/x-hyperhive/bin/hive-priv check-rootfs --warn-only x"
# The stable path is a link to the current package's hive-priv.
&& lib.elem "L+ /run/hive-rootfs-check - - - - ${allLocal.services.hyperhive.deploy.hive-controller.package}/bin/hive-priv" allLocal.systemd.tmpfiles.rules
# Control: a name with no unit finds nothing. # Control: a name with no unit finds nothing.
&& checks allLocal "swarm-nope" == [ ]; && checks allLocal "swarm-nope" == [ ];
} }
@ -1868,7 +1875,7 @@ let
let let
hivePrivLines = hivePrivLines =
m: name: m: name:
builtins.filter (lib.hasInfix "hive-priv") ( builtins.filter (lib.hasInfix "check-rootfs") (
map (p: builtins.unsafeDiscardStringContext (toString p)) ( map (p: builtins.unsafeDiscardStringContext (toString p)) (
m.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ] m.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ]
) )
@ -1881,7 +1888,7 @@ let
&& lib.all ignored (allLines allLocal) && lib.all ignored (allLines allLocal)
&& lib.all ignored (allLines withCi) && lib.all ignored (allLines withCi)
# Control: the test discriminates, a line without the prefix is not ignored. # Control: the test discriminates, a line without the prefix is not ignored.
&& !(ignored "/nix/store/x-hyperhive/bin/hive-priv check-rootfs --warn-only x"); && !(ignored "/run/hive-rootfs-check check-rootfs --warn-only x");
} }
{ {
# The operator's other containers are not hyperhive's to touch: a # The operator's other containers are not hyperhive's to touch: a
@ -1893,7 +1900,7 @@ let
let let
checks = checks =
name: name:
builtins.filter (lib.hasInfix "hive-priv") ( builtins.filter (lib.hasInfix "check-rootfs") (
map (p: builtins.unsafeDiscardStringContext (toString p)) ( map (p: builtins.unsafeDiscardStringContext (toString p)) (
withForeignContainer.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ] withForeignContainer.systemd.services."container@${name}".serviceConfig.ExecStartPre or [ ]
) )