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

fix(test): stopped the runtime-gate test failing in CI · bauer-group/XPD-RPIImage@4e2c5d2 · GitHub

Repository navigation

Commit 4e2c5d2

Browse files
committed
fix(test): stopped the runtime-gate test failing in CI
Section 19 of test-update.sh signed its fixture manifests with the real local signing key at .secrets/bgrpiimage-recovery.key. That directory is gitignored and only exists on a developer's machine - in CI the key is passed as a PEM in BGRPIIMAGE_SIGNING_KEY and never written to disk, and the test container mounts /repo read-only. So in CI, opening the key failed silently (stderr discarded), the manifest went out unsigned, verify_signature rejected it before the runtime gate was ever reached, and the suite reported 2 failures. * Section 19 now generates a throwaway Ed25519 key and copies its public half into the fixture device's trust store, the same idiom section 10 already uses for its foreign-key case - trusted instead of foreign, since this fixture needs the tampered manifest to reach the gate rather than being stopped by signature verification. * Re-copies the throwaway public key after every seed_device call: the trust store is reseeded from the image's own shipped keys each time, so a key added before one seed does not survive past it. * Removed the leftover /stub/podman at the end of section 19, so a section added later does not silently inherit a Podman device. * Added a case (test-update.sh and test-render-podman.sh) that stubs both podman and docker and asserts podman wins - the fixtures never had a docker stub on PATH at all, so an inverted if/else in detect_runtime() passed every existing case. Verified by bind-mounting an empty directory over /repo/.secrets, the same way the failure was found: 70 passed/2 failed before this fix, 73 passed/0 failed after, with the real .secrets/ present or not.
1 parent 5938199 commit 4e2c5d2

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

‎tests/test-render-podman.sh‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ else:
325325
"with no podman but docker on PATH, falls back to docker",
326326
f"rc={rc} stdout={out!r} stderr={err!r}")
327327
328+
# Neither single-binary case above can catch an inverted if/else: with no
329+
# `docker` on PATH at all, a docker-first probe still lands on the `else`
330+
# branch and reports docker only when podman is also absent, so swapping
331+
# the branches would still pass both cases above. Only stubbing BOTH
332+
# proves which branch actually fires first.
333+
rc, out, err = _run_with_stub("podman", "docker")
334+
report(rc == 0 and out == "podman podman.socket",
335+
"with both podman and docker on PATH, podman wins",
336+
f"rc={rc} stdout={out!r} stderr={err!r}")
337+
328338
print()
329339
print(f"{passed} passed, {failed} failed")
330340
sys.exit(0 if failed == 0 else 1)

‎tests/test-update.sh‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,21 @@ sect "19. container runtime identity"
608608
# `podman` itself - stubbing `docker` would prove nothing about which
609609
# runtime the gate actually saw.
610610
printf '#!/bin/sh\nexit 0\n' > /stub/podman; chmod +x /stub/podman
611-
KEY=/repo/.secrets/bgrpiimage-recovery.key
611+
612+
# The manifest is tampered below, so it has to be re-signed to reach the
613+
# runtime gate rather than being stopped earlier by signature verification -
614+
# same technique as section 10's foreign-key case, but the opposite trust
615+
# direction: that key proves an UNTRUSTED signer is refused, this one has to
616+
# be TRUSTED so what is actually exercised is the runtime gate. The real
617+
# release key is not an option here: it lives only in this developer's
618+
# gitignored .secrets/, or as a PEM in the CI env var BGRPIIMAGE_SIGNING_KEY
619+
# that is deliberately never written to disk (see .github/workflows/build.yml)
620+
# - and the test container mounts /repo read-only regardless. A throwaway
621+
# key, generated on the fly and trusted by THIS device only, is what works
622+
# unattended on every machine, CI included.
623+
openssl genpkey -algorithm ed25519 -out /tmp/runtime.key 2>/dev/null
624+
openssl pkey -in /tmp/runtime.key -pubout -out /tmp/runtime.pub 2>/dev/null
625+
KEY=/tmp/runtime.key
612626
work=/tmp/runtime
613627

614628
# A bundle explicitly built for Docker, applied to a Podman device.
@@ -621,6 +635,10 @@ openssl pkeyutl -sign -rawin -inkey "$KEY" \
621635
( cd $work && tar czf "$DL/$(basename "$BUNDLE")" manifest.json manifest.json.sig root )
622636
( cd "$DL" && sha256sum "$(basename "$BUNDLE")" > "$(basename "$BUNDLE").sha256" )
623637
seed_device 0.0.1
638+
# seed_device reseeds the trust store from the image's own shipped keys, so
639+
# the throwaway key has to be re-added after every seed and before the apply
640+
# that relies on it being trusted.
641+
cp /tmp/runtime.pub /usr/share/bgrpiimage/trusted-keys.d/
624642
$U apply --yes >/tmp/rt1 2>&1
625643
grep -qi "bundle is built for docker" /tmp/rt1 \
626644
&& ok "runtime mismatch refused (bundle=docker, device=podman)" \
@@ -642,6 +660,7 @@ openssl pkeyutl -sign -rawin -inkey "$KEY" \
642660
( cd $work && tar czf "$DL/$(basename "$BUNDLE")" manifest.json manifest.json.sig root )
643661
( cd "$DL" && sha256sum "$(basename "$BUNDLE")" > "$(basename "$BUNDLE").sha256" )
644662
seed_device 0.0.1
663+
cp /tmp/runtime.pub /usr/share/bgrpiimage/trusted-keys.d/
645664
$U apply --yes >/tmp/rt2 2>&1
646665
grep -qi "predates the container-runtime contract" /tmp/rt2 \
647666
&& ok "legacy bundle with no container_runtime refused on a Podman device" \
@@ -658,6 +677,31 @@ $U apply --yes >/tmp/rt3 2>&1; rc=$?
658677
[ $rc -eq 0 ] && ok "matching container runtime (podman/podman) is not blocked" \
659678
|| { bad "a matching runtime was refused"; tail -8 /tmp/rt3; }
660679

680+
# Podman-first is the load-bearing property of detect_runtime(): this fixture
681+
# has no `docker` stub anywhere on PATH, so every case above would pass just
682+
# as well with the if/else branches swapped. Only stubbing BOTH binaries and
683+
# asserting podman still wins can catch an inverted discriminator. Extracted
684+
# and run standalone (rather than through a full apply) because that is the
685+
# only way to observe detect_runtime()'s own return value directly.
686+
rt_fn=$(sed -n '/^detect_runtime() {/,/^}/p' "$U")
687+
mkdir -p /tmp/bothstub
688+
printf '#!/bin/sh\nexit 0\n' > /tmp/bothstub/podman; chmod +x /tmp/bothstub/podman
689+
printf '#!/bin/sh\nexit 0\n' > /tmp/bothstub/docker; chmod +x /tmp/bothstub/docker
690+
# `PATH=... bash -c` would search the NEW PATH for `bash` itself and fail to
691+
# find it there - so bash is resolved by its absolute path first, and PATH is
692+
# only replaced once already running, for detect_runtime()'s own PATH lookups.
693+
BASH_BIN=$(command -v bash)
694+
result=$(PATH=/tmp/bothstub "$BASH_BIN" -c "$rt_fn"$'\n'"detect_runtime")
695+
[ "$result" = "podman" ] \
696+
&& ok "podman-first ordering: both podman and docker on PATH still yields podman" \
697+
|| bad "expected podman with both stubbed, got '${result:-<empty>}'"
698+
rm -rf /tmp/bothstub
699+
700+
# Leave no stub behind: this is currently the last section, but a podman
701+
# stub surviving past it would silently turn every legacy-manifest fixture
702+
# added afterwards into a Podman device.
703+
rm -f /stub/podman
704+
661705
echo
662706
echo "================ $PASS passed, $FAIL failed ================"
663707
[ "$FAIL" -eq 0 ]

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL