Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/swarm-nats-service.nix
atlas 34e142bb5c hive-matrix: run the homeserver in its own netns, behind a unix socket
tuwunel bound `0.0.0.0:<httpPort>` in the host netns, so the homeserver
port was open on every host address, and the gateway proxied to it on
host loopback.

The container now sets `privateNetwork = true` and hangs off the hive
bridge with a DHCP lease, the same attach the CI runner, builder and
review classifier use (resolvconf on, `networking.nameservers` the bridge
IP). tuwunel listens only on `/run/hive-matrix/tuwunel.sock`, a host
directory (root:nginx 0770) bind-mounted into the container; its primary
group is nginx's gid and the socket is 0660. The gateway's `/_matrix/`
location proxies to the socket.

The static resolv.conf goes: it existed because resolvconf's host
tracking came up empty in the shared netns.

`swarm.matrix.httpPort` and `{swarm,deploy}.matrix.openFirewall` are
removed with a message: there is no host port to choose or open. The
`.well-known/matrix/*` fallback to `<hive>:<httpPort>` is dropped with
them; it only rendered with `gatewayHost = null`, which the homeserver's
SSO assertion already rejects.

Part of #4815.
2026-10-15 09:52:06 +02:00

122 lines
5 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# The swarm's message queue as every hive sees it: where it is reached and
# what it is registered as, identical on every host. What the host running it
# decides, and the container itself, are in ./swarm-nats.nix.
{
lib,
config,
...
}:
let
swarmDomain = config.services.hyperhive.swarm.domain;
# Total on a null swarm domain, for the reason ./swarm-bao-service.nix gives.
domainBase = if swarmDomain == null then "invalid" else swarmDomain;
in
{
options.services.hyperhive.swarm.nats = {
# `enable` is `services.hyperhive.deploy.nats.enable` — see
# ./deploy.nix. What the queue IS to every hive: its domain, ports
# and client id.
# ⚠️ Deliberately NO `package` option for the NATS server, anywhere —
# not here and not under `deploy.nats` either, where every other
# service's build lives. `services.nats` upstream does not expose
# one; it resolves `pkgs.nats-server` itself, so an option would be
# ignored or need an overlay to mean anything, and an option that does
# not control what it names is worse than its absence. Pin the build
# with `nixpkgs.overlays` if you need to. (`deploy.nats.authPackage`
# is a different thing: the callout responder, which IS ours.)
domain = lib.mkOption {
type = lib.types.str;
default = "nats.${domainBase}";
defaultText = lib.literalExpression ''"nats.''${services.hyperhive.swarm.domain}"'';
description = ''
Name every client reaches the queue on, as
`tls://<domain>:<port>`, and the only name its certificate carries.
A **sibling** of the swarm's other service names, for the reason
{option}`services.hyperhive.swarm.bao.domain` gives.
The host running the queue answers it through `gateway.localNames`.
Every other hive resolves it through the operator's upstream DNS,
which is where a multi-host swarm needs a record for it.
Not in {option}`services.hyperhive.swarm.serviceDomains`. The gateway
on the queue's host serves `https://<domain>/metrics` to the swarm
collector, and in self-signed mode it serves the queue's own leaf
there rather than the swarm-services one, so that host never waits
for a services leaf (./swarm-nats.nix).
'';
};
port = lib.mkOption {
type = lib.types.port;
default = 4222;
description = ''
TCP port the queue listens on. 4222 is upstream's default and
sits outside hyperhive's claimed ranges (dashboard 7000, forge
3000, every agent in 8100..8999 via FNV-1a hash).
Opened on the bridge interface, for agent containers, and on
`wg-hive` when this host is on the mesh, for other hives. Never
host-wide. TLS only: a client that does not speak it is refused.
Unlike {option}`monitorPort` and {option}`metricsPort`, the
address is not what bounds who may use this port: the queue's
`auth_callout` refuses every client it cannot identify.
'';
};
monitorPort = lib.mkOption {
type = lib.types.port;
default = 8222;
description = ''
Port NATS serves its **monitoring** endpoint on, bound to
loopback.
Not a metrics endpoint: the server has no Prometheus format of its
own. This serves `/varz`, `/connz`, `/routez` as JSON, and the
exporter below is what translates it — which is why enabling the
exporter without this produces a process that starts cleanly and
scrapes nothing.
⚠️ Loopback, and the exporter is the only intended reader. The
endpoint is unauthenticated and `/connz` names every connected
client, so the address it binds is the whole access control. Do
not widen it, and do not put it behind a gateway vhost expecting
that to add one.
'';
};
metricsPort = lib.mkOption {
type = lib.types.port;
default = 7777;
description = ''
Port the Prometheus exporter serves NATS's metrics on, bound to
loopback. The gateway's `= /metrics` location on
{option}`services.hyperhive.swarm.nats.domain` proxies it, behind
the swarm collector's authelia check.
⚠️ This and {option}`monitorPort` are two more claims on a port
space every swarm container shares — they run in this container but
`privateNetwork = false`, so a collision with any other hyperhive
service is a runtime coin toss over which process gets the port,
with nothing in any log saying so. Both defaults are upstream's
own (`nats-server` 8222, `prometheus-nats-exporter` 7777) and
neither is claimed elsewhere in this repo, checked when they were
added.
'';
};
clientId = lib.mkOption {
type = lib.types.str;
default = "swarm-nats";
description = ''
OAuth2 client id the queue's authentication path identifies
itself with. The host running authelia registers it, whichever
host runs the queue, and the queue's host reads its secret out of
the swarm secret store.
'';
};
};
}