From 5b54b6ddc8a399d09e6c154631f95e05bdaba823 Mon Sep 17 00:00:00 2001 From: atlas Date: Thu, 24 Sep 2026 11:00:20 +0200 Subject: [PATCH] swarm-bao: fail the unit when the services root cannot be read, instead of replacing it A failed `bao read` of the services root made the checkend pipeline non-zero, so the unit deleted a working root and minted a new trust anchor. A failed `bao list` of the issuers likewise looked like an empty mount. Both now fail the unit, which retries on its own restart budget; the root is replaced only when openssl parsed the returned certificate and -checkend said it expires inside a leaf's window. Closes #4663 --- nix/host-modules/swarm-bao.nix | 44 ++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index 43dd4368..49dd41fc 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -1893,11 +1893,11 @@ in # The mount's own issuer list is the only answer that cannot be # stale. # - # `bao list ${servicesPkiMountPath}/issuers` is a 404 (non-zero) on a - # mount with no issuer and a key list once one exists, so its exit - # status IS the question — no output parsing, no error string to - # recognise. The default issuer is what `${servicesPkiMountPath}/issue/` - # signs with, and there is exactly one. + # `bao list` exits 2 both on a mount with no issuer and on a request + # that failed, so its exit status cannot tell them apart. Under + # `-format=json` only the empty mount prints `{}`. The default + # issuer is what `${servicesPkiMountPath}/issue/` signs with, and + # there is exactly one. # # "Never twice" is the rule; "an issuer that cannot issue" is not a # case it was written for. A root with less than a leaf's window left @@ -1907,17 +1907,31 @@ in # the asymmetry: this replaces a root that is already useless, and # still never touches one a leaf can be issued under. regenerate=0 - if ! bao list ${lib.escapeShellArg "${servicesPkiMountPath}/issuers"} >/dev/null 2>&1; then - regenerate=1 - elif ! bao read -field=certificate ${lib.escapeShellArg "${servicesPkiMountPath}/cert/ca"} \ - | openssl x509 -noout -checkend ${toString servicesPkiLeafTtlSeconds} >/dev/null 2>&1; then - echo "the ${servicesPkiMountPath} root cannot outlive a ${servicesPkiLeafTtl} leaf — replacing it" >&2 - # `root` rather than the named issuer: the replacement re-uses - # `issuer_name`, and generating into a name already in use is a - # refusal. Clears the mount's keys with it, which is the whole of - # what it holds — one issuer, by the design above. - bao delete ${lib.escapeShellArg "${servicesPkiMountPath}/root"} >/dev/null + if issuers="$(bao list -format=json ${lib.escapeShellArg "${servicesPkiMountPath}/issuers"})"; then + # A root this unit could not read is not known to be useless, so + # every step before `-checkend` fails the unit, and the unit retries. + if ! root_ca="$(bao read -field=certificate ${lib.escapeShellArg "${servicesPkiMountPath}/cert/ca"})"; then + echo "could not read the ${servicesPkiMountPath} root — leaving it in place" >&2 + exit 1 + fi + if ! openssl x509 -noout <<<"$root_ca" >/dev/null 2>&1; then + echo "the ${servicesPkiMountPath} root that bao returned does not parse — leaving it in place" >&2 + exit 1 + fi + if ! openssl x509 -noout -checkend ${toString servicesPkiLeafTtlSeconds} <<<"$root_ca" >/dev/null 2>&1; then + echo "the ${servicesPkiMountPath} root cannot outlive a ${servicesPkiLeafTtl} leaf — replacing it" >&2 + # `root` rather than the named issuer: the replacement re-uses + # `issuer_name`, and generating into a name already in use is a + # refusal. Clears the mount's keys with it, which is the whole of + # what it holds — one issuer, by the design above. + bao delete ${lib.escapeShellArg "${servicesPkiMountPath}/root"} >/dev/null + regenerate=1 + fi + elif [ "$issuers" = '{}' ]; then regenerate=1 + else + echo "could not list the ${servicesPkiMountPath} issuers — not minting a root over one that may exist" >&2 + exit 1 fi if [ "$regenerate" = 1 ]; then