Watch
0
0
Fork
You've already forked hyperhive
0

hive CA: catch a deleted CA file however its path is spelled

The CA-file assertion compared the option's raw string, so
`/var/lib//swarm-ca/root.pem` or `/var/lib/hive-tls/./ca.pem` got past it.
Both sides are normalised lexically now: empty and `.` components drop out,
`..` removes the one before it, a trailing `/` goes. A symlink is not
resolved at eval time, so a path through one still passes; docs/swarm/ca.md
says so.
This commit is contained in:
atlas 2026-10-05 20:47:24 +02:00 • committed by mara
commit 626790a464
3 changed files with 68 additions and 21 deletions

View file

@ -79,6 +79,22 @@ let
remainsAfterExit = u: u.serviceConfig.RemainAfterExit or false;
# Whether `m` fails evaluation on `deploy.<option>` naming a deleted CA
# file, with the message pointing at the store's mTLS CA.
caFileRefused =
m: option:
lib.any (
a:
!a.assertion
&& lib.hasInfix "services.hyperhive.deploy.${option} is" a.message
&& lib.hasInfix "/var/lib/swarm-bao-pki/ca.pem" a.message
) m.assertions;
caFileOptions = [
"bao.clientCaFile"
"bao.serverCaFile"
"swarm-controller.hiveClientCaFile"
];
renewAt = hours: "renewat=$(( ${toString hours} * 3600 / 2 ))";
cases = [
@ -402,14 +418,6 @@ let
name = "a CA-file option naming a deleted CA file fails evaluation, naming the store's mTLS CA";
ok =
let
refused =
m: option:
lib.any (
a:
!a.assertion
&& lib.hasInfix "services.hyperhive.deploy.${option} is" a.message
&& lib.hasInfix "/var/lib/swarm-bao-pki/ca.pem" a.message
) m.assertions;
oldRoot = hiveWithController {
deploy.singleHostSwarm = true;
deploy.bao.clientCaFile = "/var/lib/swarm-ca/root.pem";
@ -418,19 +426,34 @@ let
deploy.bao.serverCaFile = "/var/lib/hive-tls/ca.pem";
deploy.swarm-controller.hiveClientCaFile = "/var/lib/hive-tls/ca-previous.pem";
};
options = [
"bao.clientCaFile"
"bao.serverCaFile"
"swarm-controller.hiveClientCaFile"
];
in
refused oldRoot "bao.clientCaFile"
&& refused oldRoot "swarm-controller.hiveClientCaFile"
&& refused hiveCa "bao.serverCaFile"
&& refused hiveCa "swarm-controller.hiveClientCaFile"
&& !(lib.any (refused allLocal) options)
caFileRefused oldRoot "bao.clientCaFile"
&& caFileRefused oldRoot "swarm-controller.hiveClientCaFile"
&& caFileRefused hiveCa "bao.serverCaFile"
&& caFileRefused hiveCa "swarm-controller.hiveClientCaFile"
&& !(lib.any (caFileRefused allLocal) caFileOptions)
&& allLocal.services.hyperhive.deploy.bao.clientCaFile == "/var/lib/swarm-bao-pki/ca.pem";
}
{
# The control is a host whose options normalise to paths beside the
# deleted ones: a sibling of the swarm root's directory, and the
# store's mTLS CA reached through `..`.
name = "a CA-file option spelling a deleted path with `//`, `.`, `..` or a trailing slash fails evaluation too";
ok =
let
spelled = hive {
deploy.bao.clientCaFile = "/var/lib//swarm-ca/";
deploy.bao.serverCaFile = "/var/lib/hive-tls/./ca.pem";
deploy.swarm-controller.hiveClientCaFile = "/var/lib/hive-tls/x/../ca-key.pem";
};
beside = hive {
deploy.bao.clientCaFile = "/var/lib//swarm-ca-copy/./root.pem";
deploy.bao.serverCaFile = "/var/lib/hive-tls/../swarm-bao-pki/ca.pem";
deploy.swarm-controller.hiveClientCaFile = "/var/lib/hive-tls/ca.pem/../trust-bundle.pem";
};
in
lib.all (caFileRefused spelled) caFileOptions && !(lib.any (caFileRefused beside) caFileOptions);
}
{
# The store host's hive presents the leaf ./glue-bao-tls.nix mints for
# it; not the reader's, not the services issuer's.