`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
158 lines
7.8 KiB
Nix
158 lines
7.8 KiB
Nix
# `checks.module-eval-nats-authelia` — see ./lib.nix for the shared
|
|
# rationale (why this suite exists, naming convention, "evaluates
|
|
# not executes").
|
|
{
|
|
pkgs,
|
|
lib,
|
|
self,
|
|
nixosSystem,
|
|
}:
|
|
let
|
|
inherit
|
|
(import ./lib.nix {
|
|
inherit
|
|
pkgs
|
|
lib
|
|
self
|
|
nixosSystem
|
|
;
|
|
})
|
|
hive
|
|
runGroup
|
|
;
|
|
|
|
# The queue's callout identity, fourth split slice. `autoGenerateCallout` is
|
|
# left FALSE on purpose: that is what makes the seed paths the thing deciding
|
|
# `responderConfigured`, so the assertion below is about the seeds rather
|
|
# than about the auto-mint branch. Every one of the seven old paths is
|
|
# defined — `enable` included, which is why it is spelled the old way here
|
|
# while the fixture below uses the new one — so dropping any single nats
|
|
# shim fails the eval, not just the arms read.
|
|
natsOldPath = hive {
|
|
swarm.nats.enable = true;
|
|
swarm.nats.autoGenerateCallout = false;
|
|
swarm.nats.calloutUserPublicKey = "UTESTUSERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
swarm.nats.calloutIssuerPublicKey = "ATESTISSUERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
swarm.nats.calloutUserSeedFile = "/run/secrets/nats-user.seed";
|
|
swarm.nats.calloutIssuerSeedFile = "/run/secrets/nats-issuer.seed";
|
|
swarm.nats.authPackage = pkgs.emptyDirectory;
|
|
};
|
|
|
|
# Seventh split slice, plus slice 10's two authelia packages. Of slice 7's
|
|
# movers only `usersFile` has a rename entry — the other two are `readOnly`,
|
|
# and a rename module contributes a definition, which a read-only option
|
|
# refuses; see ./host-modules/deploy.nix. `package` and `bridgePackage` are
|
|
# ordinary options, so they do carry one. The two arms
|
|
# below have different jobs. `usersFile` tests the rename; the nats one tests
|
|
# that a reader repointed to the new namespace still renders the derived
|
|
# path, which is the failure this slice could actually have shipped — seven
|
|
# of those reads went through an alias a path-shaped grep cannot see.
|
|
autheliaOldPath = hive {
|
|
deploy.authelia.enable = true;
|
|
deploy.nats.enable = true;
|
|
swarm.authelia.usersFile = "/var/lib/test-authelia/users.yml";
|
|
swarm.authelia.package = pkgs.emptyDirectory;
|
|
swarm.authelia.bridgePackage = pkgs.emptyDirectory;
|
|
swarm.nats.autoGenerateCallout = false;
|
|
swarm.nats.calloutUserPublicKey = "UTESTUSERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
swarm.nats.calloutIssuerPublicKey = "ATESTISSUERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
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 {…} // {
|
|
# … }` is a shallow merge, and a future edit that dropped or shadowed
|
|
# this key would still evaluate cleanly — the only reader that would
|
|
# notice is a publisher whose row exceeds upstream's much smaller
|
|
# default, and by then it is a dropped row, not an eval failure.
|
|
# Piggybacks on the pre-rename nats fixture above, which already
|
|
# renders this container's full config.
|
|
name = "the queue's payload ceiling is set, not inherited from the server's default";
|
|
ok = natsOldPath.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608;
|
|
}
|
|
{
|
|
# Reads the RENDERED unit text, not the module's source, because the
|
|
# failure this defends against renders perfectly: systemd substitutes
|
|
# `$NAME` in `ExecStart` regardless of quoting, so a single dollar
|
|
# here hands the responder `.term.{hive}.>` — a grant that parses, is
|
|
# accepted, and matches nothing an agent ever publishes to. Asserting
|
|
# the doubled dollar is the only way to tell the two apart before
|
|
# deploy. The flag's presence is asserted separately so that dropping
|
|
# the grant entirely fails as its own arm rather than as an escaping
|
|
# complaint.
|
|
name = "the responder grants agents their hive's terminal subject, and the dollar survives systemd";
|
|
ok =
|
|
let
|
|
exec =
|
|
natsOldPath.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart;
|
|
in
|
|
lib.hasInfix "--agent-publish-subject " exec && lib.hasInfix "$$SWARM.term.{hive}.>" exec;
|
|
}
|
|
{
|
|
# Second grant, same escaping trap, asserted separately: the two
|
|
# subject families are independent features (terminal rows and the
|
|
# turn-state header) and dropping either should fail as its own arm
|
|
# rather than being masked by the other still being present.
|
|
#
|
|
# Flag and argument are matched as one infix rather than as two
|
|
# independent `hasInfix` calls: the responder takes the flag
|
|
# repeatedly, so the thing worth pinning is that THIS subject is the
|
|
# argument of one of them, which two separate presence checks would
|
|
# both pass on while the subject sat under some other flag entirely.
|
|
name = "the responder grants agents their hive's agent-state subject too";
|
|
ok =
|
|
let
|
|
exec =
|
|
natsOldPath.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart;
|
|
in
|
|
lib.hasInfix "--agent-publish-subject '$$SWARM.agent-state.{hive}.>'" exec;
|
|
}
|
|
{
|
|
# Not a rename test. `hostClientSecretDir` is `readOnly`, so the fixture
|
|
# cannot define it; what can break is a reader left pointing at the
|
|
# namespace it moved out of. Five modules read this through an
|
|
# `autheliaCfg` alias, where a path-shaped grep does not see it.
|
|
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
|