2.8 KiB
Container Image Signing (cosign)
The Gitea deploy workflow (.gitea/workflows/deploy.yml) attempts to sign every
container image it publishes to the Gitea Package Registry using
cosign with a long-lived keypair.
The signing step is skip-safe: if COSIGN_PRIVATE_KEY or COSIGN_PASSWORD is
not configured, the workflow logs a notice and continues without signatures.
Promote runs never rebuild — they retag the staging digest as :main — so a
single successful signing pass per commit covers both staging and production
deployments.
What gets signed
For each push to main, the workflow signs the following images at their
full-SHA tag:
{registry}/{namespace}/web:<sha>{registry}/{namespace}/api:<sha>{registry}/{namespace}/worker:<sha>
Because cosign signatures are bound to the manifest digest (not the tag), the
:staging and :main aliases that are later created via
docker buildx imagetools create resolve to the same digest and therefore the
same signature.
Required Gitea secrets
Configure the following repository-level secrets in Gitea. The signing step is SKIP-safe — if either secret is unset, the workflow logs a notice and continues.
| Secret | Description |
|---|---|
COSIGN_PRIVATE_KEY |
PEM-encoded cosign private key (output of cosign generate-key-pair). |
COSIGN_PASSWORD |
Password protecting the private key. |
Generate the keypair locally with:
cosign generate-key-pair
# emits cosign.key (private, set as COSIGN_PRIVATE_KEY) and cosign.pub (public)
Commit the resulting cosign.pub to the repository root (optional but
recommended) so downstream verifiers can fetch it directly from Git.
Verifying an image
Anyone pulling a published image can verify authenticity with the public key:
# Using the public key from this repo (if checked in as cosign.pub):
cosign verify --key cosign.pub \
{registry}/{namespace}/web:main
# Or pin to a specific revision:
cosign verify --key cosign.pub \
{registry}/{namespace}/web:<full-sha>
A successful verification prints the signed payload as JSON and exits 0. A mismatched, skipped, or missing signature exits non-zero.
Operational notes
- Signatures are stored as OCI artifacts alongside the image in the Gitea Package Registry. No extra infrastructure is required.
- Rotating the keypair: generate a new pair, update the Gitea secrets, and
replace
cosign.pub. Older images remain verifiable against the previous public key kept in Git history. - The signing step runs only during the
stageaction so promote runs do not re-sign already-signed digests.