Watch
0
0
Fork
You've already forked hyperhive
0

service urls carry no gateway port, httpsPort is listen-only

This commit is contained in:
atlas 2026-10-25 02:00:20 +02:00 • committed by mara
commit 19680a8b78
25 changed files with 105 additions and 143 deletions

View file

@ -98,7 +98,7 @@ homeserver it crosses the network, so the default is TLS; hive-c0re
verifies the gateway's certificate against the hive trust bundle.
Federation peers find the homeserver through `.well-known/matrix/server`
on the hive domain, which names `<gatewayHost>:<httpsPort>`
on the hive domain, which names `<gatewayHost>:443`
([`gateway.md::Discovery flow`](../networking/gateway.md#discovery-flow-matrix)).
Port 8448 isn't used.

View file

@ -75,7 +75,7 @@ nginx obtains and renews certs via the HTTP-01 challenge on `port` (default 80);
### Self-signed TLS (default)
On by default, listening on `httpsPort` (default 443) beside the plain-http `port` (default 80).
On by default, listening on `httpsPort` (default 443) beside the plain-http `port` (default 80). `httpsPort` only sets the listen port: swarm service URLs never carry a port, so clients reach them on 443.
The issuer is the **secret store's `pki` mount**, not a bare self-signed leaf. `hive-tls-resign.service` (from `hive-tls.nix`) asks the store for a gateway **leaf** through this hive's own role, presenting the hive's own store identity (`services.hyperhive.deploy.hive-controller.tls.baoHiveIdentityCertFile` / `baoHiveIdentityKeyFile`), and writes it under `…tls.stateDir` (default `/var/lib/hive-tls`). `hive-gateway-self-signed-cert` then imports the leaf into nginx's state dir (`/var/lib/hive-gateway/tls/{cert,key}.pem`). On a host that runs no hive the import unit installs the swarm-services leaf there instead, or a throwaway self-signed pair until that leaf lands. [`swarm/ca.md`](../swarm/ca.md) has the role, the identity and what a hive without one does.
@ -169,7 +169,7 @@ services.hyperhive.deploy.forgejo.openFirewall = false; # default
services.hyperhive.swarm.forge.rootUrl = "https://forge.example.com/";
```
`rootUrl` (default `null`) overrides the Forgejo `ROOT_URL` that's autoderived as `https://<forge.domain>/`, with `:<gateway.httpsPort>` appended when that port isn't 443. The gateway always terminates TLS, so the forge is always advertised over `https://`. Set `rootUrl` explicitly when `forge.domain` differs from the public URL, or for a bespoke shape such as an external reverse proxy on another host or path. It must end with `/` (an assertion enforces this).
`rootUrl` (default `null`) overrides the Forgejo `ROOT_URL` that's autoderived as `https://<forge.domain>/`. The gateway always terminates TLS, so the forge is always advertised over `https://`. Set `rootUrl` explicitly when `forge.domain` differs from the public URL, or for a bespoke shape such as an external reverse proxy on another host or path. It must end with `/` (an assertion enforces this).
## Security headers
@ -210,13 +210,13 @@ When enabled, every vhost adds `Strict-Transport-Security: max-age=…[; include
The operator points a client at `<hive>`:
1. The client fetches `https://<hive>/.well-known/matrix/client` → `{"m.homeserver":{"base_url":"https://chat.<swarm>"}}` (no port suffix when `httpsPort` is 443).
1. The client fetches `https://<hive>/.well-known/matrix/client` → `{"m.homeserver":{"base_url":"https://chat.<swarm>"}}`.
2. It connects to `chat.<swarm>/_matrix/client/...`.
3. The gateway proxies `/_matrix/*` to tuwunel over its unix socket, `/run/hive-matrix/tuwunel.sock`.
matrix-dart-sdk (FluffyChat and others) always fetches the well-known over `https`, which is why [self-signed TLS](#self-signed-tls-default) is on by default.
Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.<swarm>:<httpsPort>"}`. The port is always explicit, even 443: a delegated host without a port means the federation default 8448, not 443. Peers then reach `/_matrix/` on the chat vhost through the gateway, so the gateway must be reachable from them (`gateway.openFirewall`).
Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.<swarm>:443"}`. The port is explicit even though it's the https default: a delegated host without a port means the federation default 8448, not 443. Peers then reach `/_matrix/` on the chat vhost through the gateway, so the gateway must be reachable from them (`gateway.openFirewall`).
## Sub-domain shape (rationale)

View file

@ -24,8 +24,7 @@
# ./glue-swarm-otel-oidc-client.nix), and `deploy.swarm-otel.enable` only says it
# runs HERE. The endpoint is its swarm-service name through the gateway, never a
# loopback literal, with no trailing slash: the http exporter appends
# `/v1/metrics` and the gateway route strips `/${producerName}`. The port suffix
# is elided on 443, as in ./swarm-bao.nix.
# `/v1/metrics` and the gateway route strips `/${producerName}`.
# `SWARM_CONTROLLER_OTEL_AUDIENCE` is the audience ./glue-controller-oidc-client.nix
# registers and ./swarm-otel.nix's `oidc/${producerName}` authenticator checks.
{
@ -38,9 +37,6 @@ let
deployCfg = hyperhiveCfg.deploy;
otelSwarmCfg = hyperhiveCfg.swarm.otel;
queueClientId = hyperhiveCfg.swarm.controller.queueClientId;
gatewayPortSuffix = lib.optionalString (
hyperhiveCfg.gateway.httpsPort != 443
) ":${toString hyperhiveCfg.gateway.httpsPort}";
in
{
config = lib.mkIf deployCfg.swarm-controller.enable (
@ -53,7 +49,7 @@ in
})
{
systemd.services.swarm-controller.environment = {
OTEL_EXPORTER_OTLP_ENDPOINT = "https://${otelSwarmCfg.domain}${gatewayPortSuffix}/${otelSwarmCfg.producerName}";
OTEL_EXPORTER_OTLP_ENDPOINT = "https://${otelSwarmCfg.domain}/${otelSwarmCfg.producerName}";
SWARM_CONTROLLER_OTEL_AUDIENCE = queueClientId;
};
}

View file

@ -23,12 +23,7 @@ let
gatewayCfg = hyperhiveCfg.gateway;
matrixCfg = hyperhiveCfg.swarm.matrix;
publicPort = gatewayCfg.httpsPort;
# The canonical 443 elides the port; see `docs/networking/gateway.md`
# ("Self-signed TLS").
homeserverUrl = "https://${matrixCfg.gatewayHost}${
lib.optionalString (publicPort != 443) ":${toString publicPort}"
}";
homeserverUrl = "https://${matrixCfg.gatewayHost}";
in
{
config =
@ -73,7 +68,7 @@ in
"= /.well-known/matrix/server" = {
extraConfig = ''
default_type application/json;
return 200 '{"m.server":"${matrixCfg.gatewayHost}:${toString publicPort}"}';
return 200 '{"m.server":"${matrixCfg.gatewayHost}:443"}';
'';
};
};

View file

@ -19,10 +19,6 @@ let
# Dashboard SPA dist, static-served by nginx.
dashboardDist = "${hyperhiveCfg.c0re.servedFrontend}/dashboard";
# The canonical 443 elides the port; see `docs/networking/gateway.md`
# ("Self-signed TLS").
publicPortSuffix = if gatewayCfg.httpsPort == 443 then "" else ":${toString gatewayCfg.httpsPort}";
# `/agent/` catch-all 404 + the two internal error-page targets it
# points at. Per-agent `location /agent/<name>/` blocks live in the
# runtime-generated `/var/lib/hive-gateway/conf/agents.conf` (included via
@ -205,7 +201,7 @@ in
# lands.
${hyperhiveCfg.domain} = {
locations."/".extraConfig = ''
return 301 https://${dashboardDomain}${publicPortSuffix}$request_uri;
return 301 https://${dashboardDomain}$request_uri;
'';
};
};

View file

@ -83,12 +83,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.hive-forge` in
# ../swarm-otel-service.nix declares, as a client of its own rather than the
# login source's. The port suffix is elided on 443, as in ../swarm-bao.nix.
# login source's.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${config.services.hyperhive.swarm.otel.domain}${gatewayPortSuffix}/hive-forge";
forwarderEndpoint = "https://${config.services.hyperhive.swarm.otel.domain}/hive-forge";
forwarderUnit = "swarm-journal-forwarder-oidc";
# In the host netns: distinct from every other collector's there, which the
# assertion below checks.
@ -115,15 +112,11 @@ let
# gateway vhost server-name, so ROOT_URL just uses it directly. The
# gateway always terminates TLS (self-signed is the implicit floor when
# neither `tls.certDir` nor ACME is configured), so the forge is always
# advertised over `https` on `httpsPort` — the canonical 443 elides the
# port suffix. Operators can still override via `cfg.rootUrl` for
# bespoke shapes. Both bindings are duplicated in ./service.nix, which
# builds `sso.redirectUri` from them; keep the two equal.
defaultRootUrl =
let
portSuffix = if gatewayCfg.httpsPort == 443 then "" else ":${toString gatewayCfg.httpsPort}";
in
"https://${cfg.domain}${portSuffix}/";
# advertised as plain `https://<domain>/`. Operators can still override via
# `cfg.rootUrl` for bespoke shapes. Both bindings are duplicated in
# ./service.nix, which builds `sso.redirectUri` from them; keep the two
# equal.
defaultRootUrl = "https://${cfg.domain}/";
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
# `actionsFromSelf` resolves `uses:` against this forge, which then has to

View file

@ -8,7 +8,6 @@
}:
let
cfg = config.services.hyperhive.swarm.forge;
gatewayCfg = config.services.hyperhive.gateway;
swarmDomain = config.services.hyperhive.swarm.domain;
# Forgejo's name for the login source. Duplicated in ./default.nix, which
@ -22,11 +21,7 @@ let
# Forgejo's `ROOT_URL`, duplicated from ./default.nix, which documents its
# shape.
defaultRootUrl =
let
portSuffix = if gatewayCfg.httpsPort == 443 then "" else ":${toString gatewayCfg.httpsPort}";
in
"https://${cfg.domain}${portSuffix}/";
defaultRootUrl = "https://${cfg.domain}/";
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
in
{
@ -117,8 +112,8 @@ in
(default), `ROOT_URL` is `https://''${cfg.domain}/`. The gateway
always terminates TLS (self-signed is the implicit floor when no
`gateway.tls.certDir` / ACME is set), so the forge is always
advertised over https. A non-canonical `gateway.httpsPort` is
appended as `:<port>`.
advertised over https, on the default port. A gateway that listens
on another `gateway.httpsPort` does not change this URL.
Set this only for a genuinely bespoke shape (e.g. an external
reverse proxy on a different host/path). Must end with `/` per
@ -159,12 +154,6 @@ in
than a knob. The glue that registers this client wherever
authelia runs reads it from here instead of restating the
format.
⚠️ With {option}`services.hyperhive.swarm.forge.rootUrl` unset,
`ROOT_URL` follows the gateway's `httpsPort`, which is
per-host. An authelia host that is not the forge's host renders
the forge's callback only if the two agree on it; set `rootUrl`
if they do not.
'';
};
# The secret half is fetched from the swarm secret store on the host

View file

@ -307,10 +307,15 @@ in
default = 443;
example = 8443;
description = ''
TCP port for the TLS-terminated vhosts. Default 443. The gateway
always terminates TLS (self-signed is the implicit floor when no
`tls.certDir` / ACME is configured), so this port is always active
alongside the plain-http `port`.
TCP port nginx listens on for the TLS-terminated vhosts, and the one
`openFirewall` opens. Default 443. The gateway always terminates TLS
(self-signed is the implicit floor when no `tls.certDir` / ACME is
configured), so this port is always active alongside the plain-http
`port`.
Listen-only: it does not change any URL. Every swarm service URL is
`https://<domain>` with no port, so clients reach the services on 443
whatever this is set to.
'';
};

View file

@ -123,14 +123,10 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.hive-matrix` in
# ./swarm-otel-service.nix declares, as a client of its own rather than the
# homeserver login's. The port suffix is elided on 443, as in
# ./swarm-bao.nix. It reads its client secret with matrix-ctl's identity,
# homeserver login's. It reads its client secret with matrix-ctl's identity,
# bound in above: one identity for the whole container.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${config.services.hyperhive.swarm.otel.domain}${gatewayPortSuffix}/hive-matrix";
forwarderEndpoint = "https://${config.services.hyperhive.swarm.otel.domain}/hive-matrix";
# Loopback in the container's own netns, and distinct from the other service
# containers' forwarders all the same.
forwarderTelemetryPort = 8899;

View file

@ -201,13 +201,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-authelia` in
# ./swarm-otel-service.nix declares, as a client of its own. The port suffix
# is elided on 443, as in ./swarm-bao.nix.
# ./swarm-otel-service.nix declares, as a client of its own.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}${gatewayPortSuffix}/${cfg.machine}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}/${cfg.machine}";
# Loopback port of the forwarder's own metrics, inside the container's netns:
# no other collector shares it. The module requires a value.
forwarderTelemetryPort = 8901;

View file

@ -1442,11 +1442,7 @@ let
# wrong. The path selects WHICH receiver: this container is a swarm-level
# producer and not a hive, so it is the swarm tier's own route, the same one
# ./glue-controller-env.nix exports to. `/` on that vhost answers 404.
#
# The port suffix follows ./hive-forge/default.nix: elided on the canonical
# 443 so this renders byte for byte like the tier's other two consumers of
# this name, present when an operator moved the gateway's TLS port.
otelFirstHop = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${swarmOtelCfg.storeProducerName}";
otelFirstHop = "https://${swarmOtelCfg.domain}/${swarmOtelCfg.storeProducerName}";
# The forwarder's own credential, and the three names it takes on the way
# in. The shape is ./swarm-otel.nix's `swarm-bao-otel-oidc` — the unit that
@ -1498,11 +1494,6 @@ let
};
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix =
let
p = hyperhiveCfg.gateway.httpsPort;
in
lib.optionalString (p != 443) ":${toString p}";
# Non-zero is what SERVES the endpoint at all — the switch is a duration, not
# a boolean, so a zero here is an openbao that answers 404 on a listener

View file

@ -43,13 +43,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-builder` in
# ./swarm-otel-service.nix declares, by the collector's gateway name. The
# port suffix is elided on 443, as in ./swarm-bao.nix.
# ./swarm-otel-service.nix declares, by the collector's gateway name.
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback in the container's own netns, and distinct from the other service
# containers' forwarders all the same.

View file

@ -31,12 +31,8 @@ let
clientId = hyperhiveCfg.swarm.ci-metrics.clientId;
# The route `serviceProducers.swarm-ci-metrics` in ./swarm-otel-service.nix
# declares, by the collector's gateway name. The port suffix is elided on
# 443, as in ./swarm-builder.nix.
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
otlpEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${unit}";
# declares, by the collector's gateway name.
otlpEndpoint = "https://${swarmOtelCfg.domain}/${unit}";
haveClientIdentity =
baoDeploy.ciMetricsClientCertFile != null && baoDeploy.ciMetricsClientKeyFile != null;

View file

@ -63,14 +63,10 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-ci-runner` in
# ./swarm-otel-service.nix declares. The port suffix is elided on 443, as in
# ./swarm-bao.nix.
# ./swarm-otel-service.nix declares.
swarmOtelCfg = config.services.hyperhive.swarm.otel;
forwarderClientId = config.services.hyperhive.swarm.ci-runner.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
forwarderTokenUrl = "${toString config.services.hyperhive.swarm.authelia.url}/api/oidc/token";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback in the container's own netns, and distinct from the other service

View file

@ -46,13 +46,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-claude-login` in
# ./swarm-otel-service.nix declares, by the collector's gateway name. The
# port suffix is elided on 443, as in ./swarm-bao.nix.
# ./swarm-otel-service.nix declares, by the collector's gateway name.
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback in the container's own netns, and distinct from the other service
# containers' forwarders all the same.

View file

@ -306,12 +306,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-grafana` in
# ./swarm-otel-service.nix declares, as a client of its own rather than the
# browser login's. The port suffix is elided on 443, as in ./swarm-bao.nix.
# browser login's.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}${gatewayPortSuffix}/${cfg.machine}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}/${cfg.machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback port of the forwarder's own metrics, inside the container's netns:
# no other collector shares it. The module requires a value.

View file

@ -27,13 +27,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-nats` in
# ./swarm-otel-service.nix declares, by the collector's gateway name. The
# port suffix is elided on 443, as in ./swarm-bao.nix.
# ./swarm-otel-service.nix declares, by the collector's gateway name.
swarmOtelCfg = config.services.hyperhive.swarm.otel;
gatewayPortSuffix = lib.optionalString (
config.services.hyperhive.gateway.httpsPort != 443
) ":${toString config.services.hyperhive.gateway.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
tlsCfg = config.services.hyperhive.deploy.hive-controller.tls;
gatewayCfg = config.services.hyperhive.gateway;

View file

@ -38,10 +38,7 @@ let
# collector, the route `serviceProducers.swarm-notify-router` in
# ./swarm-otel-service.nix declares, as ./swarm-claude-login.nix does.
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback in the container's own netns, and distinct from the other service
# containers' forwarders all the same.

View file

@ -48,13 +48,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-review-classifier` in
# ./swarm-otel-service.nix declares, by the collector's gateway name. The
# port suffix is elided on 443, as in ./swarm-bao.nix.
# ./swarm-otel-service.nix declares, by the collector's gateway name.
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${machine}";
forwarderEndpoint = "https://${swarmOtelCfg.domain}/${machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# Loopback in the container's own netns, and distinct from the other service
# containers' forwarders all the same.

View file

@ -83,13 +83,9 @@ let
# The container's journal forwarder pushes to its own receiver on the swarm
# collector, the route `serviceProducers.swarm-victorialogs` in
# ./swarm-otel-service.nix declares. The port suffix is elided on 443, as in
# ./swarm-bao.nix.
# ./swarm-otel-service.nix declares.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}${gatewayPortSuffix}/${cfg.machine}";
forwarderEndpoint = "https://${hyperhiveCfg.swarm.otel.domain}/${cfg.machine}";
forwarderUnit = "swarm-journal-forwarder-oidc";
# In the host netns: distinct from every other collector's there, which the
# assertion below checks.

View file

@ -149,9 +149,6 @@ let
# The client secret is fetched on the host, under the responder's store
# identity, and bound in read-only: the container cannot reach the store.
forwarderClientId = cfg.otel.clientId;
gatewayPortSuffix = lib.optionalString (
gatewayCfg.httpsPort != 443
) ":${toString gatewayCfg.httpsPort}";
otelDomain = hyperhiveCfg.swarm.otel.domain;
autheliaDomain = hyperhiveCfg.swarm.authelia.domain;
@ -177,7 +174,7 @@ let
}
location ^~ /${cfg.machine}/ {
proxy_pass https://${otelDomain}${gatewayPortSuffix};
proxy_pass https://${otelDomain};
${gatewayCfg.lib.verifiedProxyTo otelDomain}
}

View file

@ -64,12 +64,12 @@ let
otel.debug = true;
};
# The controller behind a gateway on a non-default TLS port: the only
# fixture whose collector URL carries a port. Only the controller's
# environment is read off it.
# The controller behind a gateway on a non-default TLS port, with the
# firewall opened. Its collector URL and its listener are read off it.
controllerAltPort = hive {
deploy.swarm-controller.enable = true;
gateway.httpsPort = 8443;
gateway.openFirewall = true;
};
# On the store's host, which mints the runner's store identity.
@ -167,6 +167,27 @@ let
name = "the all-local mode turns the swarm controller on";
ok = allLocal.services.hyperhive.deploy.swarm-controller.enable;
}
{
# `gateway.httpsPort` is a listen setting: the nginx TLS listener and the
# opened firewall port follow it, while a service URL built on the same
# host stays `https://<domain>`. The listener and firewall arms are the
# control that the port took effect on this fixture, so the URL arm
# cannot pass on a fixture where it was never set.
name = "a non-default gateway TLS port moves the listener and firewall, not the service URLs";
ok =
let
otel = controllerAltPort.services.hyperhive.swarm.otel;
listen = controllerAltPort.services.nginx.virtualHosts."_".listen;
tcp = controllerAltPort.networking.firewall.allowedTCPPorts;
url =
controllerAltPort.systemd.services.swarm-controller.environment.OTEL_EXPORTER_OTLP_ENDPOINT or null;
in
lib.any (l: l.port == 8443 && (l.ssl or false)) listen
&& !(lib.any (l: l.port == 443) listen)
&& lib.elem 8443 tcp
&& !(lib.elem 443 tcp)
&& url == "https://${otel.domain}/${otel.producerName}";
}
{
# ../host-modules/glue-controller-env.nix: the controller always exports to
# the swarm's collector. `controllerAlone` runs no collector of its own
@ -195,9 +216,6 @@ let
(env shared.controllerAlone).OTEL_EXPORTER_OTLP_ENDPOINT or null
== "https://${otel.domain}/${otel.producerName}"
&& (env shared.controllerAlone).SWARM_CONTROLLER_OTEL_AUDIENCE or null == clientId
&&
(env controllerAltPort).OTEL_EXPORTER_OTLP_ENDPOINT or null
== "https://${otel.domain}:8443/${otel.producerName}"
&& !(centralToggleOff.systemd.services ? swarm-controller);
}
{

View file

@ -34,17 +34,21 @@ let
# The shared services here, the forge somewhere else. Every derivation in
# ../host-modules/swarm-required-services.nix is `mkDefault`, so this stays
# expressible — and it is also the authelia-without-forge host the OIDC
# client case needs.
# client case needs. Its gateway listens on a non-default TLS port, which no
# URL it renders may carry.
servicesForgeElsewhere = hive {
deploy.allSwarmServices = true;
deploy.forgejo.enable = false;
gateway.httpsPort = 8443;
};
# The forge with neither authelia nor the store, its store identity placed by
# hand: the other half of the split, and the deployment the store fetch
# exists for.
# exists for. Its gateway listens on a non-default TLS port, which the
# forge's own URL may not carry.
forgeAlone = hive {
deploy.forgejo.enable = true;
gateway.httpsPort = 8443;
deploy.bao.forgePublisherClientCertFile = "/etc/pki/forge-publisher.pem";
deploy.bao.forgePublisherClientKeyFile = "/etc/pki/forge-publisher-key.pem";
};
@ -166,6 +170,18 @@ let
servicesForgeElsewhere.services.hyperhive.swarm.forge.sso.redirectUri
== "https://forge.t.local/user/oauth2/authelia/callback";
}
{
# The gateway's TLS port is a listen setting. The first clause is the
# control that the fixture's gateway is on 8443, so the URL arms cannot
# pass on a fixture where it was never set.
name = "the forge's URLs carry no port when the gateway listens on a non-default one";
ok =
forgeAlone.services.hyperhive.gateway.httpsPort == 8443
&&
forgeAlone.containers.hive-forge.config.services.forgejo.settings.server.ROOT_URL
== "https://forge.t.local/"
&& servicesForgeElsewhere.services.hyperhive.gateway.httpsPort == 8443;
}
{
# The runner reaches the forge by name and reads its token with its own
# certificate, so nothing about the host it runs on is asked.

View file

@ -132,14 +132,17 @@ let
# The discovery locations reach the hive domain's vhost from a glue
# module, merged into the vhost hive-c0re declares. The matrix-elsewhere
# arm reads the same vhost, which still exists there with its redirect,
# so its absence of discovery cannot pass on a misspelled name.
# so its absence of discovery cannot pass on a misspelled name. The
# fixture's gateway is on a non-default TLS port, which none of the URLs
# carry.
name = "matrix discovery sits on the hive domain's vhost only where the homeserver runs";
ok =
let
here = matrixNoBaoIdentity.services.nginx.virtualHosts."h1.t.local".locations;
elsewhere = matrixElsewhere.services.nginx.virtualHosts."h1.t.local".locations;
in
lib.hasInfix ''{"m.homeserver":{"base_url":"https://chat.t.local"}}'' (
matrixNoBaoIdentity.services.hyperhive.gateway.httpsPort == 8443
&& lib.hasInfix ''{"m.homeserver":{"base_url":"https://chat.t.local"}}'' (
here."= /.well-known/matrix/client".extraConfig or ""
)
&& lib.hasInfix ''{"m.server":"chat.t.local:443"}'' (

View file

@ -82,7 +82,12 @@ in
controllerAlone = hive { deploy.swarm-controller.enable = true; };
matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; };
# On a non-default gateway TLS port, so the URLs it renders (discovery, the
# hive-domain redirect) show whether they follow it.
matrixNoBaoIdentity = hiveWithController {
deploy.matrix.enable = true;
gateway.httpsPort = 8443;
};
baoWithMatrix = hiveWithController {
deploy.bao.enable = true;