Watch
0
0
Fork
You've already forked hyperhive
0

matrix apiUrl: default to https on the gateway vhost

hive-c0re logs in with the hive's matrix sender token and creates the
Space and rooms over swarm.matrix.apiUrl. On a hive that does not run
the homeserver that crosses the network, so the default is now
https://<gatewayHost>. hive-c0re already verifies against the hive trust
bundle (SSL_CERT_FILE: system CAs, swarm root and the store's services
root), which covers the gateway's leaf for that name.

Also drops the remaining loopback-default wording in hive-c0re's matrix
module and the openFirewall description, and pins the https default on a
hive host without the homeserver in module-eval-matrix-core.
This commit is contained in:
atlas 2026-10-04 18:13:47 +02:00
commit 55a67e38fa
7 changed files with 56 additions and 35 deletions

View file

@ -83,11 +83,18 @@ pin the value before the homeserver mints any ids under the default.
## Default-closed firewall ## Default-closed firewall
`deploy.matrix.openFirewall` defaults to `false`: hive-c0re and agent `deploy.matrix.openFirewall` defaults to `false`: hive-c0re and agent
containers on every hive reach the homeserver at `chat.<swarm-domain>` containers reach the homeserver at `chat.<swarm-domain>` via the gateway,
via the gateway, so the firewall hole only matters for direct access to so the firewall hole only matters for direct access to `httpPort` from
`httpPort` from *outside* the host. Flip to `true` when *outside* the host. Flip to `true` when announcing the homeserver to
announcing the homeserver to other hives or when an external matrix other hives or when an external matrix client needs to reach the
client needs to reach the client-server API directly. client-server API directly.
hive-c0re provisions matrix (its own sender login, the hive Space and
chat room, invites) through `swarm.matrix.apiUrl`, which defaults to
`https://<swarm.matrix.gatewayHost>` on every hive. That login carries
the hive's matrix access token, and on a hive that doesn't run the
homeserver it crosses the network, so the default is TLS; hive-c0re
verifies the gateway's certificate against the hive trust bundle.
Federation port 8448 is intentionally not opened here — tuwunel Federation port 8448 is intentionally not opened here — tuwunel
serves the federation API on the same `httpPort` as client-server serves the federation API on the same `httpPort` as client-server

View file

@ -21,8 +21,8 @@ use reqwest::StatusCode;
/// fallback: `localhost:8008` is right only when the homeserver happens to /// fallback: `localhost:8008` is right only when the homeserver happens to
/// share this daemon's netns, and an address baked into the binary is one /// share this daemon's netns, and an address baked into the binary is one
/// that builds fine and then talks to the wrong machine. The nix module /// that builds fine and then talks to the wrong machine. The nix module
/// supplies the loopback address when it is itself the thing running /// defaults it to `https://` on the homeserver's gateway vhost, on every
/// tuwunel, where it is not a guess but a fact about what it just started. /// hive whichever host runs tuwunel.
/// ///
/// Also not an agent-facing address either way. An agent has its own netns; /// Also not an agent-facing address either way. An agent has its own netns;
/// agents are handed the gateway vhost via `HIVE_MATRIX_URL`, and get /// agents are handed the gateway vhost via `HIVE_MATRIX_URL`, and get
@ -126,12 +126,9 @@ pub fn hive_chat_room_id_path() -> PathBuf {
/// Whether this hive has a homeserver to provision against. /// Whether this hive has a homeserver to provision against.
/// ///
/// **A configured API URL, not a local container.** It used to scan /// **A configured API URL, not a local container**: whether the homeserver
/// `nixos-container list` for `hive-matrix`, which answers a different /// runs on this host is a different question, and a remote homeserver is
/// question — "is the homeserver a container on this host" — and so made a /// provisioned through the same URL.
/// remote homeserver silently no-op no matter how it was addressed. The
/// nix module still supplies the loopback URL whenever it runs tuwunel
/// itself, so a co-located hive behaves exactly as before.
#[must_use] #[must_use]
pub fn is_present() -> bool { pub fn is_present() -> bool {
matrix_http().is_some() matrix_http().is_some()

View file

@ -126,15 +126,12 @@ in
imports = [ imports = [
./options.nix ./options.nix
./theme.nix ./theme.nix
# Hive-CA trust for this daemon's outbound TLS. Nothing it is given by # Hive-CA trust for this daemon's outbound TLS. The matrix provisioning
# default is https — the forge and matrix URLs are plain http, and the # URL (`swarm.matrix.apiUrl`) defaults to https on the homeserver's
# queue's TLS is verified against `HIVE_C0RE_OIDC_CA_FILE` # gateway vhost, which serves a leaf chaining to the store's services
# (../hive-tls.nix), not this bundle — so this # root or the swarm root — CAs generated at runtime, which no
# changes nothing on an all-local hive. It matters for the split-host # build-time trust store can contain. The queue's TLS is verified
# shape the options invite: `swarm.matrix.apiUrl`'s own example is # against `HIVE_C0RE_OIDC_CA_FILE` (../hive-tls.nix), not this bundle.
# `https://matrix.example.com`, and pointing it at another hive's
# gateway means verifying a leaf signed by a CA generated at runtime,
# which no build-time trust store can contain.
# #
# ⚠️ `SSL_CERT_FILE` REPLACES the trust store, and unlike the other # ⚠️ `SSL_CERT_FILE` REPLACES the trust store, and unlike the other
# consumers of this helper, hive-c0re already makes a *public*-TLS call: # consumers of this helper, hive-c0re already makes a *public*-TLS call:

View file

@ -181,9 +181,9 @@ in
# Client-server API base hive-c0re uses to provision matrix (register # Client-server API base hive-c0re uses to provision matrix (register
# agent users, create the hive space + chat room, invite members). # agent users, create the hive space + chat room, invite members).
# Supplied by `services.hyperhive.swarm.matrix.apiUrl`, which defaults to # Supplied by `services.hyperhive.swarm.matrix.apiUrl`, which defaults to
# the same gateway vhost as HIVE_MATRIX_URL above but is its own option, # the same gateway vhost as HIVE_MATRIX_URL above, over https because it
# so an operator can point provisioning elsewhere without changing what # carries the hive's matrix token and may cross the network. Its own
# agents are handed. # option, so provisioning can point elsewhere without changing agents.
HIVE_MATRIX_API_URL = config.services.hyperhive.swarm.matrix.apiUrl; HIVE_MATRIX_API_URL = config.services.hyperhive.swarm.matrix.apiUrl;
} }
// { // {

View file

@ -64,10 +64,10 @@ in
apiUrl = lib.mkOption { apiUrl = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = if cfg.gatewayHost == null then null else "http://${cfg.gatewayHost}"; default = if cfg.gatewayHost == null then null else "https://${cfg.gatewayHost}";
defaultText = lib.literalExpression '' defaultText = lib.literalExpression ''
if services.hyperhive.swarm.matrix.gatewayHost == null then null if services.hyperhive.swarm.matrix.gatewayHost == null then null
else "http://''${services.hyperhive.swarm.matrix.gatewayHost}" else "https://''${services.hyperhive.swarm.matrix.gatewayHost}"
''; '';
example = "https://matrix.example.com"; example = "https://matrix.example.com";
description = '' description = ''
@ -77,10 +77,14 @@ in
`hyperhive.matrix.url`, which is the gateway vhost handed to `hyperhive.matrix.url`, which is the gateway vhost handed to
each agent's `hive-matrix-daemon`. each agent's `hive-matrix-daemon`.
Defaults to the homeserver's gateway vhost (`gatewayHost`), the Defaults to `https://` on the homeserver's gateway vhost
same name on every hive whichever host runs tuwunel, and `null` (`gatewayHost`), the same name on every hive whichever host runs
when there is no vhost. Set it explicitly when the homeserver is tuwunel, and `null` when there is no vhost. hive-c0re logs in
reachable only some other way. with the hive's matrix sender token over this URL, so it crosses
the network whenever hive-c0re is not on the homeserver's host:
an `http://` value sends that token in cleartext. hive-c0re
verifies the gateway's certificate against the hive trust bundle
(system CAs plus the swarm and services roots).
`null` means hive-c0re has no homeserver to provision against `null` means hive-c0re has no homeserver to provision against
and matrix provisioning no-ops. There is deliberately no and matrix provisioning no-ops. There is deliberately no

View file

@ -431,10 +431,10 @@ in
example = true; example = true;
description = '' description = ''
Open `httpPort` in the host firewall. Off by default Open `httpPort` in the host firewall. Off by default
(secure-by-default): the host reaches the homeserver on (secure-by-default): hive-c0re and agent containers reach the
loopback, and agent containers reach it at `gatewayHost`'s vhost homeserver at `gatewayHost`'s vhost (`chat.<swarm-domain>` by
(`chat.<swarm-domain>` by default) via the gateway — so the firewall open only matters for default) via the gateway, so the open port only matters for
access from outside the host. Flip to `true` when announcing direct access to `httpPort` from outside the host. Flip to `true` when announcing
the homeserver to other hives or when an external matrix the homeserver to other hives or when an external matrix
client needs to reach the client-server API directly. client needs to reach the client-server API directly.

View file

@ -31,6 +31,10 @@ let
# The same hive with the identity taken away, which separates "a homeserver # The same hive with the identity taken away, which separates "a homeserver
# is deployed" from "this host can authenticate to the store". # is deployed" from "this host can authenticate to the store".
matrixNoBaoIdentity = hive { deploy.matrix.enable = true; }; matrixNoBaoIdentity = hive { deploy.matrix.enable = true; };
# A hive host that does not run the homeserver: its hive-c0re reaches the
# homeserver across the network.
matrixElsewhere = hive { };
cases = [ cases = [
{ {
# Absence arm, and what makes the one above able to fail: with no leaf # Absence arm, and what makes the one above able to fail: with no leaf
@ -74,6 +78,18 @@ let
&& !(s.hive-c0re.environment ? BAO_ADDR) && !(s.hive-c0re.environment ? BAO_ADDR)
&& !(lib.any (c: lib.hasPrefix "bao-" c) s.hive-c0re.serviceConfig.LoadCredential); && !(lib.any (c: lib.hasPrefix "bao-" c) s.hive-c0re.serviceConfig.LoadCredential);
} }
{
# hive-c0re logs in with the hive's matrix token over this URL, and on a
# host without the homeserver that login crosses the network, so the
# default must be TLS all the way to the daemon's environment.
name = "hive-c0re provisions matrix over https from a hive that does not run it";
ok =
!matrixElsewhere.services.hyperhive.deploy.matrix.enable
&& matrixElsewhere.services.hyperhive.swarm.matrix.apiUrl == "https://chat.t.local"
&&
matrixElsewhere.systemd.services.hive-c0re.environment.HIVE_MATRIX_API_URL
== "https://chat.t.local";
}
]; ];
in in
runGroup "matrix-core" cases runGroup "matrix-core" cases