feat(3167): options + certificate name for the swarm UI
New swarm-ui module: enable (derived from swarm.controller.enable - the UI reads that daemon's state over its socket, so the host that runs the controller is the host that can serve the UI), domain (defaults to the swarm apex; an option so a hive can pin it like forge/matrix can), and package. Adds the name to swarm.serviceDomains, which is both the services sub-CA's nameConstraints set and the leaf's SAN set. The apex is a SIBLING of forge./chat./auth., not a parent, so nothing issues for it implicitly - left out, the vhost falls back to the hive leaf and the swarm's front page opens with a name mismatch. Asserts the UI domain differs from the hive domain: the gateway's default server already answers for the latter, and two vhosts claiming one server_name resolve to whichever nginx picks rather than erroring.
This commit is contained in:
parent
657dba762b
commit
f2840612c0
3 changed files with 101 additions and 1 deletions
|
|
@ -26,6 +26,7 @@
|
|||
./swarm-ca.nix
|
||||
./swarm-controller.nix
|
||||
./swarm-snapshot-store.nix
|
||||
./swarm-ui.nix
|
||||
./swarm-wireguard.nix
|
||||
./swarm.nix
|
||||
./swarm-peers-removed.nix
|
||||
|
|
|
|||
93
nix/host-modules/swarm-ui.nix
Normal file
93
nix/host-modules/swarm-ui.nix
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# The swarm-level web UI: a static bundle served by the gateway's nginx,
|
||||
# behind authelia. Distinct from the per-hive dashboard (hive-c0re's, on
|
||||
# the hive domain) — this one answers for the swarm apex and is the
|
||||
# operator's view across hives.
|
||||
#
|
||||
# Options only. The vhost itself is declared in hive-gateway/vhosts.nix
|
||||
# alongside forge/matrix/authelia: a service module says *how* it is
|
||||
# reached, the gateway says *whether this host serves it*.
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.hyperhive.swarm.ui;
|
||||
swarmCfg = config.services.hyperhive.swarm;
|
||||
hiveDomain = config.services.hyperhive.domain;
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.swarm.ui = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = swarmCfg.controller.enable;
|
||||
defaultText = lib.literalExpression "services.hyperhive.swarm.controller.enable";
|
||||
example = true;
|
||||
description = ''
|
||||
Serve the swarm UI from this host.
|
||||
|
||||
Derived from `swarm.controller.enable` rather than from
|
||||
`enableRequiredServices`: the UI is a view onto the controller's
|
||||
state and reaches it over that daemon's unix socket, so the host
|
||||
that runs the controller is the host that can serve the UI. A
|
||||
hive that merely *uses* a swarm has nothing to serve here.
|
||||
'';
|
||||
};
|
||||
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = if swarmCfg.domain == null then "swarm.invalid" else swarmCfg.domain;
|
||||
defaultText = lib.literalExpression "services.hyperhive.swarm.domain";
|
||||
example = "swarm.example.com";
|
||||
description = ''
|
||||
Host name the swarm UI answers on. Defaults to the swarm apex
|
||||
itself — the swarm's front page is the swarm's name.
|
||||
|
||||
An option rather than a hardcoded derivation so a hive can pin a
|
||||
different name, the same way `swarm.forge.domain` and
|
||||
`swarm.matrix.gatewayHost` can.
|
||||
|
||||
Total on a null swarm domain (`.invalid`, RFC 2606) so the
|
||||
required-domain assertion is what fires rather than a coercion
|
||||
error naming this option — same reasoning as
|
||||
`hive-network.nix`'s.
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.swarm-ui;
|
||||
defaultText = lib.literalExpression "pkgs.swarm-ui";
|
||||
description = ''
|
||||
Static build of the swarm UI. nginx serves this store path
|
||||
directly — there is no server-side component beyond the
|
||||
controller's own API.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.hyperhive.enable && cfg.enable) {
|
||||
assertions = [
|
||||
{
|
||||
# The `_` default server already answers for the hive domain
|
||||
# (dashboard, per-agent routes). A second vhost claiming the same
|
||||
# server_name is not an error to nginx — it picks one and logs a
|
||||
# conflict — so the failure would surface as "the dashboard is
|
||||
# sometimes the swarm UI", which is far harder to read than an
|
||||
# eval failure naming both options.
|
||||
assertion = cfg.domain != hiveDomain;
|
||||
message = ''
|
||||
services.hyperhive.swarm.ui.domain (${cfg.domain}) must differ
|
||||
from services.hyperhive.domain (${hiveDomain}) — the hive
|
||||
domain is already served by the gateway's default vhost
|
||||
(dashboard + agent routes), and two vhosts claiming one
|
||||
server_name silently resolve to whichever nginx picks.
|
||||
|
||||
Set services.hyperhive.swarm.domain to a name distinct from
|
||||
this hive's, or pin swarm.ui.domain explicitly.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -39,7 +39,13 @@ let
|
|||
swarmCfg.forge.domain
|
||||
swarmCfg.matrix.gatewayHost
|
||||
swarmCfg.authelia.domain
|
||||
];
|
||||
]
|
||||
# The swarm UI's name is a SIBLING of the other three, not a parent of
|
||||
# them — the apex is as much a name needing a certificate as
|
||||
# `forge.<apex>` is, and no CA in the hierarchy issues for it
|
||||
# implicitly. Left out, its vhost falls back to the hive leaf and the
|
||||
# swarm's front page opens with a name mismatch.
|
||||
++ lib.optional swarmCfg.ui.enable swarmCfg.ui.domain;
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.swarm.hives = lib.mkOption {
|
||||
|
|
|
|||
Loading…
Reference in a new issue