(generated with claude sonnet 5)
Summary
OCIRepository.spec.verify.trustedRootSecretRef currently requires the supplied trusted_root.json to contain at least one transparency-log (Rekor) entry, and the underlying verification also requires at least one Certificate Transparency SCT on the signing certificate. Both requirements are hard-coded with no way to opt out via the CRD, even though the sigstore/cosign library that Flux already depends on fully supports skipping both checks in favor of RFC 3161 timestamp-only verification (CheckOpts.IgnoreTlog + CheckOpts.UseSignedTimestamps + CheckOpts.IgnoreSCT, exposed on the cosign CLI as --insecure-ignore-tlog --use-signed-timestamps --insecure-ignore-sct).
This makes it impossible to verify artifacts signed by private/enterprise Sigstore instances that don't operate a public transparency log or CT log by design — for example, GitHub's private Sigstore instance used for Artifact Attestations on private/internal repositories. GitHub explicitly does not log these to any transparency log (that's the entire point of the private instance — avoiding leaking private repo/workflow metadata into a public, permanent record), and substitutes an embedded RFC 3161 timestamp instead.
Current behavior
internal/oci/cosign/cosign.go, NewCosignVerifier:
rekorURL, err := rekorURLFromTrustedRoot(customRoot)
if err != nil {
return nil, fmt.Errorf("unable to extract Rekor URL from trusted root: %w", err)
}
If the supplied trusted root has zero tlogs entries, verifier construction fails outright before any signature/attestation checking is attempted.
Separately, even once past that, verification of a bundle with no CT-logged certificate fails with:
failed to verify signed certificate timestamp: only able to verify 0 SCT entries; unable to meet threshold of 1
Reproduction
- Sign/attest an OCI artifact using a private Sigstore instance whose trusted root has no transparency log (e.g.
gh attestation trusted-root's private-instance entry has "tlogs": [] and relies solely on timestampAuthorities).
- Configure an
OCIRepository with verify.provider: cosign, verify.trustedRootSecretRef pointing at that trusted root, no verify.secretRef.
- Reconciliation fails at verifier construction (
unable to extract Rekor URL from trusted root: no transparency log entries found in trusted root), before any signature is even checked.
Proposed solution
Add an explicit opt-in to OCIRepositoryVerification (naming to match cosign's own CLI convention), e.g.:
verify:
provider: cosign
trustedRootSecretRef:
name: private-sigstore-trusted-root
trustedInfrastructure: true # new field
When set, NewCosignVerifier skips rekorURLFromTrustedRoot/newRekorClient entirely and instead sets:
checkOpts.IgnoreTlog = true
checkOpts.UseSignedTimestamps = true
checkOpts.IgnoreSCT = true
This mirrors cosign's own --private-infrastructure / --insecure-ignore-tlog --use-signed-timestamps --insecure-ignore-sct combination almost exactly, and requires no changes elsewhere — the RFC 3161 timestamp and TSA cert chain needed for verification already flow through the same checkOpts.TrustedMaterial built from the supplied trusted root.
Evidence this works
I prototyped this exact change against a local fluxcd/source-controller checkout (pinned sigstore/cosign/v3 v3.0.6) and verified it end-to-end against a real OCI artifact carrying a real actions/attest@v4-produced Sigstore v3 bundle attestation (discovered via cosign.GetBundles/cosign.VerifyImageAttestations, so this also confirms Flux's existing bundle-discovery path already picks up actions/attest-produced attestations with no other changes needed):
- Correct
matchOIDCIdentity → verification succeeds.
- Deliberately wrong identity → verification fails closed with a clear certificate-identity-mismatch error, confirming this doesn't weaken identity-based access control, only the transparency-log/CT-log requirement.
Alternatives considered
- Inferring "TSA-only mode" automatically whenever the supplied trusted root has zero tlogs, rather than an explicit field. I'd lean against this for a real PR — it could silently downgrade security if a trusted root ends up empty due to misconfiguration rather than intentional design, whereas an explicit opt-in makes the tradeoff visible in the
OCIRepository spec itself.
Offer
Happy to open a PR for this if the approach (explicit opt-in field, mirroring cosign's own --private-infrastructure naming) sounds reasonable to maintainers.
(generated with claude sonnet 5)
Summary
OCIRepository.spec.verify.trustedRootSecretRefcurrently requires the suppliedtrusted_root.jsonto contain at least one transparency-log (Rekor) entry, and the underlying verification also requires at least one Certificate Transparency SCT on the signing certificate. Both requirements are hard-coded with no way to opt out via the CRD, even though thesigstore/cosignlibrary that Flux already depends on fully supports skipping both checks in favor of RFC 3161 timestamp-only verification (CheckOpts.IgnoreTlog+CheckOpts.UseSignedTimestamps+CheckOpts.IgnoreSCT, exposed on the cosign CLI as--insecure-ignore-tlog --use-signed-timestamps --insecure-ignore-sct).This makes it impossible to verify artifacts signed by private/enterprise Sigstore instances that don't operate a public transparency log or CT log by design — for example, GitHub's private Sigstore instance used for Artifact Attestations on private/internal repositories. GitHub explicitly does not log these to any transparency log (that's the entire point of the private instance — avoiding leaking private repo/workflow metadata into a public, permanent record), and substitutes an embedded RFC 3161 timestamp instead.
Current behavior
internal/oci/cosign/cosign.go,NewCosignVerifier:If the supplied trusted root has zero
tlogsentries, verifier construction fails outright before any signature/attestation checking is attempted.Separately, even once past that, verification of a bundle with no CT-logged certificate fails with:
Reproduction
gh attestation trusted-root's private-instance entry has"tlogs": []and relies solely ontimestampAuthorities).OCIRepositorywithverify.provider: cosign,verify.trustedRootSecretRefpointing at that trusted root, noverify.secretRef.unable to extract Rekor URL from trusted root: no transparency log entries found in trusted root), before any signature is even checked.Proposed solution
Add an explicit opt-in to
OCIRepositoryVerification(naming to match cosign's own CLI convention), e.g.:When set,
NewCosignVerifierskipsrekorURLFromTrustedRoot/newRekorCliententirely and instead sets:This mirrors cosign's own
--private-infrastructure/--insecure-ignore-tlog --use-signed-timestamps --insecure-ignore-sctcombination almost exactly, and requires no changes elsewhere — the RFC 3161 timestamp and TSA cert chain needed for verification already flow through the samecheckOpts.TrustedMaterialbuilt from the supplied trusted root.Evidence this works
I prototyped this exact change against a local
fluxcd/source-controllercheckout (pinnedsigstore/cosign/v3 v3.0.6) and verified it end-to-end against a real OCI artifact carrying a realactions/attest@v4-produced Sigstore v3 bundle attestation (discovered viacosign.GetBundles/cosign.VerifyImageAttestations, so this also confirms Flux's existing bundle-discovery path already picks upactions/attest-produced attestations with no other changes needed):matchOIDCIdentity→ verification succeeds.Alternatives considered
OCIRepositoryspec itself.Offer
Happy to open a PR for this if the approach (explicit opt-in field, mirroring cosign's own
--private-infrastructurenaming) sounds reasonable to maintainers.