feat(nix): assert swarm service names live under swarm.domain
Those names are certified by the swarm-services sub-CA, which is name-constrained to the swarm's own tree. A service hostname outside it has no issuer in this deployment -- not a misconfiguration that degrades, one that cannot produce a matching certificate at all. Asserting it turns "your certs mysteriously don't work" into an eval failure that names the option to edit. It applies to every deployment, not only the autoconfigured one: an operator bringing their own certificates still needs to know which names they are bringing them for. The message names the option rather than only the value, which is why the hostnames are carried as (option, value) pairs and flattened into serviceDomains afterwards -- flattening drops exactly the provenance an error message needs. Guarded on a null swarm.domain so hive-network.nix's required-domain assertion stays the one that fires for that case.
This commit is contained in:
parent
11b8140981
commit
93b89fd566
1 changed files with 58 additions and 7 deletions
|
|
@ -23,6 +23,25 @@
|
|||
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;
|
||||
}
|
||||
];
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.swarm.hives = lib.mkOption {
|
||||
|
|
@ -175,16 +194,48 @@ 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 != "") [
|
||||
swarmCfg.forge.domain
|
||||
swarmCfg.matrix.gatewayHost
|
||||
swarmCfg.authelia.domain
|
||||
]
|
||||
)
|
||||
lib.unique (lib.filter (d: d != null && d != "") (lib.map (s: s.value) namedServiceDomains))
|
||||
);
|
||||
|
||||
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