Watch
0
0
Fork
You've already forked hyperhive
0

fetch the self-check's password as a systemd credential

This commit is contained in:
atlas 2026-10-28 13:07:15 +01:00 • committed by mara
commit fd63b402e1
3 changed files with 146 additions and 9 deletions

View file

@ -25,6 +25,7 @@
./hive-priv.nix
./hive-priv-cache.nix
./hive-squid.nix
./hive-squid-credential.nix
./hive-tls.nix
./otel.nix
./glue-bao-readers-policy-order.nix

View file

@ -0,0 +1,103 @@
# The password this hive's squid self-check logs in to squid with, kept where
# only root can read it.
#
# swarm-controller mints it at `swarm/hives/<hive>/squid-selfcheck`
# (`swarm_secret_client::proxy::selfcheck_path`). hive-c0re reads the same path
# to put its hash in squid's users file; this root oneshot reads it under the
# hive's own store identity and lands it as the systemd credential
# `squid-selfcheck` imports (`/run/credstore/<name>`, root, `0400`, tmpfs).
#
# A hive with no minted credential yet leaves the file alone and the self-check
# reports `credential=absent`.
{
pkgs,
lib,
config,
...
}:
let
hyperhiveCfg = config.services.hyperhive;
hiveController = hyperhiveCfg.deploy.hive-controller;
tls = hiveController.tls;
baoCfg = hyperhiveCfg.swarm.bao;
baoDeploy = hyperhiveCfg.deploy.bao;
enabled =
hiveController.enable
&& hiveController.hiveName != null
&& tls.baoHiveIdentityCertFile != null
&& tls.baoHiveIdentityKeyFile != null;
credentialFile = "/run/credstore/${hyperhiveCfg.squid.selfcheck.credentialName}";
storeRetry = import ./lib/store-retry.nix { };
storeRefreshTimer = import ./lib/store-refresh-timer.nix { };
in
{
config = lib.mkIf enabled {
systemd.services.squid-selfcheck-credential = {
description = "Read the squid self-check's password from the swarm secret store";
wantedBy = [ "multi-user.target" ];
before = [ "squid-selfcheck.service" ];
wants = [ "network-online.target" ];
after = [
"network-online.target"
"systemd-tmpfiles-setup.service"
];
path = [
baoDeploy.package
pkgs.coreutils
pkgs.gnugrep
];
environment = {
BAO_ADDR = "https://${baoCfg.domain}:${toString baoCfg.port}";
BAO_CLIENT_CERT = tls.baoHiveIdentityCertFile;
BAO_CLIENT_KEY = tls.baoHiveIdentityKeyFile;
}
// lib.optionalAttrs (baoDeploy.serverCaFile != null) {
BAO_CACERT = baoDeploy.serverCaFile;
};
# ./lib/store-retry.nix: the login fails for as long as the store is
# sealed or down.
inherit (storeRetry) startLimitBurst startLimitIntervalSec;
serviceConfig = storeRetry.serviceConfig // {
Type = "oneshot";
UMask = "0077";
SyslogIdentifier = "squid-selfcheck-credential";
TimeoutStartSec = "5min";
};
script = ''
set -euo pipefail
${import ./lib/store-seeded-secret.nix { }}
${import ./lib/atomic-write-secret.nix { }}
${import ./lib/refresh-consumer.nix { }}
install -d -m 0700 /run/credstore
store_init
store_login
if [ "$STORE_UP" != 1 ]; then
echo "could not read the squid self-check credential; ${credentialFile} stays as it is." >&2
exit 1
fi
if ! value="$(store_bao kv get -field=value ${lib.escapeShellArg "secret/swarm/hives/${hiveController.hiveName}/squid-selfcheck"} 2>"$STORE_ERR")"; then
if grep -qi 'no value found' "$STORE_ERR"; then
echo "the swarm has not minted this hive's squid self-check credential yet; ${credentialFile} stays as it is."
exit 0
fi
cat "$STORE_ERR" >&2
exit 1
fi
if [[ ! "$value" =~ ^[A-Za-z0-9_-]+$ ]]; then
echo "the stored squid self-check credential is not one base64url word; ${credentialFile} stays as it is." >&2
exit 1
fi
# Written only when the value changed, so the file keeps its mtime.
if secret_differs ${credentialFile} "$value"; then
atomic_write_secret 0400 root:root ${credentialFile} "$value"
fi
'';
};
systemd.timers.squid-selfcheck-credential = storeRefreshTimer "re-read the squid self-check's password";
};
}

View file

@ -942,12 +942,13 @@ let
inherit hiveName;
}) hyperhiveCfg.swarm.hives;
# What a hive's gateway-leaf identity may do: issue from its own role, and
# read two of its own cache facts. The root unit on the hive host
# (`hive-priv-cache-facts`, ./hive-priv-cache.nix) reads them under this
# identity: the key hive-priv's downloads must be signed by, and the token it
# pulls with. The cache's URL and name come from the host's configuration,
# not from the store.
# What a hive's gateway-leaf identity may do: issue from its own role, read
# two of its own cache facts, and read its own squid self-check credential.
# The root unit on the hive host (`hive-priv-cache-facts`,
# ./hive-priv-cache.nix) reads the facts under this identity: the key
# hive-priv's downloads must be signed by, and the token it pulls with. The
# cache's URL and name come from the host's configuration, not from the
# store. `squid-selfcheck-credential` (./hive-squid.nix) reads the credential.
hiveLeafPolicyText =
obj:
''
@ -961,7 +962,8 @@ let
[
"public-key"
"pull-token"
];
]
+ readStanza "${credentialMountPath}/data/swarm/hives/${obj.hiveName}/squid-selfcheck";
# The PKI mount agent client certificates are issued from. Its root is
# generated inside the store, so the agent CA's key never exists outside it.