hive-c0re: hand the daemon the store identity it cannot open itself

`credential.rs` calls `SecretStore::from_env`, and nothing set `BAO_*` for
this unit — only `swarm-bao-matrix-token` had them. Fixing that alone would
not have been enough: hive-c0re runs as hive-core, and glue-bao-tls mints the
client key `0600` inside a `0700` directory, so the daemon cannot read the
file even when it is named.

Both halves go through LoadCredential, which this unit already uses for the
swarm-status client secret: root reads the identity at unit start, hive-core
sees it under `%d`, and no second on-disk copy exists.

The gate is the identity, never `deploy.bao.enable` — a hive that reads a
store on another machine holds a certificate and runs no store. Four
module-eval cases: the co-located and off-host presence arms, the absence arm
for a hive with no identity, and a presence/absence pair for the optional CA.
This commit is contained in:
atlas 2026-09-02 23:51:00 +02:00 committed by mara
commit ad439843f1
3 changed files with 103 additions and 1 deletions

View file

@ -483,6 +483,58 @@ let
in
s ? swarm-bao-matrix-token && s.swarm-bao-matrix-token.requires == [ "swarm-bao-pki.service" ];
}
{
# hive-c0re runs as hive-core and the client key is `0600` root-owned
# inside a `0700` directory, so the identity reaches the daemon as a
# systemd credential and the environment names `%d` rather than the
# file. Both halves are asserted together because either alone is a
# daemon that fails at the TLS handshake, naming neither.
name = "a reader hands hive-c0re a store identity the daemon cannot open itself";
ok =
let
s = baoRemoteReader.systemd.services;
in
s ? hive-c0re
&& (s.hive-c0re.environment.BAO_CLIENT_CERT or null) == "%d/bao-client.pem"
&& (s.hive-c0re.environment.BAO_CLIENT_KEY or null) == "%d/bao-client-key.pem"
&& builtins.elem "bao-client.pem:/etc/pki/bao-client.pem" s.hive-c0re.serviceConfig.LoadCredential
&& builtins.elem "bao-client-key.pem:/etc/pki/bao-client-key.pem" s.hive-c0re.serviceConfig.LoadCredential;
}
{
# Absence arm for the case above. A hive with no client identity gets no
# store environment at all — the daemon reports a queue it cannot serve
# rather than a handshake it cannot explain.
name = "a hive with no client identity gives hive-c0re no store environment";
ok =
let
s = matrixNoBaoIdentity.systemd.services;
in
s ? hive-c0re
&& !(s.hive-c0re.environment ? BAO_ADDR)
&& !(lib.any (c: lib.hasPrefix "bao-" c) s.hive-c0re.serviceConfig.LoadCredential);
}
{
# The CA is its own arm: absent means the system trust store, which is
# right for a deployment with a real CA and wrong for a self-signed one.
name = "a reader that names no store CA falls through to the system trust store";
ok =
let
s = baoRemoteReader.systemd.services;
in
s ? hive-c0re && !(s.hive-c0re.environment ? BAO_CACERT);
}
{
# Presence control for the arm above: the CA is conditional, not gone.
# Co-located, ./host-modules/glue-bao-tls.nix mints one and names it.
name = "a reader beside a self-signed store is given that store's CA";
ok =
let
s = baoWithMatrix.systemd.services;
in
s ? hive-c0re
&& (s.hive-c0re.environment.BAO_CACERT or null) == "%d/bao-ca.pem"
&& lib.any (c: lib.hasPrefix "bao-ca.pem:" c) s.hive-c0re.serviceConfig.LoadCredential;
}
{
# The name a reader dials has to resolve where the store runs; a
# multi-host swarm resolves it upstream instead.