From 67a20d387fb7aa8862da2e30ed38339e269f38a2 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 11 Aug 2026 22:32:26 +0200 Subject: [PATCH] feat(3083): the gateway serves authelia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit authelia has listened on 127.0.0.1:9091 since it was stood up, with nothing proxying to it — so `auth.` resolved and then refused the connection. This is the vhost that was never written. Follows forge and matrix exactly: one `optionalAttrs` attrset merged into `virtualHosts`, TLS chosen by `vhostTlsFor` (the swarm-services leaf already names it, since `swarm.serviceDomains` includes `authelia.domain`), and the same four wiring sites those two occupy — vhost, dnsmasq address, local-dev `/etc/hosts`, and the arg lists that feed both files. Gated on this host running the container, not on authelia being configured: every hive knows the swarm's `authelia.url`, but only the one serving it may claim the name. A client hive declaring this vhost would answer for a service it does not run. Two things that are deliberate rather than incidental: `X-Forwarded-{Proto,Host,Uri,For}` are set because authelia decides by the *original* request — the login redirect and the session cookie's domain both derive from them. Without them every request looks like it arrived at 127.0.0.1 over plain http. And no `auth_basic`. Applying the gateway's basic-auth block to the SSO provider would put the login page behind the login mechanism it exists to replace. --- nix/host-modules/hive-gateway/default.nix | 4 +++ nix/host-modules/hive-gateway/dnsmasq.nix | 4 ++- nix/host-modules/hive-gateway/vhosts.nix | 38 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/nix/host-modules/hive-gateway/default.nix b/nix/host-modules/hive-gateway/default.nix index 084937a5..92d5f7c9 100644 --- a/nix/host-modules/hive-gateway/default.nix +++ b/nix/host-modules/hive-gateway/default.nix @@ -22,6 +22,7 @@ let # same list rather than each deciding what "a swarm service" means. swarmServiceDomains = config.services.hyperhive.swarm.serviceDomains; matrixCfg = config.services.hyperhive.swarm.matrix; + autheliaCfg = config.services.hyperhive.swarm.authelia; forgeCfg = config.services.hyperhive.swarm.forge; networkCfg = config.services.hyperhive.network; @@ -70,6 +71,7 @@ let cfg forgeCfg matrixCfg + autheliaCfg hyperhiveDomain dashboardDist swaggerUiTheme @@ -309,6 +311,7 @@ in networkCfg forgeCfg matrixCfg + autheliaCfg hyperhiveDomain ; }; @@ -332,6 +335,7 @@ in ++ lib.optional (config.services.hyperhive.swarm.forge.behindGateway or false ) config.services.hyperhive.swarm.forge.domain ++ lib.optional (matrixCfg.enable && matrixCfg.gatewayHost != null) matrixCfg.gatewayHost + ++ lib.optional autheliaCfg.enable autheliaCfg.domain ); }; }; diff --git a/nix/host-modules/hive-gateway/dnsmasq.nix b/nix/host-modules/hive-gateway/dnsmasq.nix index b212309a..52eabb4d 100644 --- a/nix/host-modules/hive-gateway/dnsmasq.nix +++ b/nix/host-modules/hive-gateway/dnsmasq.nix @@ -10,6 +10,7 @@ networkCfg, forgeCfg, matrixCfg, + autheliaCfg, hyperhiveDomain, }: { @@ -58,7 +59,8 @@ ++ lib.optional ((forgeCfg.behindGateway or false)) "/${forgeCfg.domain}/${networkCfg.bridgeIp}" ++ lib.optional ( matrixCfg.enable && matrixCfg.gatewayHost != null - ) "/${matrixCfg.gatewayHost}/${networkCfg.bridgeIp}"; + ) "/${matrixCfg.gatewayHost}/${networkCfg.bridgeIp}" + ++ lib.optional autheliaCfg.enable "/${autheliaCfg.domain}/${networkCfg.bridgeIp}"; # DHCP pool covering all usable host addresses on the bridge # subnet — bounds computed by hive-network.nix from # bridgeIp/bridgePrefixLength. All containers (agents and service diff --git a/nix/host-modules/hive-gateway/vhosts.nix b/nix/host-modules/hive-gateway/vhosts.nix index 91d5f484..20d9c97d 100644 --- a/nix/host-modules/hive-gateway/vhosts.nix +++ b/nix/host-modules/hive-gateway/vhosts.nix @@ -9,6 +9,7 @@ cfg, # services.hyperhive.gateway forgeCfg, matrixCfg, + autheliaCfg, # services.hyperhive.swarm.authelia hyperhiveDomain, dashboardDist, swaggerUiTheme, # nix/packages/swagger-ui-theme.nix: has index.html + hyperhive-theme.css @@ -123,6 +124,42 @@ let }; }; + # Authelia sub-domain vhost. `server_name = authelia.domain`, all of + # `/` → authelia. Empty attrset unless THIS host runs the container: + # every hive knows the swarm's `authelia.url`, but only the one + # serving it may claim the name — a client hive declaring this vhost + # would answer for a service it does not run. + # + # ⚠️ The server name must be exactly `autheliaCfg.domain`, not a + # near-miss: authelia validates `authelia_url ⊂ session cookie domain` + # at STARTUP, so a mismatch is a container that refuses to boot rather + # than a login that misbehaves. + # + # ⚠️ And deliberately NO `dashboardAuth` here. That block is the + # gateway's `auth_basic`; applying it to the SSO provider would put + # the login page behind the login mechanism it exists to replace. + autheliaVhost = lib.optionalAttrs autheliaCfg.enable { + "${autheliaCfg.domain}" = (vhostTlsFor autheliaCfg.domain) // { + listen = vhostListen; + extraConfig = securityHeaders; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString autheliaCfg.port}/"; + proxyWebsockets = true; + extraConfig = '' + proxy_buffering off; + # authelia decides by the ORIGINAL request, not by the hop it + # sees — the login redirect and the session cookie's domain + # both derive from these. Without them every request looks + # like it arrived at 127.0.0.1 over plain http. + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Uri $request_uri; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + ''; + }; + }; + }; + # Matrix sub-domain vhost. `server_name = matrixCfg.gatewayHost`. # `/_matrix/*` → tuwunel (CORS *, 50M body cap, 1h long-poll # timeout). `/` serves fluffychat or 404 if GUI off. nginx @@ -409,5 +446,6 @@ in }; } // forgeVhost + // autheliaVhost // matrixVhost; }