Skip to content

[staging/5.1.0] Derive the signing public key and harden the update-publish preflight - #2394

Merged
NipunaRanasinghe merged 5 commits into
wso2:staging/5.1.0from
NipunaRanasinghe:backport/derive-signing-pubkey
Sep 8, 2026
Merged

[staging/5.1.0] Derive the signing public key and harden the update-publish preflight#2394
NipunaRanasinghe merged 5 commits into
wso2:staging/5.1.0from
NipunaRanasinghe:backport/derive-signing-pubkey

Conversation

@NipunaRanasinghe

Copy link
Copy Markdown
Contributor

Backport of #2327 to staging/5.1.0. Five commits, cherry-picked from e3e67f5, 68e50fb,
6c93a4c, f4fc335 and 17bc1dd with no conflicts, in their original order.

Why

#2384 and #2385 backported the component-update mechanism (#2247, #2248) to this branch, but the CI
fixes that landed on main afterwards were not part of either PR. build.yml on this branch is
therefore #2248's original, and a workflow_dispatch started on staging/5.1.0 runs it.

That is already failing. In
run 34251747457
the macOS job fails at Publish Squirrel.Mac update payload to update bucket:

upload failed: installers/mac/wso2-integrator-5.1.0-202609081633-arm64-mac.zip
  to s3://***/artifacts/app/.../wso2-integrator-...-arm64-mac.zip
  Unable to locate credentials

The step's gate on this branch is env.HAS_AWS == 'true', which is only
secrets.AWS_ACCESS_KEY_ID != '' — the secret exists, so the gate opens — but the key is never put
in the step's environment. It also omits publish_update_source from its condition, so it runs on a
nightly that publishes nothing. The direct fix for that specific step is in #2328, which is stacked
on this PR; this one carries the preflight work it builds on.

The macOS job did not fail before #2385 only because installers/mac/build.sh on this branch
ignored INSTALLER_PROFILE, so no -mac.zip was produced and the upload path was inert.

What this contains

  • e3e67f5 — derive the signing public key from the signing key instead of configuring it twice.
  • 68e50fb — require the pinned client key on any run that publishes updates.
  • 6c93a4c — don't fail the preflight on runs that would not publish.
  • f4fc335 — compare the pinned key against the signing key before the builds run, so a mismatch
    fails in seconds rather than surfacing as every client rejecting every artifact after release.
  • 17bc1dd — require the update-bucket credentials in the publish preflight.

Net effect on build.yml: three new steps in resolve-versions (Check the publish path is configured, cosign-installer, Verify the pinned client key matches the signing key), all gated
on publish_update_source && build_packed_installers && publish_tag != '', and the
HAS_PINNED_KEY scaffolding they replace is removed.

Scope

Only .github/workflows/build.yml and .github/workflows/publish-components.yml. No installer
scripts, no product code.

The scheduled nightly is not affected either way — a schedule trigger reads build.yml from
the default branch. This matters for workflow_dispatch runs started on staging/5.1.0, and for
this branch being able to cut a release.

Verification

Not verified: no CI run has exercised this branch. The check worth running before merge is a
daily-build.yml dispatch on this branch with build_branch=builds/nightly-test and
publish_tag=nightly-test, which reproduces the failing route without touching production.

Part 1 of 2

#2328's backport is stacked on this branch and must merge after it.

gigara and others added 5 commits September 8, 2026 23:32
COSIGN_PUBLIC_KEY and WSO2_UPDATE_PUBLIC_KEY held the same public key in two
encodings -- raw PEM for cosign's verify steps, base64 of that PEM for the string
baked into product.json. Two hand-set secrets for one value, and the consistency
check compared them to each other.

That check could not see the mismatch that matters. Both public secrets can agree
perfectly and still belong to a different keypair than COSIGN_PRIVATE_KEY, which is
what actually signs; the in-run verify steps would pass too, because they verified
against the same public secret. The failure would surface on users' machines after
release, as every client rejecting every artifact.

The public half is now derived from the signing key with

  cosign public-key --key env://COSIGN_PRIVATE_KEY

so the comparison is pinned-key vs the key that signs, and the verify steps check
signatures against a key that provably belongs to the signing pair. Verified with
two throwaway keypairs: correct config passes, a pinned key from the other pair is
refused (the case the old check missed), and a non-base64 pinned value is refused.

COSIGN_PUBLIC_KEY is no longer referenced anywhere and can be deleted from the
repository secrets -- one fewer value to set and to keep in sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit e3e67f5)
WSO2_UPDATE_PUBLIC_KEY was optional, and its absence was silent in the worst
direction: HAS_PINNED_KEY went false, the key comparison was skipped, and
WSO2_UPDATE_REQUIRE_ARTIFACT_SIGNATURE computed to false — so a green release
shipped clients that pin no key and accept unverified artifacts, then published
a source document for them.

Check it in resolve-versions rather than in Publish Update Source. build-macos
and build-windows bake WSO2_UPDATE_URL and WSO2_UPDATE_PUBLIC_KEY into
product.json well before that job runs, so a late check cannot stop a run from
producing those installers — it can only decline to publish afterwards. The
early gate costs seconds instead of a full build, and covers the bucket, the
artifacts CDN base, the update URL and the signing key alongside it.

With presence guaranteed, the pinned-key DER comparison is unconditional in
both workflows, and publish-components.yml requires the same secret in its own
preflight.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 68e50fb)
The gate fired on publish_update_source alone, so a release with
build_packed_installers off — or with no publish_tag — failed for missing
publish secrets even though Publish Update Source skips itself in exactly those
cases. Mirror that job's own conditions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 6c93a4c)
…ilds run

Checking only that WSO2_UPDATE_PUBLIC_KEY is non-empty left the failure this
change is about — a pinned key from the wrong pair — to the late check in
Publish Update Source, which runs after build-macos and build-windows have
already baked that key into product.json. Nothing downstream would catch it
either: the signing steps verify against the signing key itself, so they pass.

Derive and compare in resolve-versions instead. cosign-installer plus one
`cosign public-key` costs seconds, and it also exercises COSIGN_PASSWORD, which
otherwise burns a full build before failing at the derive step. The late
comparison is now redundant and removed; Publish Update Source still derives
signing.pub for signature verification.

Also guard the decode with `[ -s pinned.pem ]` in both workflows — `base64 -d`
accepts the empty string, so an empty secret used to skip the friendly error
and die at openssl — and drop a comment left describing a `cosign.pub` write
that no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit f4fc335)
A release with AWS_UPDATE_S3_BUCKET set but no credentials — or the reverse —
would run the full macOS and Windows builds before anything noticed. The
preflight already covers the bucket; the keys that write to it belong beside
it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 17bc1dd)
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • 5.0.x

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 8a72bdd6-627e-4768-8866-876de754e779

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@NipunaRanasinghe
NipunaRanasinghe merged commit 7673edd into wso2:staging/5.1.0 Sep 8, 2026
2 checks passed
Sign up for free to 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