73 lines
2.8 KiB
Markdown
73 lines
2.8 KiB
Markdown
# 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](https://docs.sigstore.dev/cosign/overview/) 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# 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 `stage` action so promote runs do not
|
|
re-sign already-signed digests.
|