fix: bake openssl and jq into the runner image, and assert them

The release tag gate parses the Control Plane quorum verdict and verifies its
Ed25519 DSSE signature by shelling out to jq and openssl. Neither is in this
image, and both call sites read cmd ... || { echo <security message>; exit 1; },
so command-not-found took the failure branch: the gate reported a valid, signed
approval first as "not a signed DSSE envelope" and then as "signature FAILED
verification". That blocked v0.0.134 and v0.0.135 on 2026-08-31 and pointed the
investigation at the Tag Request rather than at this image — the same shape as
the git-daemon gap already documented above the package list.

Also adds both to the toolchain assertion. That check already existed and still
missed them, because it only covers what someone thought to list; the comment
now says so, and says to extend it whenever a gate grows a shell dependency.

Verified by building this image and running the gate script inside it against
the live Control Plane: openssl 3.5.8, jq 1.8.1, and the v0.0.135 verdict
verifies and binds to its commit. The unpatched image fails the same script.
This commit is contained in:
agent committed 2026-08-31 12:56:44 +02:00
1 parent bf26dc4e15
commit bc1d6890b6
2 files changed
+26 -1

No files matched your search

+10
View File
@@ -101,6 +101,14 @@ jobs:
# #
# Asserted on BOTH architectures, because a package that resolves on amd64 # Asserted on BOTH architectures, because a package that resolves on amd64
# and not on arm64 would otherwise reach half the nodes unnoticed. # and not on arm64 would otherwise reach half the nodes unnoticed.
#
# openssl and jq were added after this check already existed and still
# missed them: the release tag gate shells out to both to verify the
# Control Plane's signed quorum verdict, and their absence surfaced as
# "verdict not signed" / "signature FAILED verification" rather than as a
# missing tool, blocking v0.0.134 and v0.0.135. The list only covers what
# someone remembered to add, so add to it whenever a gate grows a new
# shell dependency.
- name: Verify the image still contains the toolchain the gates need - name: Verify the image still contains the toolchain the gates need
if: ${{ steps.meta.outputs.push == 'true' }} if: ${{ steps.meta.outputs.push == 'true' }}
run: | run: |
@@ -119,6 +127,8 @@ jobs:
command -v make >/dev/null && echo "make=ok" || echo "make=MISSING" command -v make >/dev/null && echo "make=ok" || echo "make=MISSING"
command -v curl >/dev/null && echo "curl=ok" || echo "curl=MISSING" command -v curl >/dev/null && echo "curl=ok" || echo "curl=MISSING"
command -v tar >/dev/null && echo "tar=ok" || echo "tar=MISSING" command -v tar >/dev/null && echo "tar=ok" || echo "tar=MISSING"
command -v openssl >/dev/null && echo "openssl=ok" || echo "openssl=MISSING"
command -v jq >/dev/null && echo "jq=ok" || echo "jq=MISSING"
[ -x "$(git --exec-path)/git-http-backend" ] \ [ -x "$(git --exec-path)/git-http-backend" ] \
&& echo "git-http-backend=ok" || echo "git-http-backend=MISSING" && echo "git-http-backend=ok" || echo "git-http-backend=MISSING"
' 2>&1) || { echo "::error::could not run $REF on $plat"; fail=1; continue; } ' 2>&1) || { echo "::error::could not run $REF on $plat"; fail=1; continue; }
+16 -1
View File
@@ -38,6 +38,19 @@ USER root
# curl,tar -> setup-go / gitleaks download + extract their own arch-specific binaries # curl,tar -> setup-go / gitleaks download + extract their own arch-specific binaries
# make -> zegit Makefile targets # make -> zegit Makefile targets
# ca-certificates -> trusted roots for HTTPS to gitea/github/goproxy # ca-certificates -> trusted roots for HTTPS to gitea/github/goproxy
# openssl -> the release tag gate verifies the Control Plane's SIGNED quorum
# verdict (Ed25519 DSSE) with `openssl pkeyutl -verify -rawin`.
# Absent, that command failed "command not found" and the gate's
# `|| { echo ...; exit 1; }` reported it as "Quorum verdict
# signature FAILED verification" -- a TOOLING gap narrated as a
# SECURITY verdict, pointing the investigation at a perfectly
# valid signed approval instead of at this image. It blocked
# v0.0.134 and v0.0.135 on 2026-08-31.
# jq -> the same story one step earlier: the gate parsed that envelope
# with `jq -e`, and a missing jq was reported as "not a signed
# DSSE envelope". jq is the reflex tool for JSON in shell steps,
# so its absence keeps resurfacing as a misleading failure in
# whichever step reaches for it next.
# gcc,musl-dev -> C toolchain for cgo; `go test -race` (the Makefile test # gcc,musl-dev -> C toolchain for cgo; `go test -race` (the Makefile test
# target) requires cgo, which needs a C compiler + libc headers # target) requires cgo, which needs a C compiler + libc headers
# (musl on Alpine) # (musl on Alpine)
@@ -60,7 +73,9 @@ RUN apk add --no-cache \
gcc \ gcc \
musl-dev \ musl-dev \
python3 \ python3 \
py3-yaml py3-yaml \
openssl \
jq
# Match the runner pod's runAsUser (1000); keep the image non-root by default. # Match the runner pod's runAsUser (1000); keep the image non-root by default.
USER 1000 USER 1000