Watch
0
0
Fork
You've already forked hyperhive
0

hive-tls: stage bao pki root in every hive's trust bundle

A self-signed hive fronting no swarm service name never filled
`$svcroot` (/var/lib/hive-tls/swarm-services-root.pem): the only writer
was `swarm-services-cert`'s issuance, which exits early on
`want_svc=0`. So those hives' trust-bundle.pem lacked the store's `pki`
root, even though every other hive's swarm-service names chain to it.

On `want_svc=0`, `swarm-services-cert` now reads the root from the
store's unauthenticated `GET /v1/pki/ca/pem`, over the hive's existing
BAO_ADDR, `deploy.bao.serverCaFile` and reader identity
(`deploy.bao.clientCertFile`/`clientKeyFile`) at the TLS layer only; no
login. It writes `$svcroot` and restarts `hive-tls-ca` when the root
changed, as the issuing path already does. The daily
`swarm-services-cert-renew` timer runs the same script, so the copy
follows a root rotation.

The gateway no longer `Requires=` `swarm-services-cert` on such a host:
nginx reads nothing that unit writes there, and the fetch retries
against a sealed store.

`hivectl peer-config` prints the root as step 2 of the self-signed
instructions: the swarm's service names are issued under it, not the
swarm root.

No new option. Refs #4588.
This commit is contained in:
atlas 2026-09-30 21:23:18 +02:00 • committed by mara
commit 72f5e39278
5 changed files with 171 additions and 10 deletions

View file

@ -2,7 +2,8 @@
# this suite exists, naming convention, "evaluates not executes").
#
# The swarm-services leaf's renewal: a timer that really re-runs the
# issuance, at a threshold read off the store role's lifetime.
# issuance, at a threshold read off the store role's lifetime. And the
# store's root on a hive that asks it for no leaf.
{
pkgs,
lib,
@ -36,6 +37,15 @@ let
deploy.bao.servicesPkiLeafTtlHours = 48;
};
# Fronting none of the swarm's names, reading a store it does not run,
# with the reader identity placed by hand.
noService = hive {
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
deploy.bao.serverCaFile = "/etc/pki/bao-ca.pem";
};
noSvcFetch = noService.systemd.services.swarm-services-cert.script;
units = allLocal.systemd.services;
boot = units.swarm-services-cert;
timer = allLocal.systemd.timers.swarm-services-cert-renew;
@ -117,6 +127,40 @@ let
&& lib.hasInfix ''-in "$svcroot" -noout -checkend ${toString (48 * 3600)} '' shortBoot
&& lib.hasInfix "-checkend ${toString (48 * 3600)} <<<\"$root_ca\"" shortStore;
}
{
# The slot the bundle reads the store's root from, filled on a hive
# that asks the store for no leaf.
name = "a hive fronting no swarm name puts the store's pki root in its trust bundle";
ok =
noService.services.hyperhive.swarm.localServiceDomains == [ ]
&& lib.hasInfix ''"$BAO_ADDR/v1/pki/ca/pem"'' noSvcFetch
&& lib.hasInfix ''mv -f "$svcroot.new" "$svcroot"'' noSvcFetch
&& lib.hasInfix ''svcroot="$d/swarm-services-root.pem"'' noService.systemd.services.hive-tls-ca.script
&& lib.hasInfix ''if [ -s "$svcroot" ]; then anchors+=("$svcroot"); fi'' noService.systemd.services.hive-tls-ca.script;
}
{
name = "the root fetch reaches the store with the hive's own reader identity and server CA";
ok =
lib.hasInfix "--cacert ${lib.escapeShellArg "/etc/pki/bao-ca.pem"}" noSvcFetch
&& lib.hasInfix (
"--cert ${lib.escapeShellArg "/etc/pki/bao-client.pem"}"
+ " --key ${lib.escapeShellArg "/etc/pki/bao-client-key.pem"}"
) noSvcFetch;
}
{
# The fetch retries against a sealed store; a gateway `Requires=` on it
# would go down with each attempt. The control is a host fronting
# swarm names, whose gateway does wait for its leaf.
name = "a hive fronting no swarm name does not hold its gateway on the store";
ok =
let
holds =
m:
lib.elem "hive-gateway-self-signed-cert.service" m.systemd.services.swarm-services-cert.requiredBy
|| lib.elem "hive-gateway-self-signed-cert.service" m.systemd.services.swarm-services-cert.before;
in
!(holds noService) && holds allLocal;
}
];
in
runGroup "hive-tls" cases