diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index da924288..5c5194c1 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -403,22 +403,21 @@ in { system.stateVersion = "26.05"; - # Ensure a valid self-signed cert exists before nginx starts. - # nginx `Requires=` this via `requiredBy`, so systemd refuses - # to start nginx until the script succeeds. ALWAYS runs (no - # ConditionPathExists) and is idempotent — that's necessary - # to reconcile broken state left over from prior failed - # boots (a 0700 dir from a stale UMask, a truncated cert - # from an interrupted oneshot, etc.) which a guarded-on- - # missing-cert script would silently skip and leave broken. - # Cert covers the bare hive domain plus `*.${hyperhiveDomain}` - # so the matrix + forge sub-domains are valid under the same + # Generate a self-signed cert on first boot if missing. nginx + # `Requires=` this via `requiredBy`, so systemd refuses to + # start nginx until the cert exists — `before=` alone wasn't + # enough (it only orders within a single transaction, but + # nginx was being pulled into a different transaction by + # multi-user.target and started without waiting, #856). Cert + # covers the bare hive domain plus `*.${hyperhiveDomain}` so + # the matrix + forge sub-domains are valid under the same # cert. See `docs/gateway.md` ("Self-signed TLS"). systemd.services.hive-gateway-self-signed-cert = lib.mkIf cfg.selfSignedTls { - description = "Ensure self-signed TLS cert for hive-gateway"; + description = "Generate self-signed TLS cert for hive-gateway"; wantedBy = [ "multi-user.target" ]; before = [ "nginx.service" ]; requiredBy = [ "nginx.service" ]; + unitConfig.ConditionPathExists = "!${tlsCert}"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; @@ -439,29 +438,16 @@ in ); in '' - set -eu mkdir -p ${tlsDir} # 0755 dir so nginx (master starts as root but workers - # drop to the nginx user) can traverse to read the cert - # path. Re-applied every boot in case a prior run left - # a tighter mode behind. Key stays 0600 below. + # drop to the nginx user) can read the cert path + # without traversal failures. Key stays 0600 below. chmod 0755 ${tlsDir} - # Generate the cert when EITHER the cert or key is - # missing/empty, OR the cert fails an openssl parse — - # catches truncated / corrupt leftovers from a previous - # interrupted run AND the "cert clean but key absent" - # edge case (argus 🟡 on the first revision) which - # otherwise tripped `chmod 0600 ${tlsKey}` below with - # ENOENT under `set -eu`. The whole oneshot is safe to - # re-run; a healthy cert+key pair is left alone. - if [ ! -s ${tlsCert} ] || [ ! -s ${tlsKey} ] || ! openssl x509 -in ${tlsCert} -noout >/dev/null 2>&1; then - echo "generating fresh self-signed cert at ${tlsCert}" - openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 3650 \ - -keyout ${tlsKey} \ - -out ${tlsCert} \ - -subj "/CN=${subjectCN}" \ - -addext "subjectAltName=${sanLines}" - fi + openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 3650 \ + -keyout ${tlsKey} \ + -out ${tlsCert} \ + -subj "/CN=${subjectCN}" \ + -addext "subjectAltName=${sanLines}" chmod 0600 ${tlsKey} chmod 0644 ${tlsCert} '';