Skip to content

feat(security): cosign-verify the infrastructure Flux OCI artifact#2319

Merged
devantler merged 2 commits into
mainfrom
claude/flux-verify-infra-oci
Jun 28, 2026
Merged

feat(security): cosign-verify the infrastructure Flux OCI artifact#2319
devantler merged 2 commits into
mainfrom
claude/flux-verify-infra-oci

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Assistant

Closes #1570 — enables keyless cosign signature verification on the flux-system OCIRepository (the platform's own manifests source: the single largest blast radius in the cluster — it defines every controller, policy, and gateway). Extends the supply-chain verification #1559 added for apps to the infrastructure source.

This is the issue I'd been self-blocking on — deep investigation dissolved the gate

My 2026-06-26 triage parked #1570 'awaiting maintainer direction' and called signing 'the real remaining gap'. Investigating live proved that wrong:

  1. The SIGN half is already done. The shared deploy-prod composite already signs the manifests artifact keyless (push → sign → attest; cosign sign --yes --recursive on the digest, plus SBOM attestation). My triage trusted the issue body's stale 'no signing step' and never re-checked the composite.
  2. So only the VERIFY half remained. Per #4987 the flux-system OCIRepository is KSail-generated and continuously reconciled, so a hand-written spec.verify would lose the reconcile fight. KSail exposes spec.cluster.verify (FluxVerifySpec, present in v7.84.x — platform's exact pin) which it renders onto the generated OCIRepository. This PR sets it.

The matcher is grounded in the real signature, not a guess

This is high blast radius — a wrong identity makes Flux reject the infra artifact and halt all reconciliation — so I read the live signature rather than guessing. cosign verify ghcr.io/devantler-tech/platform/manifests:latest confirmed the artifact is signed by .github/workflows/ci.yaml (the merge-queue deploy-prod path) under issuer https://token.actions.githubusercontent.comci.yaml matches, cd.yaml does not (no manual deploy has signed yet). The matcher allows both ci.yaml and cd.yaml (same composite/identity flow, so a future manual deploy isn't rejected), and @.+ keeps it resilient to the per-run ref (merge_group signs under refs/heads/gh-readonly-queue/...).

verify:
  provider: cosign
  matchOIDCIdentity:
    - issuer: '^https://token\.actions\.githubusercontent\.com$'
      subject: '^https://github\.com/devantler-tech/platform/\.github/workflows/(ci|cd)\.yaml@.+$'

Validation

  • Live signature inspected with cosign (identity confirmed, above).
  • ksail.prod.yaml parses (yq); config is schema-valid against ksail's schema (provider enum + matchOIDCIdentity shape, no extra keys); ksail v7.84.1 (platform's exact pin) accepts the field; the render is unit-tested upstream (ocirepository_verify_test.go).
  • Behaviour-preserving until it deploys: the artifact is already signed, so verification passes for legitimately-published artifacts; it only starts rejecting unsigned/foreign-signed ones.

Blast radius — why it's a draft

Verification can't be fully exercised without a deploy. It's left as a draft so you can promote it when you're ready to watch the first merge-queue deploy-prod reconcile with verification on. If anything were wrong with the identity, the symptom would be the flux-system OCIRepository failing to verify — recoverable by reverting this one-field change.

Out of scope: upstream third-party Helm OCI charts (hcloud-csi, longhorn, …) remain unverified — we can't sign upstream artifacts; that's a separate concern, not part of this issue's 'our own infra artifact' fix.

Fixes #1570. Enables keyless cosign signature verification on the flux-system
OCIRepository (the platform's own manifests source — the largest blast radius in
the cluster), closing the supply-chain gap #1559 opened for apps but left on the
infra source.

Deep investigation dissolved the 'gated' status this issue was parked under:
- The SIGN half (my 2026-06-26 triage's 'real remaining gap') is ALREADY done —
  the deploy-prod composite signs the manifests artifact keyless (push → sign →
  attest). The triage trusted the issue body's stale 'no signing step' and never
  re-checked the composite.
- So only the VERIFY half remained. Per ksail#4987 the flux-system OCIRepository
  is KSail-generated/owned, so a hand-written spec.verify would lose the reconcile
  fight; KSail exposes spec.cluster.verify (FluxVerifySpec, v7.84.x — the pinned
  version) which it renders onto the generated OCIRepository.

The signer identity was confirmed against the LIVE signature (cosign verify on
ghcr.io/devantler-tech/platform/manifests:latest): signed by
.github/workflows/ci.yaml (the merge-queue deploy-prod path) under GitHub's Fulcio
OIDC issuer. cd.yaml (manual deploy, same composite/identity flow) is allowed too;
@.+ keeps the matcher resilient to the per-run ref (merge_group signs under
refs/heads/gh-readonly-queue/...).

Validated: YAML parses; config schema-valid (provider enum + matchOIDCIdentity);
ksail v7.84.1 (platform's exact pin) accepts the field; render is unit-tested
upstream (ocirepository_verify_test.go). Upstream third-party Helm OCI charts
remain unverifiable (can't sign upstream artifacts) — out of scope.
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 33d74bdd-ec3e-4e4f-bd71-e8f043982385

📥 Commits

Reviewing files that changed from the base of the PR and between 23a7a7e and 8a158cb.

📒 Files selected for processing (1)
  • ksail.prod.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • ksail.prod.yaml

📝 Walkthrough

Walkthrough

Adds a verify block to ksail.prod.yaml for the flux-system OCIRepository, enabling Cosign keyless signature verification. The block uses matchOIDCIdentity to constrain accepted artifacts to signatures from GitHub Actions OIDC with subject matching the platform's ci.yaml or cd.yaml workflows, including merge-queue ref variants.

Changes

Cosign Keyless Verification for flux-system

Layer / File(s) Summary
Flux OCIRepository verify block
ksail.prod.yaml
Inserts a verify block with provider: cosign, secretRef, and matchOIDCIdentity rules constraining the OIDC issuer to the GitHub Actions token endpoint and subject to ci.yaml or cd.yaml workflow refs (including merge-queue variants).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A rabbit checks the signature line,
Only trusted workflows may sign!
GitHub Actions must vouch for the art,
Cosign guards each manifested part.
Merge-queue or main, the regex holds true—
No unsigned artifact gets through! 🔏

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The verify config matches the main goal, but the linked issue also calls for a signed publish path and docs update, which are not shown here. Add or reference the signing step, update docs with the verification identity, and validate the rollout and rejection cases.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: cosign verification for the infrastructure Flux OCI artifact.
Description check ✅ Passed The description is directly about enabling keyless cosign verification for the infrastructure OCIRepository.
Out of Scope Changes check ✅ Passed The change is limited to adding Flux verify config in ksail.prod.yaml and stays within the stated supply-chain scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/flux-verify-infra-oci

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ksail.prod.yaml`:
- Around line 138-155: This change edits protected production cluster config in
ksail.prod.yaml, so do not commit the verification override directly there;
instead remove the change from the live prod manifest and route it through the
approved protected-config exception/change-control process, or move the rendered
verify configuration to the intended allowed source used by KSail’s
reconciliation (the verify block under the generated OCIRepository). Keep the
subject/issuer matching logic in the generated source rather than hand-editing
ksail.prod.yaml so the reconciler-owned config remains the single source of
truth.
- Line 155: The OIDC subject matcher is too broad because the `subject` pattern
in the production config accepts any ref for the `ci.yaml` and `cd.yaml`
workflows. Tighten the matcher in the OIDC policy to only allow the trusted
production publishing refs, using the relevant subject rule near the existing
`subject` entry and the workflow names `ci.yaml`/`cd.yaml`; keep `main` and the
merge-queue prefix if those are the only approved sources, and avoid a catch-all
`@.+` unless all refs are intended to publish `latest`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 63bf2f14-ea4a-4cc6-b53a-3ca24286acdc

📥 Commits

Reviewing files that changed from the base of the PR and between 2e78edc and 23a7a7e.

📒 Files selected for processing (1)
  • ksail.prod.yaml

Comment thread ksail.prod.yaml Outdated
Comment thread ksail.prod.yaml Outdated
Address CodeRabbit review: `@.+` accepted a signature from ci.yaml/cd.yaml on
any ref. Constrain to the two production deploy paths confirmed against the live
signature: ci.yaml on the merge-queue ref (refs/heads/gh-readonly-queue/main/<pr>)
and cd.yaml on refs/heads/main. Tightens the trust surface on a high-blast-radius
source without changing legitimate signers (re-verified the live artifact still
passes).
@devantler devantler marked this pull request as ready for review June 28, 2026 17:49
@devantler devantler added this pull request to the merge queue Jun 28, 2026
Merged via the queue into main with commit a670ca9 Jun 28, 2026
14 checks passed
@devantler devantler deleted the claude/flux-verify-infra-oci branch June 28, 2026 17:51
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Jun 28, 2026
@botantler-1

botantler-1 Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.84.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

Supply-chain: cosign-verify the infrastructure Flux OCI artifact (extend #1559 beyond apps)

1 participant