nix: address the swarm IdP by its domain, not by who runs it

`swarm.authelia.url` defaulted to `https://<domain>` only when this host
ran the container, and to `null` otherwise — so the address a client is
given was a statement about co-location rather than about the swarm. A
swarm has one SSO provider; every hive addresses the same name and
resolution decides which address that reaches, exactly as
`swarm.otel.domain` already works.

The option stays nullable: "this swarm has no IdP" is still expressible,
it is just now something an operator states rather than something not
running the container produces. The Grafana fixture that exercised the
no-IdP refusal says it explicitly.

Closes #4536
This commit is contained in:
atlas 2026-09-19 20:39:19 +02:00 committed by mara
commit 4b6214305f
4 changed files with 61 additions and 14 deletions

View file

@ -73,12 +73,17 @@ let
# The mirror image: the identity is placed, and the swarm names no IdP. The
# other half of "SSO must always be configured", and isolated the same way —
# exactly one thing wrong, so the arm reads one refusal.
#
# The null is now written out: `swarm.authelia.url` defaults to the swarm's
# IdP name on every hive, so "this swarm has no IdP" is a thing an operator
# states rather than a thing not running the container produces.
grafanaNoSso = hive {
deploy.grafana.enable = true;
deploy.grafana.plugins = [ ];
deploy.grafana.package = pkgs.emptyDirectory;
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
swarm.authelia.url = null;
};
# Did ./host-modules/swarm-grafana.nix refuse this host, and for which of its

View file

@ -59,6 +59,15 @@ let
swarm.nats.calloutUserSeedFile = "/run/secrets/nats-user.seed";
swarm.nats.calloutIssuerSeedFile = "/run/secrets/nats-issuer.seed";
};
# A hive running NOTHING of the swarm's own services — no IdP here, no
# `swarm.authelia.url` set by hand. The whole point of the fixture is what it
# does *not* say: it is the shape whose IdP address used to be null, and
# before that a co-location-derived guess.
autheliaNotColocated = hive { };
# The same swarm's IdP host, for the arm that the two agree.
autheliaColocated = hive { deploy.authelia.enable = true; };
cases = [
{
# Reads the RENDERED settings, not the option: `calloutBlocks {…} // {
@ -116,6 +125,34 @@ let
name = "a consumer of authelia's host client-secret dir renders it from the deploy namespace";
ok = lib.hasInfix "/var/lib/nixos-containers/swarm-authelia/var/lib/authelia-swarm/oidc-clients/" autheliaOldPath.systemd.services.swarm-nats-auth-secrets.script;
}
{
# Config NAMES the IdP; it never computes where the IdP is. Both arms
# matter together: the address is the swarm's name on a hive that runs
# nothing, and it is the SAME string on the hive that serves the vhost —
# so a re-introduced co-location branch shows up as the two disagreeing
# rather than as a value that merely looks plausible on one of them.
name = "the swarm IdP address is its domain on every hive, co-located or not";
ok =
autheliaNotColocated.services.hyperhive.swarm.authelia.url
== "https://${autheliaNotColocated.services.hyperhive.swarm.authelia.domain}"
&&
autheliaColocated.services.hyperhive.swarm.authelia.url
== autheliaNotColocated.services.hyperhive.swarm.authelia.url;
}
{
# …and the name it resolves through is a DOMAIN, not a host this config
# picked. `127.0.0.1` is what the old default rendered on the IdP host,
# the bridge address is what the queue's defaults still render, and
# neither is an address a client may be handed: the domain is allowed to
# resolve differently in different places, which is the whole property.
name = "the swarm IdP address names no host address";
ok =
let
url = autheliaNotColocated.services.hyperhive.swarm.authelia.url;
bridgeIp = autheliaNotColocated.services.hyperhive.network.bridgeIp;
in
!(lib.hasInfix "127.0.0.1" url) && !(lib.hasInfix "localhost" url) && !(lib.hasInfix bridgeIp url);
}
];
in
runGroup "nats-authelia" cases