diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index 64f17c38..20711fb0 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -52,6 +52,13 @@ let # `nix/module-eval/grafana.nix` pins it against the shipped dashboard file. collectorServiceName = "bao"; + # OpenBao's start job limit inside the container, and the container's own, + # which is the same plus a margin: the container is `Type=notify`, so its + # start job spans OpenBao's and must outlast it, or the container's limit + # kills a start the unit would still finish. + openbaoStartSec = 240; + containerStartSec = openbaoStartSec + 60; + # Upstream's own default, kept so its documentation matches. The raft data # lives INSIDE the container on `ephemeral = false`, the same way # ./swarm-grafana.nix keeps its sqlite database — no sibling service binds @@ -4138,7 +4145,7 @@ in # The container is `Type=notify`, so its start job waits for OpenBao, # whose first start after a restart takes 60-144 s opening the raft # storage. The 1min default kills it mid-open and every retry is cold. - timeoutStartSec = "5min"; + timeoutStartSec = "${toString containerStartSec}s"; inherit privateNetwork; @@ -4395,31 +4402,36 @@ in # attrset literal and does not parse. Module merging happens across # `config` blocks, not within a literal — so the seal's half joins # with `optionalAttrs`. - systemd.services.openbao.serviceConfig = - lib.optionalAttrs haveServerTls { - LoadCredential = [ "${serverKeyCredential}:${serverKeyPath}" ]; - } - # The PIN reaches openbao as an environment variable read from a - # 0400 file the provisioning unit wrote — never as a value in this - # expression, which would render it world-readable into the store. - # `TPM2_PKCS11_STORE` is required because the store is not at the - # library's default location. - // lib.optionalAttrs (baoDeploy.seal == "pkcs11") { - EnvironmentFile = pinEnvFile; - Environment = [ "TPM2_PKCS11_STORE=${tokenStoreDir}" ]; - # `DynamicUser` implies `ProtectSystem=strict`, which leaves the - # bind mount read-only to this unit however it is owned, and the - # pkcs11 library opens its sqlite store read-write. - ReadWritePaths = [ tokenStoreDir ]; - # Two groups, two different jobs: the store's sqlite file, and - # the TPM device the seal opens through it. `CapabilityBounding - # Set=` is empty upstream, so there is no `CAP_DAC_OVERRIDE` to - # fall back on — group membership is the only way in. - SupplementaryGroups = [ - tokenGroup - tpmGroup - ]; - }; + systemd.services.openbao.serviceConfig = { + # Upstream sets no start limit, so the 90 s default cut off + # the 60-144 s raft open; `Restart=on-failure` upstream then + # restarts it cold. + TimeoutStartSec = openbaoStartSec; + } + // lib.optionalAttrs haveServerTls { + LoadCredential = [ "${serverKeyCredential}:${serverKeyPath}" ]; + } + # The PIN reaches openbao as an environment variable read from a + # 0400 file the provisioning unit wrote — never as a value in this + # expression, which would render it world-readable into the store. + # `TPM2_PKCS11_STORE` is required because the store is not at the + # library's default location. + // lib.optionalAttrs (baoDeploy.seal == "pkcs11") { + EnvironmentFile = pinEnvFile; + Environment = [ "TPM2_PKCS11_STORE=${tokenStoreDir}" ]; + # `DynamicUser` implies `ProtectSystem=strict`, which leaves the + # bind mount read-only to this unit however it is owned, and the + # pkcs11 library opens its sqlite store read-write. + ReadWritePaths = [ tokenStoreDir ]; + # Two groups, two different jobs: the store's sqlite file, and + # the TPM device the seal opens through it. `CapabilityBounding + # Set=` is empty upstream, so there is no `CAP_DAC_OVERRIDE` to + # fall back on — group membership is the only way in. + SupplementaryGroups = [ + tokenGroup + tpmGroup + ]; + }; # ⚠️ Upstream sets `restartIfChanged = false` on this unit: a restart # SEALS the store and disconnects every client. The container around diff --git a/nix/module-eval/hive-tls.nix b/nix/module-eval/hive-tls.nix index ca857561..92a95f61 100644 --- a/nix/module-eval/hive-tls.nix +++ b/nix/module-eval/hive-tls.nix @@ -853,10 +853,19 @@ let ) names; } { - # The container is `Type=notify`: its start job lasts as long as - # OpenBao's first start, which a 1min limit cuts off mid-open. - name = "the store's container is given five minutes to start"; - ok = lib.hasInfix "TimeoutStartSec=5min" allLocal.systemd.units."container@swarm-bao.service".text; + # The container is `Type=notify`: its start job spans OpenBao's first + # start, so OpenBao's own limit is raised too and sits a minute below + # the container's. `Restart=` comes from upstream, so it shows the + # lookup reaches the real unit. + name = "openbao's start limit is raised and stays below its container's"; + ok = + let + inner = allLocal.containers.swarm-bao.config.systemd.services.openbao.serviceConfig; + unit = allLocal.systemd.units."container@swarm-bao.service".text; + in + inner.Restart == "on-failure" + && inner.TimeoutStartSec == 240 + && lib.hasInfix "TimeoutStartSec=300s" unit; } { name = "hive-priv is told the cache's address by the host's configuration";