Keyless Signing Has Two Hard Blockers Here

Cosign's keyless mode needs a CI platform Fulcio trusts and a live path to public Sigstore at admission time. This cluster fails one of those for one CI platform, and fails the other for both — so the phase-in policy ships key-based only.

Keyless Signing Has Two Hard Blockers Here

04-verify-image-signature-cosign.yaml ships two verification rules side by side: verify-key-based and verify-keyless. Keyless is the mode everyone wants — no key pair, no OpenBao round trip, just an OIDC identity token and a public transparency log. The header comment on that file explains, in two separate paragraphs, why it isn't the one this cluster can actually turn on.

Blocker one: not every CI platform can hand Kyverno a token Fulcio trusts

Keyless verification works by checking that the image was signed using an OIDC identity token from an issuer Fulcio trusts, then matching the signing certificate's subject and issuer against the policy. GitLab CE 18.9 issues exactly that kind of token — CI_JOB_JWT_V2 and id_tokens: OIDC claims — with no Ultimate licence required, so keyless is structurally available there. Azure DevOps has no Fulcio-trusted OIDC issuer today. Its pipelines have no way to produce a token the verify-keyless rule's issuer and subject matchers would accept, full stop. For Azure DevOps, key-based verification isn't a preference — it's the only rule that can pass.

Blocker two: the one platform that clears blocker one still doesn't get a pass

GitLab CE clears blocker one. It still can't use keyless here, because keyless verification needs the cluster itself to reach fulcio.sigstore.dev and rekor.sigstore.dev at admission time, for every Pod create that matches the rule. That turns the public Sigstore service's uptime into a dependency of this cluster's scheduler — logged separately as a real availability risk (K083). A transient Sigstore outage doesn't just delay a deploy; with failurePolicy: Fail it can stall every pod creation the rule matches, cluster-wide, for a service this cluster does not control and cannot page.

Keyless verification's two blockers next to the key-based path that avoids both
Keyless verification's two blockers next to the key-based path that avoids both

One CI platform fails blocker one outright; the platform that clears it still fails blocker two — so both routes land on key-based.

What key-based avoids

verify-key-based needs a cosign key pair instead of an OIDC issuer. The public key is safe to embed directly in the ClusterPolicy — the POC's version is a throwaway P-256 key generated with openssl ecparam, valid PEM so the verification code path actually runs, signing nothing real. In a real rollout the private key lives in OpenBao and reaches CI as an ExternalSecret or a masked CI variable; the public key ships in the policy itself. Verification then needs registry access only — no egress to Sigstore, no dependency on an external identity provider, and it works identically whether the pipeline is GitLab CE or Azure DevOps. That's why the phase-in policy sets rekor.ignoreTlog: true on the key-based rule: it deliberately opts out of the transparency log, because avoiding a live Sigstore dependency is the entire point.

The POC could not go further than validating this structure — signing a real image needs a key pair and a registry the POC was allowed to write signature artifacts to, and keyless needs public Sigstore egress this cluster shouldn't have anyway. What's proven is the shape of the policy, not a verified signature. That's still enough to know which rule ships first.

Next: Harbor's pull-blocking does not block an OCI index — the same vulnerable content, blocked as a plain manifest, waved through as an index.