perf(ci-runner): bake pinned cosign + syft into the image #85

Merged
ryangr0 merged 1 commit from perf/ci-runner-bake-cosign-syft into main 2026-08-05 11:26:03 +00:00
Owner

Every signing job installs cosign and syft at runtime. Both are uses: on absolute github.com URLs, so act git-clones each action repository, and the actions then download the binaries from GitHub releases — and cosign-installer additionally downloads a bootstrap cosign to verify the real one:

INFO: Downloading bootstrap version 'v3.0.6' of cosign to verify version to be installed...
INFO: Custom cosign version 'v2.4.3' requested
INFO: Downloading cosign public key 'v2.4.3' of cosign...
INFO: Downloading detached signature for platform-specific 'v2.4.3' of cosign...

Four WAN round-trips on the critical path of every release, in the job that decides whether an image is fit to sign.

This is the same reasoning that already put Helm and yq in this image — the Helm comment says it outright: "CI jobs currently run azure/setup-helm per job — a WAN download plus an action clone on every run." It also makes the release path independent of github.com being reachable at release time, which is where this repo is heading anyway.

Checksums, not just versions

ARG COSIGN_VERSION=2.4.3
ARG COSIGN_SHA256=caaad125acef1cb81d58dcdc454a1e429d09a750d1e9e2b3ed1aed8964454708

The SHA256 is what's actually trusted, and a Renovate bump that forgets the checksum fails this build, loudly, rather than silently installing something else. This still trusts the GitHub release artifact — the same trust root cosign-installer's bootstrap binary ultimately rests on — but it pins an exact byte sequence and resolves it once at image build instead of on every release.

Plus a regression guard in the same shape as the existing envsubst one: RUN command -v cosign && command -v syft.

Verified by actually downloading both artifacts and checking the pinned digests, and that syft sits at the root of its tarball (tar -xzf … -C /tmp syft depends on that).

cosign stays on 2.x

Carried over verbatim from the action's input default. 3.x changes the default bundle/attestation format and the verification surface, so it has to roll out together with the Kyverno image-verify policy and everything already consuming these attestations. Don't let a Renovate major land here on its own.

⚠️ Tools only — the removal comes later

This deliberately does not touch cosign-sign-attest. The forgejo-runner ScaledJob pins ci-runner by digest:

image: harbor.webgrip.dev/webgrip/ci-runner@sha256:ee1401d0a5532e4aee…

so runner pods don't pick this up until that digest is bumped in homelab-cluster. Removing the installer steps before then breaks every signing job.

Order:

  1. merge this → ci-runner releases with the tools
  2. bump the ScaledJob digest in homelab-cluster (rollout.strategy: gradual means in-flight runners finish on the old spec)
  3. then drop the two uses: from cosign-sign-attest

Sequencing note

Merge #84 first if you can — this PR touches ops/docker/, so its build-check job is the first thing that will actually exercise #84's CLI rewrite, without pushing or releasing anything.

Every signing job installs cosign and syft **at runtime**. Both are `uses:` on absolute **github.com** URLs, so act git-clones each action repository, and the actions then download the binaries from GitHub releases — and `cosign-installer` additionally downloads a *bootstrap* cosign to verify the real one: ``` INFO: Downloading bootstrap version 'v3.0.6' of cosign to verify version to be installed... INFO: Custom cosign version 'v2.4.3' requested INFO: Downloading cosign public key 'v2.4.3' of cosign... INFO: Downloading detached signature for platform-specific 'v2.4.3' of cosign... ``` Four WAN round-trips on the critical path of every release, in the job that decides whether an image is fit to sign. This is the same reasoning that already put **Helm** and **yq** in this image — the Helm comment says it outright: *"CI jobs currently run azure/setup-helm per job — a WAN download plus an action clone on every run."* It also makes the release path independent of github.com being reachable at release time, which is where this repo is heading anyway. ## Checksums, not just versions ```dockerfile ARG COSIGN_VERSION=2.4.3 ARG COSIGN_SHA256=caaad125acef1cb81d58dcdc454a1e429d09a750d1e9e2b3ed1aed8964454708 ``` The SHA256 is what's actually trusted, and a Renovate bump that forgets the checksum fails **this build**, loudly, rather than silently installing something else. This still trusts the GitHub release artifact — the same trust root `cosign-installer`'s bootstrap binary ultimately rests on — but it pins an exact byte sequence and resolves it once at image build instead of on every release. Plus a regression guard in the same shape as the existing `envsubst` one: `RUN command -v cosign && command -v syft`. **Verified by actually downloading both artifacts** and checking the pinned digests, and that `syft` sits at the root of its tarball (`tar -xzf … -C /tmp syft` depends on that). ## cosign stays on 2.x Carried over verbatim from the action's input default. 3.x changes the default bundle/attestation format and the verification surface, so it has to roll out together with the Kyverno image-verify policy and everything already consuming these attestations. **Don't let a Renovate major land here on its own.** ## ⚠️ Tools only — the removal comes later This deliberately does **not** touch `cosign-sign-attest`. The `forgejo-runner` ScaledJob pins ci-runner **by digest**: ```yaml image: harbor.webgrip.dev/webgrip/ci-runner@sha256:ee1401d0a5532e4aee… ``` so runner pods don't pick this up until that digest is bumped in `homelab-cluster`. Removing the installer steps before then breaks every signing job. **Order:** 1. merge this → ci-runner releases with the tools 2. bump the ScaledJob digest in homelab-cluster (`rollout.strategy: gradual` means in-flight runners finish on the old spec) 3. *then* drop the two `uses:` from `cosign-sign-attest` ## Sequencing note Merge [#84](https://forgejo.webgrip.dev/webgrip/infrastructure/pulls/84) first if you can — this PR touches `ops/docker/`, so its `build-check` job is the first thing that will actually exercise #84's CLI rewrite, without pushing or releasing anything.
perf(ci-runner): bake pinned cosign + syft into the image
All checks were successful
[Workflow] On Source Change / Guard: per-image .releaserc.cjs present (push) Successful in 4s
[Workflow] On Source Change / Determine Changed Directories (push) Successful in 26s
[Workflow] On Source Change / Determine changed images (push) Successful in 0s
[Workflow] On Source Change / Release (ci-runner, ops/docker/ci-runner) (push) Has been skipped
[Workflow] On Source Change / Build check (ci-runner, ops/docker/ci-runner) (push) Successful in 2m10s
24095a899b
Every signing job installs both at runtime. sigstore/cosign-installer and
anchore/sbom-action/download-syft are `uses:` on ABSOLUTE github.com URLs, so act git-clones each
action repository, and the actions then download the binaries from GitHub releases —
cosign-installer additionally downloads a *bootstrap* cosign to verify the real one. Four WAN
round-trips on the critical path of every release, in the job that decides whether an image is fit
to sign.

Same reasoning that already put Helm and yq in this image ("a WAN download plus an action clone on
every run"). It also makes the release path independent of github.com being reachable at release
time, which is the direction this repository is going anyway.

Checksums are pinned next to the versions and verified with sha256sum -c, so the SHA256 is what is
actually trusted and a Renovate bump that forgets the checksum fails THIS build loudly instead of
silently installing something else. That trusts the GitHub release artifact — the same trust root
cosign-installer's bootstrap binary ultimately rests on — but pins it to an exact byte sequence and
resolves it once at image build rather than on every release.

cosign stays on 2.x deliberately, carried over from the action's input default: 3.x changes the
default bundle/attestation format and the verification surface, so it has to roll out together with
the Kyverno image-verify policy and everything already consuming these attestations.

Verified by downloading both artifacts and checking the pinned digests, and that `syft` sits at the
root of its tarball.

TOOLS ONLY — this deliberately does not touch cosign-sign-attest. The forgejo-runner ScaledJob pins
ci-runner BY DIGEST, so runner pods do not pick this up until that digest is bumped in
homelab-cluster. Removing the installer steps before then would break every signing job. Order is:
release this, bump the ScaledJob digest, then drop the two `uses:`.
ryangr0 force-pushed perf/ci-runner-bake-cosign-syft from 24095a899b
All checks were successful
[Workflow] On Source Change / Guard: per-image .releaserc.cjs present (push) Successful in 4s
[Workflow] On Source Change / Determine Changed Directories (push) Successful in 26s
[Workflow] On Source Change / Determine changed images (push) Successful in 0s
[Workflow] On Source Change / Release (ci-runner, ops/docker/ci-runner) (push) Has been skipped
[Workflow] On Source Change / Build check (ci-runner, ops/docker/ci-runner) (push) Successful in 2m10s
to ed778a3f7a
Some checks failed
[Workflow] On Source Change / Guard: per-image .releaserc.cjs present (push) Successful in 2s
[Workflow] On Source Change / Determine Changed Directories (push) Successful in 1m18s
[Workflow] On Source Change / Determine changed images (push) Successful in 0s
[Workflow] On Source Change / Release (ci-runner, ops/docker/ci-runner) (push) Has been skipped
[Workflow] On Source Change / Build check (ci-runner, ops/docker/ci-runner) (push) Failing after 2m18s
2026-08-02 13:43:15 +00:00
Compare
Author
Owner

Amended: the versions I first pushed were stale. cosign v2.4.3 -> v2.6.4 (latest 2.x; 3.x changes the attestation format and is a coordinated Kyverno migration, not a side effect of this PR), syft v1.48.0 -> v1.50.0 (syft had been printing 'a newer version is available' in every release log). Both checksums re-verified by downloading the real artifacts. The action's input defaults move with them so the baked and installed binaries cannot drift.

Amended: the versions I first pushed were stale. cosign v2.4.3 -> v2.6.4 (latest 2.x; 3.x changes the attestation format and is a coordinated Kyverno migration, not a side effect of this PR), syft v1.48.0 -> v1.50.0 (syft had been printing 'a newer version is available' in every release log). Both checksums re-verified by downloading the real artifacts. The action's input defaults move with them so the baked and installed binaries cannot drift.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/infrastructure!85
No description provided.