gateway: reject unmatched Host instead of serving the dashboard

The `_` vhost was serving the hive's own surface, so every dashboard and
agent-UI request matched the default server rather than a named vhost --
and so did a request for any name at all, including a raw IP.

Split it: `_` keeps only `return 444`, and the hive surface moves to a
vhost named for the hive domain. `_` is `mkDefault` so an operator can
claim default_server themselves, plus an assertion for the case where
they add one without turning ours off -- nginx refuses to start on a
duplicate default_server and nixpkgs asserts nothing, so that would
otherwise surface as a gateway outage at rebuild time.
This commit is contained in:
atlas 2026-08-13 19:34:12 +02:00 committed by mara
commit c32a9367e4
2 changed files with 74 additions and 4 deletions

View file

@ -219,11 +219,41 @@ let
in
{
virtualHosts = {
# `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.
# The catch-all, and now *only* a catch-all: anything whose `Host`
# matches no vhost gets an immediate 444 (close without a response)
# rather than being served the hive's dashboard.
#
# `_` is the idiomatic spelling because it is not a legal hostname,
# so it can never match a request by name — it serves traffic solely
# by being `default_server`.
#
# ⚠️ It still needs TLS attrs. It listens on the https port, so a
# client connecting by IP completes a TLS handshake *before* nginx
# can look at `Host` and reject it; with no cert the vhost fails to
# load. The certificate will not match what such a client asked for
# — that is unavoidable and correct: nothing can present a valid
# cert for a name the operator never issued one for.
#
# `mkDefault` per the operator: an operator with their own
# `default = true` vhost must be able to win without fighting
# priorities. The assertion in ./default.nix catches the case where
# they add one *without* turning this off, which nginx would
# otherwise only report at runtime as a failed config test.
"_" = (vhostTlsFor "_") // {
listen = vhostListen;
default = lib.mkDefault true;
extraConfig = ''
return 444;
'';
};
# The hive's own surface, now reachable by NAME. This used to be
# served by the `_` vhost above: no vhost was named for the hive
# domain, so every dashboard and agent-UI request matched the
# default server instead (confirmed against 24h of nginx's access
# log — `server: _` on requests whose Host *was* the hive domain).
# Naming it is what lets the catch-all start rejecting.
${hyperhiveDomain} = (vhostTlsFor hyperhiveDomain) // {
listen = vhostListen;
locations =
matrixRedirectLocations