Watch
0
0
Fork
You've already forked hyperhive
0

remove the hive-c0re forge-credential chain nothing reads any more

With no nix build on the hive, the boot publish of the forge token for nix has no reader. Removes the ForgeSweep node, the token file, the git credential helper and hive-c0re's forge gitconfig sections.
This commit is contained in:
atlas 2026-10-29 22:55:31 +01:00
commit 221c3f35a1
4 changed files with 69 additions and 310 deletions

View file

@ -35,26 +35,18 @@ let
gatewayCfg = config.services.hyperhive.gateway;
};
forgeGit = import ../lib/hive-forge-git.nix {
inherit pkgs;
forgeDomain = config.services.hyperhive.swarm.forge.domain;
};
# Privsep splits ownership across users, so git/libgit2's dubious-
# ownership guard trips on legitimate cross-user reads: hive-c0re
# (hive-core) fetches the agent-owned proposed-config repos. The process is
# trusted and can already read the files; this gitconfig only satisfies the
# ownership guard. libgit2 honours the literal `*` (mid-path globs aren't
# supported, so per-agent repos can't be listed); in practice this process
# only ever touches hyperhive's own repos.
#
# The forge sections (`forgeGit.config`) answer for the `git+http` forge
# fetches nix makes: the agent config inputs and `internal/meta`. hive-c0re's
# own forge reads use `forge::reader_token()` instead.
# only ever touches hyperhive's own repos. Its forge reads authenticate with
# `forge::reader_token()`, handed to git per command, so no credential helper
# is configured.
safeDirGitconfig = pkgs.writeText "hyperhive-safe-gitconfig" ''
[safe]
directory = *
${forgeGit.config}
[http]
# Abort a stalled fetch instead of blocking startup indefinitely. If a
# git http transfer from the forge (e.g. filling an applied mirror)
@ -251,9 +243,6 @@ in
after = [ "hive-c0re.socket" ];
path = [
pkgs.git
# `git-credential-hive-forge` on PATH so git finds it for git+http
# fetches from the forge (helper = hive-forge).
forgeGit.credentialHelper
"/run/current-system/sw"
];
environment = import ./environment.nix { inherit lib config pkgs; };

View file

@ -47,8 +47,8 @@ in
HOME = "/var/lib/hyperhive";
HYPERHIVE_GIT = "${pkgs.git}/bin/git";
# Never let git block on an interactive credential prompt. hive-core is a
# TTY-less system user, so a prompt (e.g. the forge credential helper
# returns nothing because the forge isn't reachable yet on cold boot)
# TTY-less system user, so a prompt (e.g. git asking for credentials
# because the forge isn't reachable yet on cold boot)
# would hang forever and freeze the daemon during startup. With this set,
# git fails fast instead of prompting. Paired with the git http
# low-speed abort in `safeDirGitconfig` (bounds a stalled transfer) and

View file

@ -1,45 +0,0 @@
# Git settings for the host processes that hand nix a forge URL: hive-c0re
# (prebuilds as `hive-core`) and hive-priv (builds as root, with a HOME of its
# own), each through its own `$HOME/.gitconfig`. nix runs `git` for a `git+…`
# input, so git's config is the only credential and URL plumbing it has.
#
# - `credential`: the forge host answers through `git-credential-hive-forge`,
# which reads the hive's own forge account and token from the file hive-c0re
# publishes. An empty `helper =` first resets the list, so no /etc/gitconfig
# helper (e.g. `oauth`, which waits on a browser) runs ahead of ours.
# - `url`: the swarm's `internal/meta` lock names its inputs by the forge's
# `https` URL, which only the builder's credential helper answers for. They
# are fetched over the `http` URL the helper above is scoped to.
#
# Pure function — NOT a NixOS module (don't add it to the host-modules
# aggregator). Call it from a module's `let`:
#
# forgeGit = import ./lib/hive-forge-git.nix { inherit pkgs; forgeDomain = …; };
# # then put `forgeGit.credentialHelper` on the unit's `path` and
# # `forgeGit.config` into its gitconfig.
{ pkgs, forgeDomain }:
{
# Answers only `get`. The file (`/run/hyperhive/forge-hive-token`, hive-c0re's
# `paths::FORGE_HIVE_TOKEN`) holds the account on its first line and the
# token on its second; it is read on every invocation, so it never holds a
# stale copy and survives token rotation. Prints nothing where the file is
# absent or incomplete.
credentialHelper = pkgs.writeShellScriptBin "git-credential-hive-forge" ''
[ "''${1:-}" = "get" ] || exit 0
[ -r /run/hyperhive/forge-hive-token ] || exit 0
{ read -r login; read -r token; } < /run/hyperhive/forge-hive-token || true
[ -n "$login" ] && [ -n "$token" ] || exit 0
printf 'username=%s\n' "$login"
printf 'password=%s\n' "$token"
'';
config = ''
[credential]
helper =
[credential "http://${forgeDomain}"]
helper =
helper = hive-forge
[url "http://${forgeDomain}/"]
insteadOf = https://${forgeDomain}/
'';
}

View file

@ -234,39 +234,25 @@ let
&& env shared.controllerAlone == { };
}
{
# hive-c0re carries the credential helper and the https-to-http rewrite as
# git config. hive-priv builds nothing and fetches nothing from the forge,
# so it carries neither and has no forge URL. The hive-c0re arm is the
# control that the lookups find a helper and a forge URL where there is
# one, so the absences on hive-priv and the controller are real.
name = "hive-c0re answers the forge's git fetches and rewrites its https inputs to http; hive-priv has neither";
# No host process hands nix a forge URL any more, so none carries a git
# credential helper for the forge. The hive-c0re arm is the control: its
# `git` and its forge URL are found by the same lookups, so the absences
# are real.
name = "no host unit carries the forge credential helper, and hive-priv has no forge URL";
ok =
let
forge = allLocal.services.hyperhive.swarm.forge.domain;
git = import ../host-modules/lib/hive-forge-git.nix {
inherit pkgs;
forgeDomain = forge;
};
helperOn =
pathNames =
unit:
lib.any (
p: builtins.isAttrs p && (p.name or "") == "git-credential-hive-forge"
) allLocal.systemd.services.${unit}.path;
map (p: if builtins.isAttrs p then p.name or "" else "") allLocal.systemd.services.${unit}.path;
env = unit: allLocal.systemd.services.${unit}.environment;
in
helperOn "hive-c0re"
&& !(helperOn "hive-priv")
# The helper answers from the file hive-c0re publishes the hive's own
# token to. The first arm is the control for the second: the lookup
# finds the helper's text, so the absent `core` strings are really absent.
&& lib.hasInfix "/run/hyperhive/forge-hive-token" git.credentialHelper.text
&& !(lib.hasInfix "forge-core-token" git.credentialHelper.text)
&& !(lib.hasInfix "username=core" git.credentialHelper.text)
&& !(lib.hasInfix "username = core" git.config)
&& !(helperOn "swarm-controller")
&& lib.hasInfix "[credential \"http://${forge}\"]\nhelper =\nhelper = hive-forge" git.config
&& lib.hasInfix "[url \"http://${forge}/\"]\ninsteadOf = https://${forge}/" git.config
lib.any (lib.hasPrefix "git-2") (pathNames "hive-c0re")
&& (env "hive-c0re") ? HIVE_FORGE_URL
&& lib.all (unit: !(builtins.elem "git-credential-hive-forge" (pathNames unit))) [
"hive-c0re"
"hive-priv"
"swarm-controller"
]
&& !((env "hive-priv") ? HIVE_PRIV_FORGE_URL);
}
{