From 40557ffb74aaf6d274bffa342f22eae2127bd6a6 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 19 Aug 2026 20:16:16 +0200 Subject: [PATCH] fix(#3527): count the hive half, not the assembled bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard inspected the assembled file for any certificate. The system store always holds certificates, so it passed unconditionally — including in the one case it was written to catch, where the hive CA half contributed nothing. That half is the only one that matters here: every name these consumers verify is issued by our own CA, so a bundle of nothing but public CAs is, for this purpose, an empty bundle that measures as full. The failure is silent and total — the unit reports success and every egress TLS call to a swarm service then fails. Counts the source on its own before assembling, and checks the result carries what both halves brought, so a source truncated between the count and the copy is caught too. Scope is stated at the guard: it proves the anchor was contributed, not that it is usable. A consumer reading only the first certificate ignores it regardless, which is what took the swarm collector down, and no check on this file can see that. Only a handshake can. --- nix/host-modules/lib/hive-ca-trust.nix | 37 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/nix/host-modules/lib/hive-ca-trust.nix b/nix/host-modules/lib/hive-ca-trust.nix index e88065c8..eb0e3744 100644 --- a/nix/host-modules/lib/hive-ca-trust.nix +++ b/nix/host-modules/lib/hive-ca-trust.nix @@ -170,13 +170,40 @@ in set -euo pipefail install -d -m 0755 ${dir} tmp=${bundlePath}.tmp - cat /etc/ssl/certs/ca-certificates.crt ${source} > "$tmp" - # `cat` of an empty or missing-but-mounted source exits 0, so the - # result has to be inspected rather than the command trusted. - if ! grep -q 'BEGIN CERTIFICATE' "$tmp"; then - echo "${unit}: assembled bundle contains no certificate" >&2 + # Count the HIVE half on its own, before assembling. + # + # Inspecting the assembled file cannot work: the system store + # always holds certificates, so "does the result contain a + # certificate" passes unconditionally — including in the one case + # worth catching, where this source contributed nothing. And the + # public CAs are irrelevant to what this bundle is for: every name + # the consumer verifies is issued by our own CA, so a bundle of + # nothing but public CAs is, for this purpose, an empty one that + # measures as full. + # + # `grep -c` exits 1 on zero matches and `set -e` is on, hence the + # `|| true` — without it the guard would die instead of reporting. + contributed=$(grep -c 'BEGIN CERTIFICATE' ${source} || true) + if [ "$contributed" -eq 0 ]; then + echo "${unit}: ${source} contributed no certificate to the bundle" >&2 exit 1 fi + system=$(grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt || true) + cat /etc/ssl/certs/ca-certificates.crt ${source} > "$tmp" + # `cat` of a source truncated between the count and the copy still + # exits 0, so the result is checked against what both halves + # brought rather than merely for being non-empty. + total=$(grep -c 'BEGIN CERTIFICATE' "$tmp" || true) + if [ "$total" -ne $((system + contributed)) ]; then + echo "${unit}: bundle has $total certificates, expected $system + $contributed" >&2 + exit 1 + fi + # ⚠️ Scope: this proves the anchor was CONTRIBUTED, not that it is + # USABLE. A consumer that reads only the first certificate of the + # file ignores it anyway — that is what took the swarm collector + # down — and no check on this file can see it. Only a real + # handshake can. Do not read a green assembly as a working trust + # store. chmod 0644 "$tmp" mv "$tmp" ${bundlePath} '';