R-297: installer compares a local golden against the manifest before using it
Step 7 short-circuited on any local golden archive with no version compare, no digest and no warning, so the manifest sha256 was consulted only on the fetch path. Local discovery is newest-by-filename: correct by recency, never by verification. A box could reinstall from a stale archive and come back below the version where the offsite recovery screen exists. Digest first, then the baked controller tag. An auto-discovered mismatch re-fetches the vouched golden; an operator-named mismatch refuses. An unreadable manifest refuses rather than passing. Not published: installer-v1.26.0 is deliberately not cut until a fresh install has been observed taking a stale local golden on drill-r50. Also files R-295..R-298.
This commit is contained in:
@@ -184,7 +184,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="1.25.0" # the SINGLE version source (F-1): -h and the run banners follow it.
|
||||
SCRIPT_VERSION="1.26.0" # the SINGLE version source (F-1): -h and the run banners follow it.
|
||||
# The hub used to carry a copy for its Setup tab; R-94 DELETED it
|
||||
# (2026-08-02) because the hub cannot know which version a box runs —
|
||||
# the Setup command fetches this script at run time. scripts/
|
||||
@@ -228,6 +228,10 @@ HUB_URL="https://hub.felhom.eu"
|
||||
VMID="9201"
|
||||
VMID_EXPLICIT=false # set true when --vmid is given; gates the auto-pick-a-free-vmid behavior
|
||||
GOLDEN_VOLID=""
|
||||
# R-297: true only when the operator named the archive with --golden, so a mismatch REFUSES
|
||||
# (never silently substitutes) rather than re-fetching.
|
||||
GOLDEN_VOLID_EXPLICIT=false
|
||||
GOLDEN_CHECK_WHY=""
|
||||
GOLDEN_VMID="9100"
|
||||
ARCHIVE_STORAGE="local"
|
||||
NODE=""
|
||||
@@ -1230,7 +1234,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--mode) MODE="$2"; shift 2 ;;
|
||||
--hub-url) HUB_URL="$2"; shift 2 ;;
|
||||
--vmid) VMID="$2"; VMID_EXPLICIT=true; shift 2 ;;
|
||||
--golden) GOLDEN_VOLID="$2"; shift 2 ;;
|
||||
--golden) GOLDEN_VOLID="$2"; GOLDEN_VOLID_EXPLICIT=true; shift 2 ;;
|
||||
--golden-vmid) GOLDEN_VMID="$2"; shift 2 ;;
|
||||
--archive-storage) ARCHIVE_STORAGE="$2"; shift 2 ;;
|
||||
--node) NODE="$2"; NODE_EXPLICIT=true; shift 2 ;;
|
||||
@@ -2642,12 +2646,81 @@ PY
|
||||
# Local auto-discovery is the default + fallback. When no local golden exists (or --force-gitea-golden),
|
||||
# fetch the golden from Gitea (git token), VERIFY its sha256 against the hub manifest, and import it
|
||||
# into the archive storage's dump dir under a valid vzdump name so the provision restore can use it.
|
||||
# R-297 — A LOCAL GOLDEN IS NOT A VOUCHED GOLDEN UNTIL IT HAS BEEN COMPARED TO THE MANIFEST.
|
||||
#
|
||||
# Step 7 used to short-circuit on ANY local archive: no version compare, no digest, no warning. The
|
||||
# manifest's sha256 — whose whole purpose is to vouch from a different trust root than the code host
|
||||
# — was consulted only on the fetch path. Local discovery is `sort | tail -1`: correct by RECENCY,
|
||||
# never by verification. A box with an old archive lying around therefore reinstalled from it
|
||||
# silently, and could come back BELOW the version where the off-site recovery screen exists — unable
|
||||
# to run the ceremony its own data depends on, and born below the update floor.
|
||||
#
|
||||
# Two comparisons, cheapest first:
|
||||
# 1. DIGEST — if the local file's sha256 equals the manifest's, it IS the vouched artifact. Certain.
|
||||
# 2. VERSION — a locally BAKED golden is not byte-identical to the published one, so a digest
|
||||
# mismatch is not by itself proof of staleness. The controller version baked into the archive
|
||||
# (/etc/felhom-controller-image, the tag the bootstrap unit runs) is then compared to the
|
||||
# manifest's vouched version.
|
||||
# Neither matching ⇒ the archive is not what the operator approved.
|
||||
#
|
||||
# Reading the marker streams the archive until the entry is found (--occurrence=1 stops there), which
|
||||
# is why the digest is tried first: on a previously-FETCHED golden the cheap check settles it.
|
||||
golden_local_matches_manifest() {
|
||||
local volid="$1" path want_sha got_sha marker ver
|
||||
path=$(pvesm path "$volid" 2>/dev/null)
|
||||
if [[ -z "$path" || ! -f "$path" ]]; then
|
||||
GOLDEN_CHECK_WHY="the archive could not be resolved to a file on disk"
|
||||
return 1
|
||||
fi
|
||||
want_sha="$ART_GOLDEN_SHA"
|
||||
if [[ -z "$ART_GOLDEN_VER" || -z "$want_sha" ]]; then
|
||||
# We could not look. That must never read as "it is fine" — the whole finding is a stale
|
||||
# archive being taken on trust.
|
||||
GOLDEN_CHECK_WHY="the hub manifest has no vouched golden version/sha256 to compare against"
|
||||
return 1
|
||||
fi
|
||||
got_sha=$(sha256sum "$path" 2>/dev/null | awk '{print $1}')
|
||||
if [[ -n "$got_sha" && "$got_sha" == "$want_sha" ]]; then
|
||||
log_info " local golden digest matches the manifest (${got_sha:0:16}…) — this IS the vouched artifact"
|
||||
return 0
|
||||
fi
|
||||
marker=$(tar --zstd -xOf "$path" --occurrence=1 ./etc/felhom-controller-image 2>/dev/null | tr -d '[:space:]')
|
||||
ver="${marker##*:}"
|
||||
if [[ -z "$ver" ]]; then
|
||||
GOLDEN_CHECK_WHY="its digest does not match the manifest and it carries no readable controller version marker"
|
||||
return 1
|
||||
fi
|
||||
if [[ "$ver" == "$ART_GOLDEN_VER" ]]; then
|
||||
log_info " local golden is controller $ver — the vouched version (digest differs: locally baked, not the published file)"
|
||||
return 0
|
||||
fi
|
||||
GOLDEN_CHECK_WHY="it is controller $ver, but the vouched golden is $ART_GOLDEN_VER"
|
||||
return 1
|
||||
}
|
||||
|
||||
step_golden() {
|
||||
log_step "7/8 golden archive"
|
||||
|
||||
if [[ -n "$GOLDEN_VOLID" ]] && ! $FORCE_GITEA_GOLDEN; then
|
||||
log_skip " using local golden: $GOLDEN_VOLID"
|
||||
_state_mark golden; return 0
|
||||
# The manifest is needed to compare; resolve it here rather than trusting the archive.
|
||||
# resolve_artifacts dies with its own message if the manifest cannot be read. That is
|
||||
# deliberate: without a vouched version there is nothing to compare an archive against, and
|
||||
# "we could not look" must not resolve to "install whatever is lying around".
|
||||
[[ -n "$ART_GOLDEN_VER" ]] || resolve_artifacts
|
||||
GOLDEN_CHECK_WHY=""
|
||||
if golden_local_matches_manifest "$GOLDEN_VOLID"; then
|
||||
log_skip " using local golden: $GOLDEN_VOLID"
|
||||
_state_mark golden; return 0
|
||||
fi
|
||||
if $GOLDEN_VOLID_EXPLICIT; then
|
||||
# The operator named this archive. Never silently substitute a different one.
|
||||
die "refusing the golden you named ($GOLDEN_VOLID): ${GOLDEN_CHECK_WHY}.
|
||||
The vouched golden is ${ART_GOLDEN_VER:-<unknown>}. Either pass the archive that matches it,
|
||||
or re-run with --force-gitea-golden to fetch the vouched one from Gitea."
|
||||
fi
|
||||
log_warn " ignoring the local golden $GOLDEN_VOLID — ${GOLDEN_CHECK_WHY}"
|
||||
log_warn " fetching the vouched golden instead (this is what the manifest is for)"
|
||||
GOLDEN_VOLID=""
|
||||
fi
|
||||
|
||||
# Need the manifest + git creds (already resolved in step 5, but re-resolve on a fresh --resume run).
|
||||
|
||||
Reference in New Issue
Block a user