Watch
0
0
Fork
You've already forked hyperhive
0

swarm options: no swarm.* value depends on a deploy option or this host's hive

- swarm.serviceDomains lists every swarm service name on every host. The
  store's issuing role is built from it, so the store now admits a leaf
  for grafana, metrics, otel, logs, the bao UI and the apex whichever host
  runs the store. Each gateway's own leaf stays narrowed by
  localServiceDomains.
- swarm.matrix.apiUrl defaults to http://<swarm.matrix.gatewayHost>
  (null when gatewayHost is null) instead of loopback on the matrix host.
- The authelia vhost proxies /bridge/ to swarm-authelia-bridge with the
  prefix stripped, and deploy.swarm-controller.authBridgeUrl defaults to
  ${swarm.authelia.url}/bridge on every host. swarm.authelia.bridgeUrl is
  removed.
- swarm-authelia-bridge admits only swarm-controller's OIDC client on
  every request (403 for any other active token), configured by
  SWARM_AUTHELIA_BRIDGE_CONTROLLER_CLIENT_ID (renamed from
  ..._OPERATOR_READER_CLIENT_ID, which gated ListOperators alone).
- swarm.peerHives is removed; swarm-wireguard.nix filters this host's own
  hive out of swarm.hives itself.

Closes #4933
This commit is contained in:
atlas 2026-10-04 17:26:13 +02:00
commit bed4358e19
21 changed files with 226 additions and 283 deletions

View file

@ -1,6 +1,6 @@
# The swarm's directory: one entry per hive, **including this one**,
# identical on every host in the swarm. `services.hyperhive.hiveName`
# says which entry is us, and `peerHives` below derives the rest.
# says which entry is us.
#
# Why a directory rather than a per-host peer list: every field here is
# intrinsic to the hive it describes — none of them says anything about
@ -78,41 +78,31 @@ let
# 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.
#
# Every name is listed whether or not this host runs the service: the
# store's issuing role is built from this list on the store's host and
# has to admit a leaf for any gateway in the swarm. What one gateway's
# leaf carries is `localServiceDomains`.
#
# Membership is also what `gateway.lib.tlsFor` consults to pick the
# services leaf over the hive one, so a gateway-published swarm name
# missing here is served the hive certificate, which cannot cover a name
# under a different apex.
serviceDomains' = [
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 (deployCfg.swarm-ui.enable && swarmCfg.domain != null) swarmCfg.domain
# Every swarm service that claims a gateway name belongs here, and
# these three were missing it. Membership is what `gateway.lib.tlsFor`
# consults to pick the services leaf over the hive one, so a name
# absent from this list is served the HIVE certificate — which cannot
# cover a name under a different apex however the sub-CA is set up.
#
# ⚠️ How that stayed invisible: a mismatch a browser shows as a
# click-through warning is one a machine client rejects outright. The
# metrics UI and store looked fine for as long as only people opened
# them; the collector's exporter — same defect, no human in the loop —
# failed every POST and dropped the samples.
++ lib.optional deployCfg.grafana.enable swarmCfg.grafana.domain
++ lib.optional deployCfg.victoriametrics.enable swarmCfg.victoriametrics.domain
++ lib.optional deployCfg.swarm-otel.enable swarmCfg.otel.domain
# VictoriaLogs' vhost is new (was previously unpublished entirely — see
# swarm-victorialogs.nix's file-top comment) and needs the same
# membership every gateway-published swarm service needs: absent from
# this list, `gateway.lib.tlsFor` falls back to the hive leaf, which
# cannot cover a name under a different apex — see the ⚠️ above this
# list for what that looked like the last time a name was missed here.
++ lib.optional deployCfg.victorialogs.enable swarmCfg.victorialogs.domain
# The store's browser UI, not the store: `swarm.bao.domain` is mTLS and
# never gets a gateway certificate.
++ lib.optional deployCfg.bao.enable swarmCfg.bao.ui.domain;
# The swarm UI's apex is a sibling of the other names, not a parent:
# no CA in the hierarchy issues for it implicitly.
swarmCfg.domain
swarmCfg.grafana.domain
swarmCfg.victoriametrics.domain
swarmCfg.otel.domain
swarmCfg.victorialogs.domain
# The store's browser UI, not the store: `swarm.bao.domain` is mTLS and
# never gets a gateway certificate.
swarmCfg.bao.ui.domain
];
# Hives whose entry still carries the removed `certFingerprint`. Scanned
# here, at top level, because that is the only place an assertion about a
@ -274,28 +264,17 @@ in
'';
};
options.services.hyperhive.swarm.peerHives = lib.mkOption {
type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified);
readOnly = true;
internal = true;
description = ''
Read-only: `hives` minus this host's own entry. Derived once here
rather than in each consumer, because "everything that isn't me"
is a filter four different modules were re-implementing and only
one of them has to be wrong for a hive to peer with itself.
'';
};
options.services.hyperhive.swarm.serviceDomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
readOnly = true;
internal = true;
description = ''
Read-only: the public hostnames of the swarm's own services, in a
stable sorted order. Second derived set alongside `peerHives`, and
here for the same reason — the CA that name-constrains these and
the leaf that carries them as SANs must agree exactly, and two
modules each assembling the list is how they stop agreeing.
Read-only: the public hostnames of every swarm service, in a
stable sorted order, identical on every host whichever services it
runs. Derived once here because the CA that name-constrains these,
the store role that issues for them and the leaf that carries them
as SANs must agree exactly, and two modules each assembling the
list is how they stop agreeing.
Sorted and deduplicated deliberately: consumers compare this list
against what they issued last time to decide whether to re-issue,
@ -326,8 +305,6 @@ in
};
config = {
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 != "") serviceDomains')
);