diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index c3d6034b..248695f6 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -14,13 +14,10 @@ let # Same runtime→build-time bridge hive-ci and hive-forge already cross: # binds the hive trust bundle (which folds in the swarm root) into the # container and orders the container after `hive-tls-ca.service`. The - # *consumption* is per-runtime and stays here — see the bundle service - # in the container config below. + # assembled bundle itself comes from `caTrust.trustBundle`, imported in + # the container config below. caTrust = import ./lib/hive-ca-trust.nix { inherit lib tlsCfg gatewayCfg; }; - useSelfSigned = caTrust.useSelfSigned; - # tuwunel's own combined bundle, assembled at start. /run is tmpfs, so - # it is rebuilt from the current CA every boot rather than going stale. - matrixCaBundle = "/run/hive-matrix-ca/ca-bundle.crt"; + swarmDomain = config.services.hyperhive.swarm.domain; # `url` is the half of the authelia module that exists on EVERY hive — @@ -790,6 +787,24 @@ in config = { ... }: { + imports = [ + # tuwunel's rustls verifier resolves through `rustls-native-certs` + # → `openssl-probe`, which reads `SSL_CERT_FILE` — so the + # openssl-shaped variable is the lever despite tuwunel linking no + # openssl. + # + # ⚠️ The helper CONCATENATES, and that is load-bearing here beyond + # the usual reason: `SSL_CERT_FILE` replaces the default location, + # so naming the hive anchor alone would drop every public CA and + # break federation with the wider matrix network — trading a small + # outage for a much larger one. + (caTrust.trustBundle { + inherit pkgs; + name = cfg.machine; + consumers = [ "tuwunel" ]; + }) + ]; + system.stateVersion = "26.05"; # Shared host netns: this container's own firewall.service @@ -933,41 +948,6 @@ in # the unit's start — a credentials path does. ++ lib.optional cfg.sso.enable "oidc_client_secret:${toString cfg.sso.clientSecretFile}"; - # Federation TLS against a peer whose cert chains to the swarm - # root: tuwunel's outbound client is reqwest with the `rustls` - # feature, which builds a `rustls_platform_verifier::Verifier` - # and — because tuwunel calls `tls_certs_merge` rather than - # `tls_certs_only` — keeps the platform roots alongside its - # compiled-in webpki set. On Linux that verifier resolves through - # `rustls-native-certs` → `openssl-probe`, which reads - # `SSL_CERT_FILE`. So the openssl-shaped variable IS the lever - # here, despite tuwunel linking no openssl. - # - # ⚠️ CONCATENATE, never point at the anchor alone. `openssl-probe` - # uses `SSL_CERT_FILE` *instead of* the default location, so - # naming just the hive bundle would drop every public CA and - # break federation with the wider matrix network — trading a - # small outage for a much larger one. - systemd.services.hive-matrix-ca-bundle = lib.mkIf useSelfSigned { - description = "assemble tuwunel TLS trust bundle (system CAs + hive CA)"; - wantedBy = [ "tuwunel.service" ]; - before = [ "tuwunel.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - SyslogIdentifier = "hive-matrix-ca-bundle"; - }; - path = [ pkgs.coreutils ]; - script = '' - set -euo pipefail - install -d -m 0755 /run/hive-matrix-ca - cat /etc/ssl/certs/ca-certificates.crt ${caTrust.caContainerPath} \ - > ${matrixCaBundle} - chmod 0644 ${matrixCaBundle} - ''; - }; - systemd.services.tuwunel.environment.SSL_CERT_FILE = lib.mkIf useSelfSigned matrixCaBundle; - environment.systemPackages = [ cfg.package ]; }; }; diff --git a/nix/host-modules/swarm-grafana.nix b/nix/host-modules/swarm-grafana.nix index 42a0f2ce..878f4b1f 100644 --- a/nix/host-modules/swarm-grafana.nix +++ b/nix/host-modules/swarm-grafana.nix @@ -23,7 +23,6 @@ let swarmDomain = hyperhiveCfg.swarm.domain; caTrust = import ./lib/hive-ca-trust.nix { inherit lib tlsCfg gatewayCfg; }; - useSelfSigned = caTrust.useSelfSigned; # Total on a null swarm domain for the same reason every sibling module is: # the required-domain assertion in hive-network.nix should be what an @@ -61,7 +60,6 @@ let # the authelia client and Grafana itself. redirectUri = "https://${cfg.domain}/login/generic_oauth"; - grafanaCaBundle = "/run/swarm-grafana-ca/ca-bundle.crt"; in { options.services.hyperhive.swarm.grafana = { @@ -357,6 +355,16 @@ in inherit (networkCfg) bridgeIp; dnsConsumers = [ "grafana.service" ]; }) + # Grafana is Go, and `SSL_CERT_FILE` *replaces* the default store + # rather than adding to it — so it needs the system CAs and the + # hive CA concatenated, not the CA alone. Without it the browser + # half of the login succeeds and the server-to-server token + # exchange fails `x509: unknown authority`. + (caTrust.trustBundle { + inherit pkgs; + name = cfg.machine; + consumers = [ "grafana" ]; + }) ]; system.stateVersion = "26.05"; @@ -371,37 +379,6 @@ in # the boundary after start. networking.resolvconf.enable = lib.mkForce false; - # Self-signed mode: Grafana is Go, and Go's `SSL_CERT_FILE` - # *replaces* the default bundle rather than adding to it — so - # concatenate the system CAs with the bind-mounted hive CA instead - # of pointing at the CA alone, which would lose every public - # anchor. /run is tmpfs, so this is rebuilt from the current CA - # each boot rather than going stale. - # - # Without it the browser half of the login succeeds and the - # server-to-server token exchange fails with an x509 "unknown - # authority" — the same shape of failure the swarm queue hit. - systemd.services.swarm-grafana-ca-bundle = lib.mkIf useSelfSigned { - description = "assemble Grafana TLS trust bundle (system CAs + hive CA)"; - wantedBy = [ "grafana.service" ]; - before = [ "grafana.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - SyslogIdentifier = "swarm-grafana-ca-bundle"; - }; - path = [ pkgs.coreutils ]; - script = '' - set -euo pipefail - install -d -m 0755 /run/swarm-grafana-ca - cat /etc/ssl/certs/ca-certificates.crt ${caTrust.caContainerPath} \ - > ${grafanaCaBundle} - chmod 0644 ${grafanaCaBundle} - ''; - }; - - systemd.services.grafana.environment.SSL_CERT_FILE = lib.mkIf useSelfSigned grafanaCaBundle; - # Grafana's `secret_key` has **no default in nixpkgs** and an # assertion refuses the build without one — which is how the first # version of this module broke a deploy. It signs the datasource