diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index 9efcf00f..da205d18 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -159,6 +159,15 @@ let gatewayCfg = config.services.hyperhive.gateway; }; + # `swarmctl --swarm` (and `login`) reach the same gateway as the daemon, + # through the same `reqwest`/`rustls` roots, so the wrapper hands it the + # daemon's bundle. `--set`, not a default: a nix shell exports its own + # `SSL_CERT_FILE`, which would win and lack the swarm's root. The bundle + # holds the system CAs too, so public hosts still verify. + swarmctlTrustEnv = lib.optionalAttrs caTrust.useSelfSigned { + SSL_CERT_FILE = caTrust.bundlePathFor "swarm-controller"; + }; + # The controller's own OAuth2 client. It is NOT a hive: the per-hive # clients the roster issues belong to hives, and the responder's client # belongs to the responder. One identity per principal — the rule is that @@ -257,7 +266,7 @@ let wrapProgram $out/bin/swarmctl ${ lib.concatStringsSep " " ( lib.mapAttrsToList (name: value: "--set ${name} ${lib.escapeShellArg value}") ( - controllerSocketEnv // deployCfg.swarm-controller.swarmctlEnvironment + controllerSocketEnv // swarmctlTrustEnv // deployCfg.swarm-controller.swarmctlEnvironment ) ++ lib.mapAttrsToList ( name: value: "--set-default ${name} ${lib.escapeShellArg value}" diff --git a/nix/module-eval/bao-controller.nix b/nix/module-eval/bao-controller.nix index e74333ec..99fa88b5 100644 --- a/nix/module-eval/bao-controller.nix +++ b/nix/module-eval/bao-controller.nix @@ -146,6 +146,12 @@ let && lib.hasInfix "--set-default SWARMCTL_LOGIN_DEFAULT_SWARM https://t.local" swarmctlWrapperCmd && !(lib.hasInfix "SWARMCTL_SWARM" swarmctlWrapperCmd); } + { + # The gateway's leaf chains to the store's root, which no system store + # holds; without the bundle `login` fails `UnknownIssuer`. + name = "the host's swarmctl trusts the swarm's root through the daemon's bundle"; + ok = lib.hasInfix "--set SSL_CERT_FILE /run/swarm-controller-ca/trust-bundle.pem" swarmctlWrapperCmd; + } { name = "a swarm with no matrix gateway vhost gives the controller no homeserver variable"; ok = diff --git a/swarmctl/README.md b/swarmctl/README.md index f7c0754e..566ab2ac 100644 --- a/swarmctl/README.md +++ b/swarmctl/README.md @@ -189,5 +189,8 @@ refuses it. When it can't be refreshed, any verb says to run `swarmctl login` again. To log out, delete the file. A swarm whose gateway serves a certificate from the swarm's own CA needs -that CA: `--ca-file` or `SWARMCTL_CA_FILE`. The `user` verbs edit the local +that CA. On a controller host the swarm-controller module's wrapper sets +`SSL_CERT_FILE` to the bundle the daemon itself trusts (the system CAs plus +the swarm's root), so nothing needs setting there. Elsewhere, pass the CA +with `--ca-file` or `SWARMCTL_CA_FILE`. The `user` verbs edit the local users file and have no remote form.