diff --git a/nix/host-modules/hive-tls.nix b/nix/host-modules/hive-tls.nix index b3e3abe4..6da7b095 100644 --- a/nix/host-modules/hive-tls.nix +++ b/nix/host-modules/hive-tls.nix @@ -255,13 +255,26 @@ in # instances of the `container@.service` template. before = [ "container@hive-gateway.service" ]; requiredBy = [ "container@hive-gateway.service" ]; - # The issuance below needs the swarm root key on disk. When this - # host generates it (single-host swarm) that unit must have run; - # when the operator provides it there is no unit to wait for, so - # the dependency is conditional rather than a unit that exists and - # does nothing. - after = lib.optional swarmCaCfg.autoConfigure "swarm-ca.service"; - requires = lib.optional swarmCaCfg.autoConfigure "swarm-ca.service"; + # The issuance below needs the swarm root key on disk, and (for the + # services leaf) the services sub-CA it signs under. When this host + # generates them (single-host swarm) both units must have run first; + # when the operator provides the material there is no unit to wait + # for, so the dependency is conditional rather than a unit that + # exists and does nothing. + # + # Without waiting for swarm-services-ca specifically, this unit races + # it: if hive-tls-ca finishes first, it finds no services-ca.pem yet, + # silently skips signing the services leaf (the same as "operator + # hasn't set one up"), and the gateway comes up with a vhost pointed + # at a cert that was never written. + after = lib.optionals swarmCaCfg.autoConfigure [ + "swarm-ca.service" + "swarm-services-ca.service" + ]; + requires = lib.optionals swarmCaCfg.autoConfigure [ + "swarm-ca.service" + "swarm-services-ca.service" + ]; path = [ pkgs.openssl ]; serviceConfig = { Type = "oneshot";