hive-tls: the root fetch presents the hive identity, as on main
On a hive fronting no swarm name, swarm-services-cert's token-less
GET pki/ca/pem presents baoHiveIdentity{Cert,Key}File again. The fetch
never logs in and grants nothing; the client certificate only completes
the store listener's mTLS. A hive host without the store therefore needs
no copy of the services-issuer pair.
The issue path, which logs in to pki/issue/swarm-services, still presents
only deploy.bao.servicesIssuerClient{Cert,Key}File.
module-eval-hive-tls: the root-fetch cases pin the hive identity and the
absence of the services-issuer pair (a fixture holding both, and one
holding only the hive identity); the issue-path case pins the pair on a
hive host and on a host without a hive; a gateway without the pair
evaluates and its issue path names both options.
Root-fetch identity: option (a), mara on PR #5002 (comment 92591).
Refs #4921
This commit is contained in:
parent
6a9c819136
commit
a66670be87
4 changed files with 143 additions and 103 deletions
|
|
@ -54,6 +54,17 @@ let
|
|||
};
|
||||
noSvcFetch = noService.systemd.services.swarm-services-cert.script;
|
||||
|
||||
# The same hive holding its own identity and no services-issuer pair: the
|
||||
# shape of every hive host without the store that fronts no swarm name.
|
||||
noServiceNoPair = hiveWithController {
|
||||
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";
|
||||
deploy.hive-controller.tls.baoHiveIdentityCertFile = "/etc/pki/bao-hive.pem";
|
||||
deploy.hive-controller.tls.baoHiveIdentityKeyFile = "/etc/pki/bao-hive-key.pem";
|
||||
};
|
||||
noPairFetch = noServiceNoPair.systemd.services.swarm-services-cert.script;
|
||||
|
||||
# The same hive with no store identity at all.
|
||||
noIdentity = hiveWithController { };
|
||||
noIdFetch = noIdentity.systemd.services.swarm-services-cert.script;
|
||||
|
|
@ -83,6 +94,15 @@ let
|
|||
lib.splitString "\nfi\n\n" (lib.last (lib.splitString ''if [ "$want_svc" = 0 ]; then'' script))
|
||||
);
|
||||
|
||||
# Everything after that branch: the path that logs in and issues the leaf.
|
||||
issuePath =
|
||||
script:
|
||||
lib.concatStringsSep "\nfi\n\n" (
|
||||
lib.tail (
|
||||
lib.splitString "\nfi\n\n" (lib.last (lib.splitString ''if [ "$want_svc" = 0 ]; then'' script))
|
||||
)
|
||||
);
|
||||
|
||||
units = allLocal.systemd.services;
|
||||
boot = units.swarm-services-cert;
|
||||
caUnit = units.hive-tls-ca;
|
||||
|
|
@ -210,55 +230,59 @@ let
|
|||
&& lib.hasInfix ''cat "$svcroot" > "$bundle"'' noService.systemd.services.hive-tls-ca.script;
|
||||
}
|
||||
{
|
||||
# The services-issuer pair, not the reader identity, whose policy reads
|
||||
# every secret the hive is entitled to, and not the hive's own.
|
||||
name = "the root fetch reaches the store with the services-issuer pair and server CA";
|
||||
# The hive's own identity, not its reader identity, whose policy reads
|
||||
# every secret the hive is entitled to, and not the services-issuer
|
||||
# pair, which `noService` also holds. A hive holding no pair fetches
|
||||
# the same way.
|
||||
name = "the root fetch reaches the store with the hive's own identity and server CA, never the services-issuer pair";
|
||||
ok =
|
||||
let
|
||||
hiveTls =
|
||||
"--cert ${lib.escapeShellArg "/etc/pki/bao-hive.pem"}"
|
||||
+ " --key ${lib.escapeShellArg "/etc/pki/bao-hive-key.pem"}";
|
||||
in
|
||||
lib.hasInfix "--cacert ${lib.escapeShellArg "/etc/pki/bao-ca.pem"}" noSvcFetch
|
||||
&& lib.hasInfix (
|
||||
"--cert ${lib.escapeShellArg "/etc/pki/bao-services-issuer.pem"}"
|
||||
+ " --key ${lib.escapeShellArg "/etc/pki/bao-services-issuer-key.pem"}"
|
||||
) noSvcFetch
|
||||
&& lib.hasInfix hiveTls (noSvcBranch noSvcFetch)
|
||||
&& !(lib.hasInfix "/etc/pki/bao-client.pem" (noSvcBranch noSvcFetch))
|
||||
&& !(lib.hasInfix "/etc/pki/bao-hive" (noSvcBranch noSvcFetch));
|
||||
&& !(lib.hasInfix "/etc/pki/bao-services-issuer" (noSvcBranch noSvcFetch))
|
||||
&& noServiceNoPair.services.hyperhive.deploy.bao.servicesIssuerClientCertFile == null
|
||||
&& lib.hasInfix hiveTls (noSvcBranch noPairFetch)
|
||||
&& lib.hasInfix "/v1/pki/ca/pem" (noSvcBranch noPairFetch)
|
||||
&& !(lib.hasInfix "servicesIssuerClient" (noSvcBranch noPairFetch));
|
||||
}
|
||||
{
|
||||
# No identity to present: the unit fails, naming both options, rather
|
||||
# than leaving the bundle without the root behind a green unit. The
|
||||
# control is the same hive with the pair, whose branch fetches.
|
||||
name = "a host fronting no swarm name with no services-issuer pair fails the unit, naming both options";
|
||||
# control is the same hive with an identity, whose branch fetches.
|
||||
name = "a hive fronting no swarm name with no store identity fails the unit, naming the hive identity's options";
|
||||
ok =
|
||||
noIdentity.services.hyperhive.swarm.localServiceDomains == [ ]
|
||||
&& noIdentity.services.hyperhive.deploy.bao.servicesIssuerClientCertFile == null
|
||||
&& noIdentity.services.hyperhive.deploy.hive-controller.tls.baoHiveIdentityCertFile == null
|
||||
&& !(lib.hasInfix "/v1/pki/ca/pem" noIdFetch)
|
||||
&& lib.hasInfix "servicesIssuerClientCertFile (unset)" (noSvcBranch noIdFetch)
|
||||
&& lib.hasInfix "servicesIssuerClientKeyFile (unset)" (noSvcBranch noIdFetch)
|
||||
&& lib.hasInfix "baoHiveIdentityCertFile (unset)" (noSvcBranch noIdFetch)
|
||||
&& lib.hasInfix "baoHiveIdentityKeyFile (unset)" (noSvcBranch noIdFetch)
|
||||
&& !(lib.hasInfix "servicesIssuerClient" (noSvcBranch noIdFetch))
|
||||
&& lib.hasSuffix "exit 1" (lib.removeSuffix "\n" (noSvcBranch noIdFetch))
|
||||
&& !(lib.hasInfix "exit 0" (noSvcBranch noIdFetch))
|
||||
&& !(lib.hasInfix "not staged" noIdFetch)
|
||||
&& lib.hasInfix "/v1/pki/ca/pem" (noSvcBranch noSvcFetch);
|
||||
}
|
||||
{
|
||||
# A pair named but not on disk fails before curl, naming the paths.
|
||||
name = "a host fronting no swarm name whose services-issuer files are absent fails the unit, naming the paths";
|
||||
# An identity named but not on disk fails before curl, naming the paths.
|
||||
name = "a hive fronting no swarm name whose identity files are absent fails the unit, naming the paths";
|
||||
ok =
|
||||
lib.hasInfix "for f in /etc/pki/bao-services-issuer.pem /etc/pki/bao-services-issuer-key.pem; do" (
|
||||
noSvcBranch noSvcFetch
|
||||
)
|
||||
lib.hasInfix "for f in /etc/pki/bao-hive.pem /etc/pki/bao-hive-key.pem; do" (noSvcBranch noSvcFetch)
|
||||
&& lib.hasInfix ''[ -r "$f" ] || missing="$missing $f"'' (noSvcBranch noSvcFetch)
|
||||
&& lib.hasInfix "cannot read$missing" (noSvcBranch noSvcFetch)
|
||||
&& lib.hasInfix "servicesIssuerClientCertFile (/etc/pki/bao-services-issuer.pem)" (
|
||||
noSvcBranch noSvcFetch
|
||||
)
|
||||
&& lib.hasInfix "servicesIssuerClientKeyFile (/etc/pki/bao-services-issuer-key.pem)" (
|
||||
noSvcBranch noSvcFetch
|
||||
);
|
||||
&& lib.hasInfix "baoHiveIdentityCertFile (/etc/pki/bao-hive.pem)" (noSvcBranch noSvcFetch)
|
||||
&& lib.hasInfix "baoHiveIdentityKeyFile (/etc/pki/bao-hive-key.pem)" (noSvcBranch noSvcFetch);
|
||||
}
|
||||
{
|
||||
# A gateway host with no hive, fronting no swarm name, the pair placed
|
||||
# by hand. The control is the hive host, whose gateway-leaf unit does
|
||||
# present the hive identity, so the scan can find one.
|
||||
name = "on a host without a hive the swarm-services units present no hive identity, and the root fetch runs";
|
||||
# Each path presents one identity: the issue path the services-issuer
|
||||
# pair, on a hive host and on a host without a hive alike, and the root
|
||||
# fetch the hive's own. The control for the scan is `hive-tls-resign`,
|
||||
# which does present the hive identity.
|
||||
name = "the issue path presents only the services-issuer pair, and the root fetch only the hive identity";
|
||||
ok =
|
||||
let
|
||||
hiveRefs = [
|
||||
|
|
@ -266,33 +290,40 @@ let
|
|||
"hive-leaf"
|
||||
"/etc/pki/bao-hive"
|
||||
];
|
||||
refsHive =
|
||||
units:
|
||||
lib.any (
|
||||
u: lib.any (r: lib.hasInfix r u.script || lib.hasInfix r (builtins.toJSON u.environment)) hiveRefs
|
||||
) units;
|
||||
units = gatewayNoHive.systemd.services;
|
||||
s = units.swarm-services-cert;
|
||||
refsHive = s: lib.any (r: lib.hasInfix r s) hiveRefs;
|
||||
issuesWithPair =
|
||||
m:
|
||||
let
|
||||
u = m.systemd.services;
|
||||
path = issuePath u.swarm-services-cert.script;
|
||||
in
|
||||
lib.hasInfix "for f in /etc/pki/bao-services-issuer.pem /etc/pki/bao-services-issuer-key.pem; do" path
|
||||
&& !(refsHive path)
|
||||
&&
|
||||
lib.all
|
||||
(
|
||||
e:
|
||||
e.BAO_CLIENT_CERT == "/etc/pki/bao-services-issuer.pem"
|
||||
&& e.BAO_CLIENT_KEY == "/etc/pki/bao-services-issuer-key.pem"
|
||||
)
|
||||
[
|
||||
u.swarm-services-cert.environment
|
||||
u.swarm-services-cert-renew.environment
|
||||
];
|
||||
in
|
||||
!gatewayNoHive.services.hyperhive.deploy.hive-controller.enable
|
||||
&& gatewayNoHive.services.hyperhive.swarm.localServiceDomains == [ ]
|
||||
&& !(refsHive [
|
||||
s
|
||||
units.swarm-services-cert-renew
|
||||
])
|
||||
&& lib.hasInfix "/v1/pki/ca/pem" (noSvcBranch s.script)
|
||||
&& lib.hasInfix "--cert ${lib.escapeShellArg "/etc/pki/bao-services-issuer.pem"}" (
|
||||
noSvcBranch s.script
|
||||
)
|
||||
&& s.environment.BAO_CLIENT_CERT == "/etc/pki/bao-services-issuer.pem"
|
||||
&& refsHive [ noService.systemd.services.hive-tls-resign ];
|
||||
issuesWithPair noService
|
||||
&& issuesWithPair gatewayNoHive
|
||||
&& !gatewayNoHive.services.hyperhive.deploy.hive-controller.enable
|
||||
&& refsHive (noSvcBranch noSvcFetch)
|
||||
&& !(lib.hasInfix "/etc/pki/bao-services-issuer" (noSvcBranch noSvcFetch))
|
||||
&& refsHive noService.systemd.services.hive-tls-resign.script;
|
||||
}
|
||||
{
|
||||
# A host with no store and no pair placed is a supported shape: it
|
||||
# evaluates, and its unit goes red. The control is the same host with
|
||||
# the pair placed, whose branch fetches, and the store's host, where
|
||||
# the glue supplies the pair.
|
||||
name = "a self-signed gateway with no services-issuer pair evaluates, and its unit fails naming both options";
|
||||
# evaluates, and its issue path goes red. A hive among them still
|
||||
# fetches the root. The controls are the same host without a hive with
|
||||
# the pair placed, and the store's host, where the glue supplies it.
|
||||
name = "a self-signed gateway with no services-issuer pair evaluates, and its issue path fails naming both options";
|
||||
ok =
|
||||
let
|
||||
refused =
|
||||
|
|
@ -305,24 +336,21 @@ let
|
|||
|| lib.hasInfix "servicesIssuerClientKeyFile" a.message
|
||||
)
|
||||
) m.assertions;
|
||||
redUnit =
|
||||
redIssue =
|
||||
m:
|
||||
let
|
||||
branch = noSvcBranch m.systemd.services.swarm-services-cert.script;
|
||||
in
|
||||
lib.hasInfix "services.hyperhive.deploy.bao.servicesIssuerClientCertFile (unset)" branch
|
||||
&& lib.hasInfix "services.hyperhive.deploy.bao.servicesIssuerClientKeyFile (unset)" branch
|
||||
&& lib.hasSuffix "exit 1" (lib.removeSuffix "\n" branch)
|
||||
&& !(lib.hasInfix "/v1/pki/ca/pem" branch);
|
||||
lib.hasInfix (
|
||||
"set services.hyperhive.deploy.bao.servicesIssuerClientCertFile (unset)"
|
||||
+ " and services.hyperhive.deploy.bao.servicesIssuerClientKeyFile (unset)"
|
||||
) (issuePath m.systemd.services.swarm-services-cert.script);
|
||||
in
|
||||
!(refused gatewayNoHiveNoPair)
|
||||
&& !(refused noIdentity)
|
||||
&& redUnit gatewayNoHiveNoPair
|
||||
&& redUnit noIdentity
|
||||
&& !(redUnit gatewayNoHive)
|
||||
&& lib.hasInfix "/v1/pki/ca/pem" (
|
||||
noSvcBranch gatewayNoHive.systemd.services.swarm-services-cert.script
|
||||
)
|
||||
&& !(refused noServiceNoPair)
|
||||
&& redIssue gatewayNoHiveNoPair
|
||||
&& redIssue noIdentity
|
||||
&& redIssue noServiceNoPair
|
||||
&& !(redIssue gatewayNoHive)
|
||||
&& lib.hasInfix "/v1/pki/ca/pem" (noSvcBranch noPairFetch)
|
||||
&&
|
||||
allLocal.services.hyperhive.deploy.bao.servicesIssuerClientCertFile
|
||||
== "/var/lib/swarm-bao-pki/services-issuer.pem";
|
||||
|
|
|
|||
Loading…
Reference in a new issue