Skip to content

ci: CI signing via OIDC KMS, now with the Rekor transparency log - #279

Merged
LeeroyHannigan merged 5 commits into
mainfrom
ci/sign-image-rekor
Aug 19, 2026
Merged

ci: CI signing via OIDC KMS, now with the Rekor transparency log#279
LeeroyHannigan merged 5 commits into
mainfrom
ci/sign-image-rekor

Conversation

@robinnsc

Copy link
Copy Markdown
Collaborator

What

Revives the CI signing workflow from #268 (closed unmerged during
post-launch cleanup, no review findings) rebased onto current main, with
one substantive change: the Rekor transparency log is ON
(--tlog-upload=true), and the verify step checks the log entry instead of
ignoring it. Everything else is as reviewed before: dockerhub environment
(two-person rule enforced mechanically, registry credential from
environment secrets), OIDC-federated AWS access to
ExtendDBContainerSigningCI (live and verified since 2026-08-14),
checksum-pinned cosign, SHA-pinned actions, strict input validation
including the release_tag annotation input, and the post-sign gate that
fails the run unless the legacy .sig TAG exists.

Why

Phase 1 of the release-pipeline consolidation (runbook §7): signing moves
off operator laptops into a reviewer-gated workflow. Rekor could not be
enabled for v0.1.5 because manual signing ran on corp machines, where
rekor.sigstore.dev is DNS-sinkholed; CI runners reach it fine. The Rekor
bundle rides in the .sig annotations, so the signature mirrored to GHCR
(and copied to ECR) verifies with the transparency log from any registry —
and from 0.1.6 on, user verify instructions drop the
--insecure-ignore-tlog flag.

v0.1.5's signatures remain valid and verifiable exactly as released; this
is additive.

Testing done

Checklist

  • All tests pass (cargo test --workspace) — not applicable, workflow-only change
  • Code is formatted (cargo fmt --check) — not applicable
  • Clippy is clean (cargo clippy -- -W clippy::pedantic) — not applicable
  • I have added or updated tests for new functionality — post-sign .sig tag gate is the storage-mode regression test; live rehearsal plan above
  • I have updated documentation if behavior changed — header rewritten for the Rekor decision; runbook §7 tracks this as Phase 1
  • Breaking changes are noted below (if any)

ADR / RFC: n/a — CI tooling only.

Breaking changes

None. The manual signing path remains as break-glass.


By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.

KEY_ARN: ${{ inputs.key-arn }}
run: |
set -euo pipefail
cosign verify --key "awskms:///${KEY_ARN}" "${IMAGE_REPO}@${DIGEST}" | head -1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cosign verify --key "awskms:///${KEY_ARN}" "${IMAGE_REPO}@${DIGEST}" | head -1
cosign verify --key "awskms:///${KEY_ARN}" "${IMAGE_REPO}@${DIGEST}" >/dev/null

Line 107 is set -euo pipefail, so head -1 closing the pipe can SIGPIPE cosign to exit 141 and fail the job after the signature is already pushed and the .sig tag exists. Fail-closed, so not dangerous, but it reads as a signing failure when signing worked.

Comment thread.github/workflows/sign-image.yml Outdated
grep -q '"architecture": *"amd64"' <<< "$RAW" || { echo "::error::amd64 missing from index"; exit 1; }
grep -q '"architecture": *"arm64"' <<< "$RAW" || { echo "::error::arm64 missing from index"; exit 1; }

- uses: actions/checkout@v4

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262

@v4 is a moving tag: GitHub can repoint it at different code later, so what runs in this job can change without a PR. This job holds the signing credentials, so it should be pinned to a fixed commit like configure-aws-credentials below already is.

@robinnsc

robinnsc commented Aug 19, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Thanks, addressed those comments

Moves production Docker Hub signing off operator laptops into a
reviewer-gated workflow: the dockerhub environment supplies the
registry credential and enforces the two-person rule mechanically,
and AWS access is OIDC-federated to a role only this repository's
approved environment runs can assume. Pins cosign by checksum and
the credentials action by commit SHA, forces the proven tag-based
storage flags, and fails the run if the .sig tag does not appear.
github.ref_name on a workflow_dispatch from main is 'main', which
would have annotated production signatures with the wrong release.
Runners reach the public Sigstore log; corp machines do not (the
reason v0.1.5 shipped without Rekor entries). The bundle rides in the
.sig annotations, so mirrored signatures verify with the transparency
log from any registry, and user verify commands drop the
--insecure-ignore-tlog flag from 0.1.6 on.
The release pipeline folds signing into the publish stage (release
owner decision: publish approval covers signing; a reusable-workflow
call would carry its own environment approval and silently
reintroduce the click that was folded away). A composite action keeps
one source of truth for the proven cosign flags; this workflow
becomes the thin break-glass wrapper around it.

@LeeroyHanniganLeeroyHannigan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signing is digest-bound, inputs are validated and routed through env, actions are SHA-pinned, and dispatch-only plus the environment gate keeps forks away from the role. Approving.

Two follow-ups worth doing:

  • The headers say routine releases sign inside release-image's publish stage, but release-image never references the composite action, so this wrapper is currently the only caller. Either wire release-image to it or fix the comments, otherwise the next person assumes pipeline signing already exists.
  • Worth confirming the IAM trust policy pins sub to repo:ExtendDB/extenddb:environment:dockerhub rather than the whole repo. That condition is the real control and it lives outside this diff.

Minor: the arch check greps the raw index JSON for the architecture strings. jq on manifests[].platform.architecture would be tighter.

@LeeroyHannigan
LeeroyHannigan added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 908e7a6Aug 19, 2026
17 checks passed
LeeroyHannigan added a commit that referenced this pull request Aug 19, 2026
Mirrors release-image.yml's gate rules and its tested-artifact-is-published-
artifact property (native runner per arch, docker save/load, credential-free
build jobs). Deliberate difference: one dispatch runs gate, build+smoke,
publish, with the reviewer-gated dockerhub environment approval on the publish
job as the single human step -- the two-person build-then-promote split guards
the production image; this is explicitly a dev image (decided 2026-08-15).
The version tag refuses overwrite and latest only moves forward by
highest-semver comparison. The licence gate checks the DEV notices file.
Signing runs in the same job via the shared sign-image composite action (OIDC
to the signing role, Rekor on), so this workflow requires that action from
PR #279 to be on main first.
rlunar pushed a commit to rlunar/extenddb that referenced this pull request Aug 20, 2026
Mirrors release-image.yml's gate rules and its tested-artifact-is-published-
artifact property (native runner per arch, docker save/load, credential-free
build jobs). Deliberate difference: one dispatch runs gate, build+smoke,
publish, with the reviewer-gated dockerhub environment approval on the publish
job as the single human step -- the two-person build-then-promote split guards
the production image; this is explicitly a dev image (decided 2026-08-15).
The version tag refuses overwrite and latest only moves forward by
highest-semver comparison. The licence gate checks the DEV notices file.
Signing runs in the same job via the shared sign-image composite action (OIDC
to the signing role, Rekor on), so this workflow requires that action from
PR ExtendDB#279 to be on main first.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@robinnsc@LeeroyHannigan