fix(nix): drop the swarm-service-domain assertion, it asserted intent
Measured against a real config, not reasoned about: pinning `swarm.forge.domain = "forge.<hive domain>"` while `swarm.domain` is a different apex -- the documented way an existing deployment keeps its names while joining a swarm -- FAILED eval under this assertion. And the reason it gave was false. It said those names are certified by the services sub-CA, "which is constrained to the swarm's own tree, so a name outside it has no issuer here". The sub-CA is constrained to the **configured names** (swarm-ca.nix builds its nameConstraints from the derived list), and the swarm root carries no name constraints at all -- only basicConstraints and keyUsage. So any configured name is issuable, and a legacy name under the hive domain is additionally signable by the hive CA. The assertion therefore encoded the shape I had in mind rather than a constraint the code implements, and it rejected a configuration that works. Removed rather than weakened: a narrower version would still carry a claim nothing backs. `serviceDomains` stays -- it feeds the sub-CA's name constraints, the leaf's SANs and the per-vhost cert choice, and those three agreeing is the thing that mattered. The (option, value) pairs collapse back to a plain list, since the provenance existed only for the assertion message.
This commit is contained in:
parent
93b89fd566
commit
30551464c1
1 changed files with 16 additions and 56 deletions
|
|
@ -24,23 +24,21 @@ let
|
|||
cfg = config.services.hyperhive;
|
||||
swarmCfg = cfg.swarm;
|
||||
|
||||
# The service hostnames with the option that set each one, so an
|
||||
# assertion can name the thing to edit rather than the value to hunt
|
||||
# for. `serviceDomains` below is the same set flattened; this keeps
|
||||
# the provenance that flattening drops.
|
||||
namedServiceDomains = [
|
||||
{
|
||||
option = "services.hyperhive.swarm.forge.domain";
|
||||
value = swarmCfg.forge.domain;
|
||||
}
|
||||
{
|
||||
option = "services.hyperhive.swarm.matrix.gatewayHost";
|
||||
value = swarmCfg.matrix.gatewayHost;
|
||||
}
|
||||
{
|
||||
option = "services.hyperhive.swarm.authelia.domain";
|
||||
value = swarmCfg.authelia.domain;
|
||||
}
|
||||
# Public hostnames of the swarm's own services, in declaration order.
|
||||
# `serviceDomains` below is this set sorted + deduplicated.
|
||||
#
|
||||
# ⚠️ These are NOT required to be under `swarm.domain`. An earlier
|
||||
# revision asserted that, reasoning that the services sub-CA is
|
||||
# constrained to the swarm's tree — but the sub-CA is constrained to
|
||||
# the **configured names** (./swarm-ca.nix) and the swarm root carries
|
||||
# no name constraints at all, so any configured name is issuable. The
|
||||
# assertion encoded an intended shape, not a property of the code, and
|
||||
# it rejected the supported migration path: a hive pinning its old
|
||||
# `forge.<hive domain>` while joining a swarm at a different apex.
|
||||
serviceDomains' = [
|
||||
swarmCfg.forge.domain
|
||||
swarmCfg.matrix.gatewayHost
|
||||
swarmCfg.authelia.domain
|
||||
];
|
||||
in
|
||||
{
|
||||
|
|
@ -194,48 +192,10 @@ in
|
|||
services.hyperhive.swarm.peerHives = lib.filterAttrs (name: _: name != cfg.hiveName) swarmCfg.hives;
|
||||
|
||||
services.hyperhive.swarm.serviceDomains = lib.sort (a: b: a < b) (
|
||||
lib.unique (lib.filter (d: d != null && d != "") (lib.map (s: s.value) namedServiceDomains))
|
||||
lib.unique (lib.filter (d: d != null && d != "") serviceDomains')
|
||||
);
|
||||
|
||||
assertions = [
|
||||
{
|
||||
# Every swarm-service name must live under `swarm.domain`.
|
||||
#
|
||||
# This is not style. Those names are certified by the
|
||||
# swarm-services sub-CA (./swarm-ca.nix), which is
|
||||
# name-constrained; a name outside the swarm's tree has no
|
||||
# issuer in this deployment at all. Asserting it here turns
|
||||
# "your certs mysteriously don't work" into an eval failure that
|
||||
# names the option — and it applies to every deployment, not
|
||||
# just the all-local one, because the operator bringing their
|
||||
# own certs needs to know which names they are bringing them
|
||||
# for.
|
||||
#
|
||||
# Guarded on a null `swarm.domain` so the required-domain
|
||||
# assertion in ./hive-network.nix stays the one that fires for
|
||||
# that case; two assertions naming the same missing value is
|
||||
# noise.
|
||||
assertion =
|
||||
swarmCfg.domain == null
|
||||
|| lib.all (s: s.value == null || lib.hasSuffix ".${swarmCfg.domain}" s.value) namedServiceDomains;
|
||||
message =
|
||||
let
|
||||
bad = lib.filter (
|
||||
s: s.value != null && !lib.hasSuffix ".${swarmCfg.domain}" s.value
|
||||
) namedServiceDomains;
|
||||
in
|
||||
''
|
||||
Swarm service hostnames must be sub-domains of
|
||||
services.hyperhive.swarm.domain ("${toString swarmCfg.domain}"):
|
||||
|
||||
${lib.concatMapStringsSep "\n" (s: " ${s.option} = \"${s.value}\";") bad}
|
||||
|
||||
These names are issued by the swarm-services sub-CA, which is
|
||||
constrained to the swarm's own tree — a name outside it has no
|
||||
issuer here, and no certificate this deployment can produce
|
||||
will match it.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# Guarded on `hiveName != null` so the required-hiveName
|
||||
# assertion in ./hyperhive.nix is what fires for that case —
|
||||
|
|
|
|||
Loading…
Reference in a new issue