refactor(3202): the gateway publishes its vhost construction kit
Slice 1 of #3202. The listen set, the per-name TLS attrs and the security headers move out of vhosts.nix into ./vhost-lib.nix and are published as `services.hyperhive.gateway.lib` (internal, readOnly). No behaviour change: vhosts.nix consumes the published value, so the rendered vhost tree is identical. The point is the next slice. Today a swarm service's vhost lives in the gateway because only the gateway knows the port pair, the issuer for a name, and the header block. Publishing those three is what lets a service module declare its own vhost without the gateway having to know that service by name.
This commit is contained in:
parent
272944b98f
commit
d5782965db
4 changed files with 189 additions and 82 deletions
|
|
@ -16,65 +16,17 @@
|
|||
dashboardDist,
|
||||
swaggerUiTheme, # nix/packages/swagger-ui-theme.nix: has index.html + hyperhive-theme.css
|
||||
errorPages, # ./error-pages.nix: { notFound, unreachable, unauthorized, ssoUnavailable }
|
||||
tlsCert,
|
||||
tlsKey,
|
||||
svcCert, # swarm-services leaf, for names the hive CA cannot sign
|
||||
svcKey,
|
||||
swarmServiceDomains, # which vhosts those are (../swarm.nix derives it)
|
||||
gwLib, # `services.hyperhive.gateway.lib` — ./vhost-lib.nix's kit, via the option
|
||||
}:
|
||||
let
|
||||
# The gateway always terminates TLS: self-signed is the implicit
|
||||
# floor when neither `tls.certDir` nor ACME is set, so there is no
|
||||
# http-only mode. Listen addresses every vhost shares — plain http
|
||||
# on `cfg.port` plus TLS on `cfg.httpsPort`. See `docs/gateway.md`
|
||||
# ("TLS modes").
|
||||
vhostListen = [
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = cfg.port;
|
||||
}
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = cfg.httpsPort;
|
||||
ssl = true;
|
||||
}
|
||||
];
|
||||
# nixos `services.nginx.virtualHosts.<name>` ssl attrs merged
|
||||
# into each vhost. For ACME mode: `enableACME` + `addSSL` —
|
||||
# NixOS's ACME integration manages the cert lifecycle and sets
|
||||
# ssl_certificate automatically. For self-signed / certDir:
|
||||
# explicit cert paths.
|
||||
vhostTls =
|
||||
if cfg.tls.acme.enable then
|
||||
{
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
addSSL = true;
|
||||
sslCertificate = tlsCert;
|
||||
sslCertificateKey = tlsKey;
|
||||
};
|
||||
|
||||
# TLS attrs for one vhost, by name. A swarm service's name may sit
|
||||
# outside this hive's domain — and then the hive CA is
|
||||
# name-constrained out of it, so its vhost must serve the
|
||||
# swarm-services leaf instead. Everything else keeps the hive leaf.
|
||||
#
|
||||
# Only in self-signed mode: with ACME or an operator cert there is a
|
||||
# single issuer that already covers every name, and a second pair
|
||||
# would be a cert nobody asked for.
|
||||
vhostTlsFor =
|
||||
host:
|
||||
if !cfg.tls.acme.enable && cfg.tls.certDir == null && builtins.elem host swarmServiceDomains then
|
||||
{
|
||||
addSSL = true;
|
||||
sslCertificate = svcCert;
|
||||
sslCertificateKey = svcKey;
|
||||
}
|
||||
else
|
||||
vhostTls;
|
||||
# The kit's three members, bound to the names this file already used.
|
||||
# Read through `gwLib` (the published option) rather than importing
|
||||
# ./vhost-lib.nix directly: a service module declaring its own vhost
|
||||
# gets the same object, so "the forge vhost listens where the gateway
|
||||
# listens" is true by construction and not by review.
|
||||
inherit (gwLib) securityHeaders;
|
||||
vhostListen = gwLib.listen;
|
||||
vhostTlsFor = gwLib.tlsFor;
|
||||
|
||||
# Public-facing scheme + port-suffix for URLs the gateway
|
||||
# mints into responses (well-known JSON, the deprecated
|
||||
|
|
@ -85,25 +37,6 @@ let
|
|||
publicPort = cfg.httpsPort;
|
||||
publicPortSuffix = if publicPort == 443 then "" else ":${toString publicPort}";
|
||||
|
||||
# Security headers added at the server scope on every vhost.
|
||||
# nginx's add_header inheritance rule: a location that defines its
|
||||
# own add_header does NOT inherit the server-level ones. Any
|
||||
# location with its own add_header (e.g. CORS on /.well-known or
|
||||
# /_matrix/) must repeat the security headers explicitly — see those
|
||||
# locations below. HTML-serving and proxy locations that carry no
|
||||
# add_header of their own pick these up from the server scope
|
||||
# automatically.
|
||||
hstsDirectives = lib.concatStringsSep "; " (
|
||||
[ "max-age=${toString cfg.hsts.maxAge}" ]
|
||||
++ lib.optional cfg.hsts.includeSubDomains "includeSubDomains"
|
||||
);
|
||||
securityHeaders = ''
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
${lib.optionalString cfg.hsts.enable ''add_header Strict-Transport-Security "${hstsDirectives}" always;''}
|
||||
'';
|
||||
|
||||
# Forge sub-domain vhost. `server_name = forge.domain`, proxies
|
||||
# all `/` → forgejo. Tuned for git: `client_max_body_size 1G`,
|
||||
# `proxy_read_timeout 1h` (multi-GB clones). SSH stays direct on
|
||||
|
|
@ -534,7 +467,11 @@ in
|
|||
'';
|
||||
|
||||
virtualHosts = {
|
||||
"_" = vhostTls // {
|
||||
# `tlsFor "_"`, not a separate binding: the default server is a
|
||||
# vhost named `_`, and a name that is not a swarm service domain
|
||||
# (`_` never is) resolves to the hive's own leaf — which is what
|
||||
# this vhost has always served.
|
||||
"_" = (vhostTlsFor "_") // {
|
||||
listen = vhostListen;
|
||||
locations =
|
||||
matrixRedirectLocations
|
||||
|
|
|
|||
Loading…
Reference in a new issue