From 8a0ecb307bbc4893e1f221366bf47fe55ea8c220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Thu, 27 Aug 2026 20:04:40 +0200 Subject: [PATCH] gateway: pin the Host header when dialing swarm services by name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit verifiedProxyTo (43ae164d) verified TLS but left Host to nixpkgs' recommendedProxySettings, which sets Host to the CALLING vhost, not the target. Since every consumer resolves back to this same gateway, nginx picks the vhost to answer by Host header (not by the SNI proxy_ssl_name already sends) — so every auth subrequest looped back into its own vhost's auth_request, recursing until nginx's subrequest depth limit turned it into a 500, on every domain gated by SSO. Pin Host (and the rest of the header set nixpkgs' recommended include would otherwise still be the one to set) inside verifiedProxyTo, and set recommendedProxySettings = false on each of the four call sites so nixpkgs' own copy — appended after a location's extraConfig — can't clobber it back. --- nix/host-modules/hive-forge/default.nix | 4 ++++ nix/host-modules/hive-gateway/vhost-lib.nix | 20 ++++++++++++++++++++ nix/host-modules/swarm-ui.nix | 8 ++++++++ nix/host-modules/swarm-victorialogs.nix | 4 ++++ 4 files changed, 36 insertions(+) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index 63d2b173..d0fca0f6 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -517,6 +517,10 @@ in # co-location nobody had agreed to. The name resolves here # today and resolves off-host later without this line moving. proxyPass = "https://${autheliaCfg.domain}/api/authz/auth-request"; + # nixpkgs appends its OWN `Host $host` after extraConfig, + # which would override verifiedProxyTo's — see the comment + # on verifiedProxyTo in hive-gateway/vhost-lib.nix. + recommendedProxySettings = false; extraConfig = '' internal; ${gatewayCfg.lib.verifiedProxyTo autheliaCfg.domain} diff --git a/nix/host-modules/hive-gateway/vhost-lib.nix b/nix/host-modules/hive-gateway/vhost-lib.nix index 74212d4e..c02f1745 100644 --- a/nix/host-modules/hive-gateway/vhost-lib.nix +++ b/nix/host-modules/hive-gateway/vhost-lib.nix @@ -112,12 +112,32 @@ in # testing though — the session cache is keyed by upstream address and # NOT by trust config, so two locations pointing at one upstream with # different trust do not verify independently. + # + # ⚠️ Also pins `Host` (and reinstates the rest of nginx's + # `recommendedProxySettings` header set) to `name` rather than leaving + # it to be filled in later. `name` here resolves back to THIS gateway + # — every consumer dials another vhost on the same nginx, not a + # separate host — and nginx picks the vhost to answer an HTTPS request + # from the `Host` header, not from the TLS SNI that `proxy_ssl_name` + # above sends. `recommendedProxySettings`'s own `Host $host` (the + # CALLER's host, not the target) is textually appended by nixpkgs + # AFTER a location's `extraConfig` — so it always wins over a + # `proxy_set_header Host` written in the location body, and the + # subrequest loops back into the ORIGINAL vhost instead of reaching + # the target, recursing on its own `auth_request` until nginx's + # subrequest-depth limit turns it into a plain 500. Every call site + # sets `recommendedProxySettings = false` on the location for exactly + # this reason — nixpkgs' version would still clobber this one. verifiedProxyTo = name: '' proxy_ssl_verify on; proxy_ssl_verify_depth 3; proxy_ssl_trusted_certificate ${caBundle}; proxy_ssl_name ${name}; proxy_ssl_server_name on; + proxy_set_header Host ${name}; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Server $hostname; ''; # Security headers added at the server scope on every vhost. diff --git a/nix/host-modules/swarm-ui.nix b/nix/host-modules/swarm-ui.nix index 7ac10f1c..e92b5cec 100644 --- a/nix/host-modules/swarm-ui.nix +++ b/nix/host-modules/swarm-ui.nix @@ -273,6 +273,10 @@ in # frontend calls this one directly. "= /api/whoami" = { proxyPass = "https://${autheliaCfg.domain}/api/user/info"; + # nixpkgs appends its OWN `Host $host` after extraConfig, + # which would override verifiedProxyTo's — see the comment + # on verifiedProxyTo in hive-gateway/vhost-lib.nix. + recommendedProxySettings = false; extraConfig = '' ${swarmAuthRequest} ${gatewayCfg.lib.verifiedProxyTo autheliaCfg.domain} @@ -296,6 +300,10 @@ in # would look like configuration and be dead weight. "= /__hive_authelia" = { proxyPass = "https://${autheliaCfg.domain}/api/authz/auth-request"; + # nixpkgs appends its OWN `Host $host` after extraConfig, + # which would override verifiedProxyTo's — see the comment + # on verifiedProxyTo in hive-gateway/vhost-lib.nix. + recommendedProxySettings = false; extraConfig = '' internal; ${gatewayCfg.lib.verifiedProxyTo autheliaCfg.domain} diff --git a/nix/host-modules/swarm-victorialogs.nix b/nix/host-modules/swarm-victorialogs.nix index 39b1b44f..68f87a1f 100644 --- a/nix/host-modules/swarm-victorialogs.nix +++ b/nix/host-modules/swarm-victorialogs.nix @@ -183,6 +183,10 @@ in # pinned authelia binary, not copied from an example). "= /__hive_authelia" = { proxyPass = "https://${autheliaCfg.domain}/api/authz/auth-request"; + # nixpkgs appends its OWN `Host $host` after extraConfig, + # which would override verifiedProxyTo's — see the comment + # on verifiedProxyTo in hive-gateway/vhost-lib.nix. + recommendedProxySettings = false; extraConfig = '' internal; ${gatewayCfg.lib.verifiedProxyTo autheliaCfg.domain}