FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(update): documented and proved the bootstrap for older hardware · bauer-group/XPD-RPIImage@fe9926c · GitHub

Repository navigation

Commit fe9926c

Browse files
committed
feat(update): documented and proved the bootstrap for older hardware
A unit flashed before this mechanism existed refuses every bundle, and correctly so: it has no trust directory, no updater, and none of the identity the safety checks read. With only a handful of devices in the field a one-time manual bootstrap is preferable to a subcommand, so this documents the procedure - and exercises it, because an untested recovery procedure is a guess written down. The release now publishes bgrpiimage-update and the trust keys as assets alongside bgrpiimage-setup. Public keys are public by definition, and publishing them beside the bundle means an old device is bootstrapped from the same release URL as everything else rather than from a raw.githubusercontent path nobody can verify at a glance. The documented base-image hash is not a guess either: it is the value every release since v0.5.0 - the switch to Raspberry Pi OS Lite - has been built on, verified unchanged across v0.5.0, v0.6.0, v0.7.0, v0.7.7, v0.8.0 and v0.11.1. A device reporting v0.5.0 or newer is provably on that base. Anything older is on a different OS and must be reflashed, which is the whole point of the check, so the note says so rather than leaving it implied. tests/test-update.sh gains a section that builds a device looking like one flashed at v0.7.7 - no updater, no trust store, a release file with neither the base hash nor the contract marker - then runs the documented curl commands verbatim against the mock release and asserts the result: the update applies, the signature verifies against the freshly fetched key, the configuration version is recorded, and the originally flashed version is still reported. The commands in the docs are the commands the test runs. 33 assertions, all green.
1 parent d418865 commit fe9926c

3 files changed

Lines changed: 120 additions & 6 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,19 @@ jobs:
738738
# It is variant-independent, so one copy serves every image.
739739
run: |
740740
set -euo pipefail
741-
src=src/modules/bgrpiimage-base/filesystem/root/usr/local/sbin/bgrpiimage-setup
742-
install -m 0755 "$src" release-assets/bgrpiimage-setup
743-
( cd release-assets && sha256sum bgrpiimage-setup > bgrpiimage-setup.sha256 )
744-
sha256sum release-assets/bgrpiimage-setup
741+
base=src/modules/bgrpiimage-base/filesystem/root
742+
install -m 0755 "$base/usr/local/sbin/bgrpiimage-setup" release-assets/bgrpiimage-setup
743+
install -m 0755 "$base/usr/local/sbin/bgrpiimage-update" release-assets/bgrpiimage-update
744+
# The trust keys too. They are public by definition, and publishing
745+
# them beside the bundle means a device from before signing can be
746+
# bootstrapped from the same release URL as everything else, instead
747+
# of needing a raw.githubusercontent path nobody can verify at a
748+
# glance.
749+
install -m 0644 "$base"/usr/share/bgrpiimage/trusted-keys.d/*.pub release-assets/
750+
( cd release-assets \
751+
&& sha256sum bgrpiimage-setup > bgrpiimage-setup.sha256 \
752+
&& sha256sum bgrpiimage-update > bgrpiimage-update.sha256 )
753+
sha256sum release-assets/bgrpiimage-setup release-assets/bgrpiimage-update
745754
746755
# The GitHub release itself is created by release.yml (semantic-release)
747756
# when the version is bumped on main. Here we only *append* the image
@@ -757,7 +766,9 @@ jobs:
757766
release-assets/*.manifest.json
758767
release-assets/*.confbundle.tar.gz
759768
release-assets/*.bundle.manifest.json.sig
769+
release-assets/*.pub
760770
release-assets/bgrpiimage-setup
771+
release-assets/bgrpiimage-update
761772
fail_on_unmatched_files: true
762773
append_body: true
763774
body: |

‎docs/post-flash-setup.md‎

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,56 @@ dependency of `ca-certificates` — a signature format needing a package the
488488
device cannot install without an update would be circular.
489489
490490
> **Images from before signing carry no trust directory** and will refuse every
491-
> bundle, saying so explicitly. Those devices need one reflash to a signed
492-
> image; after that they update in place like the rest.
491+
> bundle, saying so explicitly. They can be brought up by hand once — see
492+
> below — or reflashed.
493+
494+
### Bootstrapping a device from before in-place updates
495+
496+
A unit flashed before this mechanism existed is missing four things: the
497+
updater itself, the trust directory, a current helper, and the identity fields
498+
the safety checks read. All four can be installed once, over SSH, without
499+
reflashing. Everything fetched here is a published release asset.
500+
501+
```bash
502+
R=https://github.com/bauer-group/XPD-RPIImage/releases/latest/download
503+
504+
sudo curl -fsSL --proto '=https' -o /usr/local/sbin/bgrpiimage-update "$R/bgrpiimage-update"
505+
sudo curl -fsSL --proto '=https' -o /usr/local/sbin/bgrpiimage-setup "$R/bgrpiimage-setup"
506+
sudo chmod 0755 /usr/local/sbin/bgrpiimage-update /usr/local/sbin/bgrpiimage-setup
507+
508+
sudo install -d /usr/share/bgrpiimage/trusted-keys.d
509+
sudo curl -fsSL --proto '=https' \
510+
-o /usr/share/bgrpiimage/trusted-keys.d/bgrpiimage-recovery.pub \
511+
"$R/bgrpiimage-recovery.pub"
512+
```
513+
514+
Then record what the image was built from, which the old release file does not
515+
say:
516+
517+
```bash
518+
sudo tee -a /etc/bgrpiimage-release >/dev/null <<'EOF'
519+
BGRPIIMAGE_BASE_IMAGE_SHA256='acff736ca7945e3b305f07cda4abdb870910e12634991da69783611756e381b3'
520+
BGRPIIMAGE_APPLY_CONTRACT=1
521+
EOF
522+
523+
sudo bgrpiimage-update plan # look before you leap
524+
sudo bgrpiimage-update apply
525+
```
526+
527+
**Why that hash is safe to write.** It is the base image every release since
528+
**v0.5.0** has been built on — the switch to Raspberry Pi OS Lite — and it has
529+
not changed since. Check the first line of `sudo bgrpiimage-setup status`: if
530+
the device reports **v0.5.0 or newer**, it is provably on that base and the
531+
value above is correct for it.
532+
533+
> ⚠️ **A device older than v0.5.0 is on a different base OS.** Do not write
534+
> that hash there. Reflash instead — the whole point of the check is that
535+
> configuration built for one base must not be applied on top of another.
536+
537+
Nothing here is a special code path: after the four files are in place the
538+
device is indistinguishable from one flashed with a current image, and every
539+
refusal and verification described above applies to it unchanged. The procedure
540+
is exercised end to end in `tests/test-update.sh`.
493541
494542
### What it never touches
495543

‎tests/test-update.sh‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ cp /repo/dist/*.bundle.manifest.json "$DL/" 2>/dev/null || \
9191
tar xzOf "$BUNDLE" manifest.json > "$DL/bgrpiimage-${VARIANT}-v${VERSION}.bundle.manifest.json"
9292
( cd "$DL" && sha256sum "$(basename "$BUNDLE")" > "$(basename "$BUNDLE").sha256" )
9393

94+
# Helpers and trust keys, exactly as the release job publishes them.
95+
B=/repo/src/modules/bgrpiimage-base/filesystem/root
96+
install -m 0755 "$B/usr/local/sbin/bgrpiimage-setup" "$DL/bgrpiimage-setup"
97+
install -m 0755 "$B/usr/local/sbin/bgrpiimage-update" "$DL/bgrpiimage-update"
98+
install -m 0644 "$B"/usr/share/bgrpiimage/trusted-keys.d/*.pub "$DL/"
99+
94100
cd "$WEB" || exit 1
95101
python3 -c "
96102
import http.server, ssl, socketserver, os
@@ -318,6 +324,55 @@ grep -qi "no trusted keys" /tmp/sg5 \
318324
&& ok "a device with no trust store refuses and explains" \
319325
|| { bad "device without trust store did not refuse clearly"; tail -4 /tmp/sg5; }
320326

327+
sect "11. bootstrapping hardware from before in-place updates"
328+
# What a device flashed at, say, v0.7.7 actually looks like: no updater, no
329+
# trust store, and a release file with none of the identity the checks need.
330+
# The documented procedure has to take it from there to a working update, or
331+
# an existing fleet can never join without a reflash.
332+
seed_device 0.0.1
333+
rm -rf /usr/share/bgrpiimage/trusted-keys.d /usr/local/sbin/bgrpiimage-update
334+
cat > /etc/bgrpiimage-release <<EOF
335+
BGRPIIMAGE_DIST="bgrpiimage"
336+
BGRPIIMAGE_VARIANT='$VARIANT'
337+
BGRPIIMAGE_VERSION='0.7.7'
338+
EOF
339+
340+
# It must refuse before the bootstrap - otherwise this proves nothing.
341+
if [ -x /usr/local/sbin/bgrpiimage-update ]; then
342+
bad "fixture is wrong: the updater is still installed"
343+
else
344+
ok "a pre-update device has no updater at all"
345+
fi
346+
347+
# --- the documented procedure, run verbatim --------------------------------
348+
R="$BGRPIIMAGE_UPDATE_DL/$REPO/releases/download/v$VERSION"
349+
curl -fsSL --proto '=https' -o /usr/local/sbin/bgrpiimage-update "$R/bgrpiimage-update"
350+
chmod 0755 /usr/local/sbin/bgrpiimage-update
351+
curl -fsSL --proto '=https' -o /usr/local/sbin/bgrpiimage-setup "$R/bgrpiimage-setup"
352+
chmod 0755 /usr/local/sbin/bgrpiimage-setup
353+
install -d /usr/share/bgrpiimage/trusted-keys.d
354+
curl -fsSL --proto '=https' \
355+
-o /usr/share/bgrpiimage/trusted-keys.d/bgrpiimage-recovery.pub \
356+
"$R/bgrpiimage-recovery.pub"
357+
# base_image.sha256 has been the same since v0.5.0, so any device from that
358+
# release onwards is provably on this base - see the note in the docs.
359+
{
360+
echo "BGRPIIMAGE_BASE_IMAGE_SHA256='$BASE_SHA'"
361+
echo "BGRPIIMAGE_APPLY_CONTRACT=1"
362+
} >> /etc/bgrpiimage-release
363+
364+
$U apply --yes >/tmp/bs 2>&1; rc=$?
365+
[ $rc -eq 0 ] && ok "bootstrapped device applies the update" \
366+
|| { bad "apply failed after bootstrap"; tail -12 /tmp/bs; }
367+
grep -qi "signature verified" /tmp/bs \
368+
&& ok "and verifies the signature against the fetched key" \
369+
|| bad "signature was not verified after bootstrap"
370+
grep -q "BGRPIIMAGE_CONFIG_VERSION='$VERSION'" /etc/bgrpiimage-applied \
371+
&& ok "configuration version recorded" || bad "version not recorded"
372+
grep -q "BGRPIIMAGE_VERSION='0.7.7'" /etc/bgrpiimage-release \
373+
&& ok "still reports the originally flashed image" \
374+
|| bad "the flashed version was overwritten"
375+
321376
echo
322377
echo "================ $PASS passed, $FAIL failed ================"
323378
[ "$FAIL" -eq 0 ]

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL