Skip to content

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:

  1. Navigate to Pipelines > Library > Secure files
  2. Click + Secure file
  3. Upload cosign.key
  4. 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:

  1. Navigate to Pipelines > Library > Variable groups
  2. Open the forma3d-staging variable group
  3. Add a new variable:
Variable Secret Value
COSIGN_PASSWORD Yes The password you used in Step 1
  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:

  1. Navigate to Pipelines in Azure DevOps
  2. Select the pipeline and click Run pipeline
  3. Ensure "Enable Image Signing & Attestation" is checked
  4. 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

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 login is 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

  1. Rotate keys periodically - Generate new keypairs every 6-12 months
  2. Use strong passwords - At least 16 characters with mixed case, numbers, and symbols
  3. Limit access - Only pipeline service accounts should have access to the private key
  4. Audit signatures - Regularly verify that production images have valid attestations
  5. Backup keys securely - Store the private key in a secure password manager

Key Rotation Procedure

When rotating keys:

  1. Generate a new keypair: cosign generate-key-pair --output-key-prefix=cosign-new
  2. Upload cosign-new.key to Azure DevOps secure files
  3. Update COSIGN_PASSWORD in the variable group
  4. Replace cosign.pub with cosign-new.pub in the repository
  5. Update the pipeline to use the new key file name (or rename to cosign.key)
  6. 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.