Cosign Image Signing Setup Guide¶
This guide explains how to set up key-based container image signing using cosign for the Forma3D.Connect CI/CD pipeline.
Overview¶
The pipeline uses cosign to: 1. Sign Docker images after they are pushed to the registry 2. Create attestations when images are promoted to staging/production 3. Verify attestations before production deployments
This provides a cryptographic audit trail of image promotions without modifying image tags.
Prerequisites¶
Install cosign on your local machine:
# macOS
brew install cosign
# Linux
curl -sSL https://github.com/sigstore/cosign/releases/download/v2.2.4/cosign-linux-amd64 -o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
# Verify installation
cosign version
Step 1: Generate Cosign Keypair¶
Generate a new keypair for signing:
# Navigate to the project root
cd /path/to/forma-3d-connect
# Generate keypair (you will be prompted for a password)
cosign generate-key-pair
This creates two files:
- cosign.key - Private key (password-protected) - KEEP SECRET
- cosign.pub - Public key - Can be committed to the repository
Important: Choose a strong password. You'll need this password in the Azure DevOps pipeline.
Step 2: Store Private Key in Azure DevOps¶
The private key must be stored as a secure file in Azure DevOps:
- Navigate to Pipelines > Library > Secure files
- Click + Secure file
- Upload
cosign.key - After upload, click on the file and check "Authorize for use in all pipelines"
Step 3: Store Password in Variable Group¶
The password for the private key must be stored as a secret variable:
- Navigate to Pipelines > Library > Variable groups
- Open the
forma3d-stagingvariable group - Add a new variable:
| Variable | Secret | Value |
|---|---|---|
COSIGN_PASSWORD |
Yes | The password you used in Step 1 |
- Click Save
Step 4: Commit Public Key to Repository¶
The public key should be committed to the repository so it can be used for verification:
# The public key is already in the project root
git add cosign.pub
git commit -m "Add cosign public key for image signature verification"
git push
Step 5: Verify Setup¶
Run a pipeline to verify the setup is working:
- Navigate to Pipelines in Azure DevOps
- Select the pipeline and click Run pipeline
- Ensure "Enable Image Signing & Attestation" is checked
- Run the pipeline
The signing steps should now complete successfully.
File Locations Summary¶
| File | Location | Purpose |
|---|---|---|
cosign.key |
Azure DevOps > Library > Secure Files | Sign images |
COSIGN_PASSWORD |
Azure DevOps > forma3d-staging group | Decrypt private key |
cosign.pub |
Repository root | Verify signatures |
Local Verification¶
Using the Promotion Script (Recommended)¶
The easiest way to view all images and their signing/promotion status is using the provided script:
# Authenticate and list all images with signature verification
./scripts/list-image-promotions.sh --key cosign.pub
# Filter to specific repositories
./scripts/list-image-promotions.sh --key cosign.pub --filter forma3d-connect
# Show detailed attestation info
./scripts/list-image-promotions.sh --key cosign.pub --verbose
Important: The --key option is required for accurate signature verification. Without it, signed images may incorrectly show as unsigned because cosign needs the public key to verify key-based signatures.
Using Cosign Directly¶
You can also verify signed images directly with cosign:
# Authenticate to the registry first
doctl registry login
# Verify an image signature
cosign verify \
--key cosign.pub \
registry.digitalocean.com/forma-3d/forma3d-connect-api:20260114120000
# View attestations attached to an image
cosign tree registry.digitalocean.com/forma-3d/forma3d-connect-api:20260114120000
# Verify and view attestation content
cosign verify-attestation \
--key cosign.pub \
--type custom \
registry.digitalocean.com/forma-3d/forma3d-connect-api@sha256:abc123... \
| jq '.payload | @base64d | fromjson | .predicate'
Note: Running
doctl registry loginis required before cosign commands to authenticate Docker to the DigitalOcean Container Registry.
Troubleshooting¶
Error: "incorrect password"¶
The COSIGN_PASSWORD variable doesn't match the password used when generating the keypair.
Solution: Re-check the password in the variable group or regenerate the keypair.
Error: "no matching signatures"¶
The image was not signed, or the signature doesn't match the public key.
Solution: - Verify the image was signed with the correct private key - Ensure the public key in the repository matches the private key used
Error: "invalid key"¶
The secure file is corrupted or not a valid cosign key.
Solution:
- Re-generate the keypair and re-upload cosign.key
- Ensure the file wasn't modified during upload
Error: "401 Unauthorized" when verifying¶
Cosign cannot authenticate to the DigitalOcean Container Registry.
Solution:
- Run doctl registry login to authenticate Docker to the registry
- The list-image-promotions.sh script handles this automatically
Security Best Practices¶
- Rotate keys periodically - Generate new keypairs every 6-12 months
- Use strong passwords - At least 16 characters with mixed case, numbers, and symbols
- Limit access - Only pipeline service accounts should have access to the private key
- Audit signatures - Regularly verify that production images have valid attestations
- Backup keys securely - Store the private key in a secure password manager
Key Rotation Procedure¶
When rotating keys:
- Generate a new keypair:
cosign generate-key-pair --output-key-prefix=cosign-new - Upload
cosign-new.keyto Azure DevOps secure files - Update
COSIGN_PASSWORDin the variable group - Replace
cosign.pubwithcosign-new.pubin the repository - Update the pipeline to use the new key file name (or rename to
cosign.key) - Delete the old secure file from Azure DevOps
Note: Images signed with the old key will still be verifiable with the old public key. Consider keeping the old public key archived for historical verification.