gateway: service modules declare their vhost under gateway.vhosts
Ten service modules (forge, matrix chat, authelia, grafana, otel, nats, swarm-ui, victorialogs, victoriametrics, bao UI) wrote services.nginx.virtualHosts themselves, each applying the gateway's listen set, per-name TLS attrs and security headers by hand. They now declare services.hyperhive.gateway.vhosts.<name> with only their own locations and options; ./declared-vhosts.nix renders it into the nginx vhost with listen, TLS and headers added. An eval assertion refuses a declared vhost that sets listen, addSSL, onlySSL, enableACME, useACMEHost, sslCertificate or sslCertificateKey, and one declared while the gateway is off. nats' own leaf is the new `certificate` field. The rendered nginx vhosts are unchanged.
This commit is contained in:
parent
7b50ae57e4
commit
ddb583cc22
17 changed files with 652 additions and 513 deletions
|
|
@ -252,107 +252,103 @@ in
|
|||
}
|
||||
'';
|
||||
|
||||
# `removeAttrs`/`forceSSL`: authelia answers a plain-http auth subrequest
|
||||
# `forceSSL`: authelia answers a plain-http auth subrequest
|
||||
# with 400, which `auth_request` cannot read as anything but a broken
|
||||
# check. Same as ./swarm-victorialogs.nix's vhost.
|
||||
services.nginx.virtualHosts."${cfg.domain}" =
|
||||
(builtins.removeAttrs (gatewayCfg.lib.tlsFor cfg.domain) [ "addSSL" ])
|
||||
// {
|
||||
forceSSL = true;
|
||||
listen = gatewayCfg.lib.listen;
|
||||
extraConfig = gatewayCfg.lib.securityHeaders;
|
||||
locations =
|
||||
readLocations
|
||||
// uiLocations
|
||||
// {
|
||||
# Nothing reaches the store unless a location here names it. The
|
||||
# store serves writes and admin calls (`/api/v1/import`,
|
||||
# `/api/v1/admin/tsdb/delete_series`, ...) on the same listener as
|
||||
# its queries, and authenticates none of them.
|
||||
"/".return = "403";
|
||||
services.hyperhive.gateway.vhosts."${cfg.domain}" = {
|
||||
forceSSL = true;
|
||||
locations =
|
||||
readLocations
|
||||
// uiLocations
|
||||
// {
|
||||
# Nothing reaches the store unless a location here names it. The
|
||||
# store serves writes and admin calls (`/api/v1/import`,
|
||||
# `/api/v1/admin/tsdb/delete_series`, ...) on the same listener as
|
||||
# its queries, and authenticates none of them.
|
||||
"/".return = "403";
|
||||
|
||||
# The swarm UI's quick link opens the bare domain. The store's own
|
||||
# index page links mostly to paths refused above, so send it to vmui.
|
||||
"= /".return = "302 /vmui/";
|
||||
# The swarm UI's quick link opens the bare domain. The store's own
|
||||
# index page links mostly to paths refused above, so send it to vmui.
|
||||
"= /".return = "302 /vmui/";
|
||||
|
||||
# The swarm collector's ingest route. `=` so it outranks the `/`
|
||||
# prefix above.
|
||||
#
|
||||
# ⚠️ No `error_page 401 =302` here, and its absence is the point: a
|
||||
# redirect is right for a browser and wrong for a pusher, which would
|
||||
# follow it and POST its batch at a login page that answers 200 —
|
||||
# ingest reporting healthy while storing nothing.
|
||||
"= /opentelemetry/api/v1/push" = {
|
||||
proxyPass = upstream "/opentelemetry/api/v1/push";
|
||||
extraConfig = ''
|
||||
auth_request /__metrics_authelia;
|
||||
'';
|
||||
};
|
||||
|
||||
# The store's own health, for the swarm collector wherever it runs:
|
||||
# served on the query listener, so no exporter and no extra port. `=`
|
||||
# for the same reason as the push route, and no `error_page` for the
|
||||
# same reason: a scraper would parse a login page as metrics.
|
||||
#
|
||||
# The collector requests exactly this URL, declared as
|
||||
# `publishedScrapeTargets.victoriametrics` in ./swarm-otel-service.nix;
|
||||
# the two must agree, since that string is also the token's audience.
|
||||
"= /metrics" = {
|
||||
proxyPass = upstream "/metrics";
|
||||
extraConfig = ''
|
||||
auth_request /__metrics_authelia;
|
||||
'';
|
||||
};
|
||||
|
||||
# `rewrite … last` re-matches locations, so the subrequest lands in
|
||||
# one of the two internal locations named by the map.
|
||||
"= /__metrics_read_auth" = {
|
||||
extraConfig = ''
|
||||
internal;
|
||||
rewrite ^ $swarm_metrics_read_auth_target last;
|
||||
'';
|
||||
};
|
||||
|
||||
# Grafana's bearer, checked by `swarm-metrics-auth`. The
|
||||
# `Authorization` header rides the subrequest unchanged. No rate limit
|
||||
# in front: the responder reads the store at most once per its cache
|
||||
# TTL, whatever the bearers.
|
||||
"= /__metrics_grafana_auth" = {
|
||||
proxyPass = "http://unix:${metricsAuthSocket}:/";
|
||||
extraConfig = ''
|
||||
internal;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
'';
|
||||
};
|
||||
|
||||
# The authelia subrequest. For the collector's two locations above it
|
||||
# admits only the collector's client; for the read and vmui locations
|
||||
# it admits an operator's session (`metricsStoreRules` in
|
||||
# ./swarm-authelia.nix). Same target and header set as the sibling log
|
||||
# store's, which took them from `swarm-ui.nix` — `X-Original-URL` and
|
||||
# `X-Original-Method` are what authelia's auth-request implementation
|
||||
# reads, and the address it compares the token's audience against.
|
||||
"= /__metrics_authelia" = {
|
||||
proxyPass = "https://${hyperhiveCfg.swarm.authelia.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 hyperhiveCfg.swarm.authelia.domain}
|
||||
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;
|
||||
'';
|
||||
};
|
||||
# The swarm collector's ingest route. `=` so it outranks the `/`
|
||||
# prefix above.
|
||||
#
|
||||
# ⚠️ No `error_page 401 =302` here, and its absence is the point: a
|
||||
# redirect is right for a browser and wrong for a pusher, which would
|
||||
# follow it and POST its batch at a login page that answers 200 —
|
||||
# ingest reporting healthy while storing nothing.
|
||||
"= /opentelemetry/api/v1/push" = {
|
||||
proxyPass = upstream "/opentelemetry/api/v1/push";
|
||||
extraConfig = ''
|
||||
auth_request /__metrics_authelia;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# The store's own health, for the swarm collector wherever it runs:
|
||||
# served on the query listener, so no exporter and no extra port. `=`
|
||||
# for the same reason as the push route, and no `error_page` for the
|
||||
# same reason: a scraper would parse a login page as metrics.
|
||||
#
|
||||
# The collector requests exactly this URL, declared as
|
||||
# `publishedScrapeTargets.victoriametrics` in ./swarm-otel-service.nix;
|
||||
# the two must agree, since that string is also the token's audience.
|
||||
"= /metrics" = {
|
||||
proxyPass = upstream "/metrics";
|
||||
extraConfig = ''
|
||||
auth_request /__metrics_authelia;
|
||||
'';
|
||||
};
|
||||
|
||||
# `rewrite … last` re-matches locations, so the subrequest lands in
|
||||
# one of the two internal locations named by the map.
|
||||
"= /__metrics_read_auth" = {
|
||||
extraConfig = ''
|
||||
internal;
|
||||
rewrite ^ $swarm_metrics_read_auth_target last;
|
||||
'';
|
||||
};
|
||||
|
||||
# Grafana's bearer, checked by `swarm-metrics-auth`. The
|
||||
# `Authorization` header rides the subrequest unchanged. No rate limit
|
||||
# in front: the responder reads the store at most once per its cache
|
||||
# TTL, whatever the bearers.
|
||||
"= /__metrics_grafana_auth" = {
|
||||
proxyPass = "http://unix:${metricsAuthSocket}:/";
|
||||
extraConfig = ''
|
||||
internal;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
'';
|
||||
};
|
||||
|
||||
# The authelia subrequest. For the collector's two locations above it
|
||||
# admits only the collector's client; for the read and vmui locations
|
||||
# it admits an operator's session (`metricsStoreRules` in
|
||||
# ./swarm-authelia.nix). Same target and header set as the sibling log
|
||||
# store's, which took them from `swarm-ui.nix` — `X-Original-URL` and
|
||||
# `X-Original-Method` are what authelia's auth-request implementation
|
||||
# reads, and the address it compares the token's audience against.
|
||||
"= /__metrics_authelia" = {
|
||||
proxyPass = "https://${hyperhiveCfg.swarm.authelia.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 hyperhiveCfg.swarm.authelia.domain}
|
||||
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;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# The socket directory, created host-side before the container starts:
|
||||
# nixos-container refuses to start when a bind source is missing.
|
||||
|
|
|
|||
Loading…
Reference in a new issue