feat(#1933): weekly hive-tls-resign timer — dedicated resign service, no hive-tls-ca bounce
The parked PR (#1934) triggered re-sign by restarting hive-tls-ca (a RemainAfterExit oneshot), which mara flagged as a hack. This replaces that with a dedicated hive-tls-resign.{service,timer}: - hive-tls-ca unchanged — still handles boot-time CA generation + initial leaf signing; RemainAfterExit, runs before the gateway. - hive-tls-resign.service — runs the leaf re-sign directly (openssl in its own PATH); does NOT touch hive-tls-ca. Re-signs only when the leaf is within half its validity of expiry, so the weekly timer catches short-lived (30d default) leaves well before they lapse. Checks sha256 before/after; if the leaf rotated it propagates into the running gateway via systemctl -M hive-gateway (mirroring how hive-c0re reloads the gateway after agents.conf writes — the documented correct approach; an inotify path unit inside the container does not work across the nspawn mount-namespace boundary). || true on the propagation calls so a stopped gateway never fails the unit (its next boot imports the already-rotated leaf). - hive-tls-resign.timer — OnCalendar=weekly, Persistent=true (fires a missed run on next boot so a host that was off on the scheduled day catches up rather than letting the leaf lapse silently). - leafValidityDays doc updated to reflect automatic renewal. Closes #1933.
This commit is contained in:
parent
6c95933775
commit
61bd0b604d
1 changed files with 102 additions and 7 deletions
|
|
@ -73,13 +73,10 @@ in
|
||||||
when it is missing or near expiry; because it shares the CA
|
when it is missing or near expiry; because it shares the CA
|
||||||
anchor, a rotation does not disturb consumer trust. Agents and
|
anchor, a rotation does not disturb consumer trust. Agents and
|
||||||
federation peers validate against the CA, not browser CA/B-forum
|
federation peers validate against the CA, not browser CA/B-forum
|
||||||
limits. NOTE: at this short a window the re-sign must run more
|
limits. The weekly `hive-tls-resign` timer re-signs the leaf once
|
||||||
often than the leaf lifetime. Today `hive-tls-ca` re-signs at
|
it is within half its validity of expiry and propagates the new
|
||||||
service activation (boot/rebuild) only — there is no periodic
|
leaf into the running gateway, so a long-uptime host renews
|
||||||
re-sign timer yet — so a hive that does not reboot within the
|
automatically without a reboot.
|
||||||
validity window would let the leaf expire. Keep that in mind (or
|
|
||||||
add a re-sign timer) when running a hive that stays up for long
|
|
||||||
stretches without a rebuild.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -165,6 +162,104 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Weekly re-sign of the gateway leaf so short-lived leaves renew
|
||||||
|
# without depending on a reboot.
|
||||||
|
#
|
||||||
|
# `hive-tls-ca` only re-signs at service activation (boot/rebuild); a
|
||||||
|
# long-uptime host would otherwise let a 30-day leaf lapse silently.
|
||||||
|
# This service re-signs the leaf directly (not by bouncing hive-tls-ca)
|
||||||
|
# and propagates the new leaf into the running gateway container when
|
||||||
|
# the file actually changed.
|
||||||
|
#
|
||||||
|
# Propagation mechanism: nginx in the gateway container serves a *copy*
|
||||||
|
# of the leaf written by `hive-gateway-self-signed-cert` (which runs at
|
||||||
|
# container start). A host-side `systemctl -M hive-gateway` call
|
||||||
|
# triggers the re-import + reload, mirroring how hive-c0re reloads the
|
||||||
|
# gateway after each agents.conf write. A path unit *inside* the
|
||||||
|
# container was tried first but does not work: IN_MOVED_TO from an
|
||||||
|
# atomic rename on the host does not propagate across the nspawn
|
||||||
|
# mount-namespace boundary.
|
||||||
|
#
|
||||||
|
# `|| true` on propagation so a stopped gateway never fails the unit —
|
||||||
|
# its next boot will import the already-rotated leaf anyway.
|
||||||
|
systemd.services.hive-tls-resign = {
|
||||||
|
description = "Re-sign the gateway TLS leaf and propagate it into the gateway container";
|
||||||
|
path = [
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.systemd
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
UMask = "0077";
|
||||||
|
SyslogIdentifier = "hive-tls-resign";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
set -euo pipefail
|
||||||
|
d=${lib.escapeShellArg cfg.stateDir}
|
||||||
|
ca="$d/ca.pem"
|
||||||
|
cak="$d/ca-key.pem"
|
||||||
|
leaf="$d/gateway.pem"
|
||||||
|
leafk="$d/gateway-key.pem"
|
||||||
|
|
||||||
|
# Re-sign only when the leaf is within half its validity of expiry.
|
||||||
|
# The weekly cadence catches this window well before the leaf lapses.
|
||||||
|
halflife=$(( ${toString cfg.leafValidityDays} * 86400 / 2 ))
|
||||||
|
if [ -s "$leaf" ] && \
|
||||||
|
openssl x509 -in "$leaf" -noout -checkend "$halflife" >/dev/null 2>&1; then
|
||||||
|
echo "gateway leaf valid for more than half its lifetime — no resign needed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "gateway leaf missing or near expiry — re-signing under current CA"
|
||||||
|
before="$(sha256sum "$leaf" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
csr="$(mktemp "$d/gateway.csr.XXXXXX")"
|
||||||
|
ext="$(mktemp "$d/leaf.ext.XXXXXX")"
|
||||||
|
trap 'rm -f "$csr" "$ext"' EXIT
|
||||||
|
|
||||||
|
openssl req -newkey rsa:4096 -nodes -sha256 \
|
||||||
|
-keyout "$leafk" -out "$csr" \
|
||||||
|
-subj "/CN=${domain}"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'subjectAltName=DNS:%s,DNS:forge.%s,DNS:matrix.%s,DNS:*.%s\n' \
|
||||||
|
${lib.escapeShellArg domain} ${lib.escapeShellArg domain} \
|
||||||
|
${lib.escapeShellArg domain} ${lib.escapeShellArg domain}
|
||||||
|
printf 'basicConstraints=critical,CA:FALSE\n'
|
||||||
|
printf 'keyUsage=critical,digitalSignature,keyEncipherment\n'
|
||||||
|
printf 'extendedKeyUsage=serverAuth\n'
|
||||||
|
} > "$ext"
|
||||||
|
|
||||||
|
openssl x509 -req -in "$csr" -CA "$ca" -CAkey "$cak" \
|
||||||
|
-CAcreateserial -days ${toString cfg.leafValidityDays} -sha256 \
|
||||||
|
-extfile "$ext" -out "$leaf"
|
||||||
|
chmod 0600 "$leafk"
|
||||||
|
chmod 0644 "$leaf"
|
||||||
|
|
||||||
|
after="$(sha256sum "$leaf" 2>/dev/null || true)"
|
||||||
|
if [ "$before" != "$after" ]; then
|
||||||
|
echo "gateway leaf rotated — propagating into hive-gateway"
|
||||||
|
systemctl -M hive-gateway restart hive-gateway-self-signed-cert.service || true
|
||||||
|
systemctl -M hive-gateway reload nginx.service || true
|
||||||
|
else
|
||||||
|
echo "gateway leaf unchanged (already up to date)"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.hive-tls-resign = {
|
||||||
|
description = "Weekly gateway-leaf re-sign and propagation";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
# Run weekly; Persistent=true fires a missed run on next boot if
|
||||||
|
# the timer was not active (e.g. the host was off on the scheduled
|
||||||
|
# day), preventing a dormant timer from letting the leaf lapse.
|
||||||
|
OnCalendar = "weekly";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Signal the hive-c0re lifecycle that a hive CA exists: it bind-mounts
|
# Signal the hive-c0re lifecycle that a hive CA exists: it bind-mounts
|
||||||
# this file (read-only, the CA cert ONLY — never the key) into each
|
# this file (read-only, the CA cert ONLY — never the key) into each
|
||||||
# agent container so agents + their tools can trust the gateway's
|
# agent container so agents + their tools can trust the gateway's
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue