feat(3167): the swarm UI vhost, behind an authelia subrequest
Serves the static bundle on the swarm apex and gates it with auth_request - the first one in this gateway, everything else being auth_basic + htpasswd. Header set measured against the pinned authelia (4.39.20) rather than copied from an example: X-Original-URL and X-Original-Method are present as literals and are what the auth-request implementation reads, while X-Forwarded-Uri does not appear in that binary at all - sending it would look like configuration and be dead weight. The endpoint is /api/authz/auth-request; /api/verify is the legacy path older examples show. auth_request_set captures the return URL BEFORE the error_page jump: in the 401 handler $request_uri is the internal one, so building the link there sends the operator back to the auth subrequest rather than the page they asked for. Authorisation is the access_control rule from the previous commit, not this subrequest: auth_request answers 'is there a session'.
This commit is contained in:
parent
d337fec565
commit
0dc2e6b64f
2 changed files with 67 additions and 1 deletions
|
|
@ -23,6 +23,7 @@ let
|
||||||
swarmServiceDomains = config.services.hyperhive.swarm.serviceDomains;
|
swarmServiceDomains = config.services.hyperhive.swarm.serviceDomains;
|
||||||
matrixCfg = config.services.hyperhive.swarm.matrix;
|
matrixCfg = config.services.hyperhive.swarm.matrix;
|
||||||
autheliaCfg = config.services.hyperhive.swarm.authelia;
|
autheliaCfg = config.services.hyperhive.swarm.authelia;
|
||||||
|
uiCfg = config.services.hyperhive.swarm.ui;
|
||||||
forgeCfg = config.services.hyperhive.swarm.forge;
|
forgeCfg = config.services.hyperhive.swarm.forge;
|
||||||
networkCfg = config.services.hyperhive.network;
|
networkCfg = config.services.hyperhive.network;
|
||||||
|
|
||||||
|
|
@ -70,6 +71,7 @@ let
|
||||||
forgeCfg
|
forgeCfg
|
||||||
matrixCfg
|
matrixCfg
|
||||||
autheliaCfg
|
autheliaCfg
|
||||||
|
uiCfg
|
||||||
hyperhiveDomain
|
hyperhiveDomain
|
||||||
dashboardDist
|
dashboardDist
|
||||||
swaggerUiTheme
|
swaggerUiTheme
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
forgeCfg,
|
forgeCfg,
|
||||||
matrixCfg,
|
matrixCfg,
|
||||||
autheliaCfg, # services.hyperhive.swarm.authelia
|
autheliaCfg, # services.hyperhive.swarm.authelia
|
||||||
|
uiCfg, # services.hyperhive.swarm.ui
|
||||||
hyperhiveDomain,
|
hyperhiveDomain,
|
||||||
dashboardDist,
|
dashboardDist,
|
||||||
swaggerUiTheme, # nix/packages/swagger-ui-theme.nix: has index.html + hyperhive-theme.css
|
swaggerUiTheme, # nix/packages/swagger-ui-theme.nix: has index.html + hyperhive-theme.css
|
||||||
|
|
@ -171,6 +172,68 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Swarm UI vhost — the swarm's front page, on the swarm apex, and the
|
||||||
|
# FIRST `auth_request` anywhere in this gateway (everything else is
|
||||||
|
# `auth_basic` + htpasswd).
|
||||||
|
#
|
||||||
|
# ⚠️ `auth_request` answers "is there a session", not "is this an
|
||||||
|
# operator". The operator-only part is authelia's `access_control`
|
||||||
|
# rule (../swarm-authelia.nix) requiring `group:operators` — agents
|
||||||
|
# are getting authelia accounts of their own, and without that rule a
|
||||||
|
# session alone would open this page.
|
||||||
|
#
|
||||||
|
# ⚠️ Failure mode here is LOCKED OUT, not unprotected: a subrequest
|
||||||
|
# that wrongly denies takes the whole UI away. That is the reason the
|
||||||
|
# redirect target and the header set below are copied from a measured
|
||||||
|
# source rather than from an example.
|
||||||
|
swarmUiVhost = lib.optionalAttrs uiCfg.enable {
|
||||||
|
"${uiCfg.domain}" = (vhostTlsFor uiCfg.domain) // {
|
||||||
|
listen = vhostListen;
|
||||||
|
extraConfig = securityHeaders;
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
root = "${uiCfg.package}";
|
||||||
|
extraConfig = ''
|
||||||
|
auth_request /__hive_authelia;
|
||||||
|
# Captured BEFORE the error_page jump: inside the 401 handler
|
||||||
|
# `$request_uri` is the internal one, so building the return
|
||||||
|
# link there sends the operator back to the auth subrequest
|
||||||
|
# instead of the page they asked for.
|
||||||
|
auth_request_set $target_url $scheme://$http_host$request_uri;
|
||||||
|
error_page 401 =302 https://${autheliaCfg.domain}/?rd=$target_url;
|
||||||
|
# SPA: any path the bundle routes client-side is served the
|
||||||
|
# entry document rather than a 404 from the filesystem.
|
||||||
|
try_files $uri /index.html;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# The subrequest itself. `auth-request` is the implementation
|
||||||
|
# name authelia exposes under `/api/authz/`; `/api/verify` is the
|
||||||
|
# LEGACY path every older example shows.
|
||||||
|
#
|
||||||
|
# Header set measured against the pinned binary (4.39.20), not
|
||||||
|
# copied: `X-Original-URL` and `X-Original-Method` are present as
|
||||||
|
# literals and are what this implementation reads —
|
||||||
|
# `X-Forwarded-Uri` does not appear in it at all, so sending it
|
||||||
|
# would look like configuration and be dead weight.
|
||||||
|
"= /__hive_authelia" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString autheliaCfg.port}/api/authz/auth-request";
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
# A subrequest carries no body, and forwarding one here makes
|
||||||
|
# authelia read a payload it will never use.
|
||||||
|
proxy_pass_request_body off;
|
||||||
|
proxy_set_header Content-Length "";
|
||||||
|
proxy_set_header X-Original-Method $request_method;
|
||||||
|
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Matrix sub-domain vhost. `server_name = matrixCfg.gatewayHost`.
|
# Matrix sub-domain vhost. `server_name = matrixCfg.gatewayHost`.
|
||||||
# `/_matrix/*` → tuwunel (CORS *, 50M body cap, 1h long-poll
|
# `/_matrix/*` → tuwunel (CORS *, 50M body cap, 1h long-poll
|
||||||
# timeout). `/` serves fluffychat or 404 if GUI off. nginx
|
# timeout). `/` serves fluffychat or 404 if GUI off. nginx
|
||||||
|
|
@ -458,5 +521,6 @@ in
|
||||||
}
|
}
|
||||||
// forgeVhost
|
// forgeVhost
|
||||||
// autheliaVhost
|
// autheliaVhost
|
||||||
// matrixVhost;
|
// matrixVhost
|
||||||
|
// swarmUiVhost;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue