From d212125b486af2dad1b5133685cc61323fe583bf Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 17 Aug 2026 20:02:15 +0200 Subject: [PATCH] fix(#3391): introspect authelia by name instead of loopback A loopback literal encodes 'authelia is in my netns' at the call site, and authelia's OIDC endpoints are https-only in effect: reached directly they answer 400, because the forwarded headers nginx injects for every other consumer are what let it determine its own issuer. Going by name deletes the need for those headers rather than reproducing them, and converges on the URL swarm-nats-auth already uses. Depends on the CA trust added for the same container in #3407 -- without it this swaps a 400 for an UnknownIssuer. The null-url assertion joins the module's existing list: interpolating a null would surface as a nix coercion error several files from its cause. --- nix/host-modules/swarm-authelia.nix | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/nix/host-modules/swarm-authelia.nix b/nix/host-modules/swarm-authelia.nix index 753b067f..e8f684ff 100644 --- a/nix/host-modules/swarm-authelia.nix +++ b/nix/host-modules/swarm-authelia.nix @@ -681,6 +681,18 @@ in # would reject it, but three layers away and at boot — naming both # sources here is the cheaper failure. ++ [ + { + # The bridge introspects by name, so a null URL becomes a nix + # coercion error several files from its cause. Only reachable by + # enabling authelia and clearing `url` by hand — an assertion + # rather than a fallback, because a guessed URL that evaluates + # cleanly is worse than a refused build. + assertion = cfg.url != null; + message = + "services.hyperhive.swarm.authelia.url must not be null when authelia " + + "is enabled: swarm-authelia-bridge introspects at " + + "`\${url}/api/oidc/introspection` from inside its container."; + } { assertion = lib.length (lib.unique (map (c: c.id) cfg.oidc.clients)) == lib.length cfg.oidc.clients; message = @@ -922,12 +934,14 @@ in # verifier's — same reasoning as `swarmctl`'s own # `SWARMCTL_AUTHELIA_BIN`. SWARM_AUTHELIA_BRIDGE_AUTHELIA_BIN = "${cfg.package}/bin/authelia"; - # Local loopback, not the public HTTPS vhost: this process - # runs right next to authelia (same container, same netns), - # so there is a faster, simpler path than round-tripping - # through the gateway's nginx for a call nothing external - # ever needs to see. - SWARM_AUTHELIA_BRIDGE_INTROSPECTION_URL = "http://127.0.0.1:${toString cfg.port}/api/oidc/introspection"; + # By name through the gateway, not loopback. A loopback + # literal encodes "authelia is in my netns" at the call site, + # and authelia's OIDC endpoints are https-only in effect — + # reached directly they answer 400, because the forwarded + # headers nginx injects for every other consumer are what let + # it determine its own issuer. Same URL `swarm-nats-auth` + # uses, so there is one idiom rather than two. + SWARM_AUTHELIA_BRIDGE_INTROSPECTION_URL = "${cfg.url}/api/oidc/introspection"; SWARM_AUTHELIA_BRIDGE_CLIENT_ID = bridgeClientId; # Minted by `${unitName}-secrets`'s `mint` loop (it iterates # every entry in `cfg.oidc.clients`, which now always