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:
parent
1a4dc16053
commit
626790a464
3 changed files with 68 additions and 21 deletions
|
|
@ -182,8 +182,10 @@ under it.
|
||||||
|
|
||||||
A host whose `deploy.bao.clientCaFile`, `deploy.bao.serverCaFile` or
|
A host whose `deploy.bao.clientCaFile`, `deploy.bao.serverCaFile` or
|
||||||
`deploy.swarm-controller.hiveClientCaFile` names one of those paths fails
|
`deploy.swarm-controller.hiveClientCaFile` names one of those paths fails
|
||||||
evaluation. Those options name the store's mTLS CA,
|
evaluation, however it's spelled with `//`, `.`, `..` or a trailing `/`. A
|
||||||
`/var/lib/swarm-bao-pki/ca.pem` on the host that runs the store.
|
symlink to one isn't resolved, so it gets past the check. Those options name
|
||||||
|
the store's mTLS CA, `/var/lib/swarm-bao-pki/ca.pem` on the host that runs
|
||||||
|
the store.
|
||||||
|
|
||||||
## What to hand a peer
|
## What to hand a peer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,29 @@ let
|
||||||
"ca-previous.pem"
|
"ca-previous.pem"
|
||||||
"ca-key.pem"
|
"ca-key.pem"
|
||||||
];
|
];
|
||||||
deleted = p: p == swarmCaDir || lib.hasPrefix "${swarmCaDir}/" p || lib.elem p hiveCaFiles;
|
# Lexical only: empty and `.` components drop out and `..` removes the
|
||||||
|
# one before it. A symlink is not resolved, so a path reaching a deleted
|
||||||
|
# file through one passes the check below.
|
||||||
|
normalise =
|
||||||
|
p:
|
||||||
|
lib.optionalString (lib.hasPrefix "/" p) "/"
|
||||||
|
+ lib.concatStringsSep "/" (
|
||||||
|
lib.foldl' (
|
||||||
|
kept: c:
|
||||||
|
if c == "" || c == "." then
|
||||||
|
kept
|
||||||
|
else if c == ".." then
|
||||||
|
(if kept == [ ] then [ ] else lib.init kept)
|
||||||
|
else
|
||||||
|
kept ++ [ c ]
|
||||||
|
) [ ] (lib.splitString "/" p)
|
||||||
|
);
|
||||||
|
deleted =
|
||||||
|
p:
|
||||||
|
let
|
||||||
|
n = normalise p;
|
||||||
|
in
|
||||||
|
n == swarmCaDir || lib.hasPrefix "${swarmCaDir}/" n || lib.elem n (map normalise hiveCaFiles);
|
||||||
# The hive CA's serial file and its adoption marker: neither is a CA, so
|
# The hive CA's serial file and its adoption marker: neither is a CA, so
|
||||||
# no CA-file option can name them, and nothing reads them.
|
# no CA-file option can name them, and nothing reads them.
|
||||||
hiveCaLeftovers = map (f: "${deployCfg.hive-controller.tls.stateDir}/${f}") [
|
hiveCaLeftovers = map (f: "${deployCfg.hive-controller.tls.stateDir}/${f}") [
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,22 @@ let
|
||||||
|
|
||||||
remainsAfterExit = u: u.serviceConfig.RemainAfterExit or false;
|
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 ))";
|
renewAt = hours: "renewat=$(( ${toString hours} * 3600 / 2 ))";
|
||||||
|
|
||||||
cases = [
|
cases = [
|
||||||
|
|
@ -402,14 +418,6 @@ let
|
||||||
name = "a CA-file option naming a deleted CA file fails evaluation, naming the store's mTLS CA";
|
name = "a CA-file option naming a deleted CA file fails evaluation, naming the store's mTLS CA";
|
||||||
ok =
|
ok =
|
||||||
let
|
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 {
|
oldRoot = hiveWithController {
|
||||||
deploy.singleHostSwarm = true;
|
deploy.singleHostSwarm = true;
|
||||||
deploy.bao.clientCaFile = "/var/lib/swarm-ca/root.pem";
|
deploy.bao.clientCaFile = "/var/lib/swarm-ca/root.pem";
|
||||||
|
|
@ -418,19 +426,34 @@ let
|
||||||
deploy.bao.serverCaFile = "/var/lib/hive-tls/ca.pem";
|
deploy.bao.serverCaFile = "/var/lib/hive-tls/ca.pem";
|
||||||
deploy.swarm-controller.hiveClientCaFile = "/var/lib/hive-tls/ca-previous.pem";
|
deploy.swarm-controller.hiveClientCaFile = "/var/lib/hive-tls/ca-previous.pem";
|
||||||
};
|
};
|
||||||
options = [
|
|
||||||
"bao.clientCaFile"
|
|
||||||
"bao.serverCaFile"
|
|
||||||
"swarm-controller.hiveClientCaFile"
|
|
||||||
];
|
|
||||||
in
|
in
|
||||||
refused oldRoot "bao.clientCaFile"
|
caFileRefused oldRoot "bao.clientCaFile"
|
||||||
&& refused oldRoot "swarm-controller.hiveClientCaFile"
|
&& caFileRefused oldRoot "swarm-controller.hiveClientCaFile"
|
||||||
&& refused hiveCa "bao.serverCaFile"
|
&& caFileRefused hiveCa "bao.serverCaFile"
|
||||||
&& refused hiveCa "swarm-controller.hiveClientCaFile"
|
&& caFileRefused hiveCa "swarm-controller.hiveClientCaFile"
|
||||||
&& !(lib.any (refused allLocal) options)
|
&& !(lib.any (caFileRefused allLocal) caFileOptions)
|
||||||
&& allLocal.services.hyperhive.deploy.bao.clientCaFile == "/var/lib/swarm-bao-pki/ca.pem";
|
&& 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
|
# The store host's hive presents the leaf ./glue-bao-tls.nix mints for
|
||||||
# it; not the reader's, not the services issuer's.
|
# it; not the reader's, not the services issuer's.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue