Watch
0
0
Fork
You've already forked hyperhive
0

hive-c0re pulls closures through the nix daemon; hive-priv verifies the swarm signature before applying

This commit is contained in:
atlas 2026-11-07 13:20:55 +01:00 • committed by mara
commit ecd4445680

View file

@ -1,18 +1,21 @@
# The binary-cache facts hive-priv (root) trusts, kept where only root can
# change them.
# The binary-cache facts the host's nix daemon and hive-priv (root) trust, kept
# where only root can change them.
#
# hive-priv downloads an agent's closure from the swarm cache when a rebuild
# asks it to. What decides whether a download is accepted — which cache, and
# which key must have signed it — must not come from a file the unprivileged
# hive-c0re writes, or a compromised hive-c0re could have root accept a
# closure from a cache of its own. So:
# hive-c0re downloads an agent's closure from the swarm cache through the
# host's nix daemon, and hive-priv applies it only if the swarm's key signed
# it. What decides whether a download is accepted — which cache the daemon
# lets an unprivileged client name, and which key must have signed it — must
# not come from a file the unprivileged hive-c0re writes, or a compromised
# hive-c0re could have root accept a closure from a cache of its own. So:
#
# - the cache's address is this host's configuration
# ({option}`services.hyperhive.swarm.attic.domain` and `cache`), handed to
# hive-priv in its unit's environment;
# ({option}`services.hyperhive.swarm.attic.domain` and `cache`);
# - the cache's public key and the pull token are read from the store by a root
# oneshot, `hive-priv-cache-facts`, under this hive's own store identity, and
# land in `/run/hyperhive-priv/attic/` (root, `0700`; files `0600`).
# oneshot, `hive-priv-cache-facts`, under this hive's own store identity. The
# key and the token land in `/run/hyperhive-priv/attic/` (root, `0700`; files
# `0600`), and the address, the key and the netrc's path in a nix.conf
# fragment, `/run/hyperhive-nix/cache.conf`, which the host's nix.conf
# includes (`!include`, so a host whose unit has not run yet still starts).
#
# hive-c0re keeps its own copies under `/run/hyperhive/attic/` for the probe,
# which only reports.
@ -42,6 +45,9 @@ let
&& tls.baoHiveIdentityKeyFile != null;
dir = "/run/hyperhive-priv/attic";
confDir = "/run/hyperhive-nix";
confFile = "${confDir}/cache.conf";
url = "https://${atticCfg.domain}/${atticCfg.cache}";
# Where the store runs on this host, the first login waits for its container.
# No `wants`: a pull would start the container mid-switch on the old closure.
@ -52,15 +58,16 @@ let
in
{
config = lib.mkIf enabled {
systemd.tmpfiles.rules = [ "d /run/hyperhive-priv 0700 root root - -" ];
systemd.tmpfiles.rules = [
"d /run/hyperhive-priv 0700 root root - -"
"d ${confDir} 0755 root root - -"
];
# The URL nix is told is the only substituter for the download; no request
# chooses it.
systemd.services.hive-priv.environment.HIVE_PRIV_CACHE_URL =
"https://${atticCfg.domain}/${atticCfg.cache}";
# `!include` tolerates a fragment that does not exist yet.
nix.extraOptions = "!include ${confFile}";
systemd.services.hive-priv-cache-facts = {
description = "Read the swarm cache's public key and pull token for hive-priv";
description = "Read the swarm cache's public key and pull token for the host's nix daemon and hive-priv";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [
@ -94,7 +101,7 @@ in
script = import ./lib/attic-root-facts.nix { inherit lib; } {
hive = hiveController.hiveName;
host = atticCfg.domain;
inherit dir;
inherit dir url confFile;
};
};