Image Signing
Image signing lets you prove who built an image and whether it has been changed since it left CI. Signing is optional by default on Kupe, but it is a strong practice for production workloads.
Recommended: Kupe Reusable Workflow
Section titled “Recommended: Kupe Reusable Workflow”If you publish container images from GitHub Actions to GHCR, the easiest
path is the public
kupecloud/github-workflows
repository. Its reusable
publish-image.yaml
workflow already:
- builds and pushes your image to
ghcr.io/<owner>/<repo> - publishes immutable
sha-<git-sha>tags - publishes semantic version tags
- signs the pushed image digest with cosign using GitHub OIDC
Reference it from your repository and pin it to a full commit SHA:
name: Publish Image
on: push: tags: - "1.*.*"
permissions: contents: read packages: write id-token: write
jobs: publish: uses: kupecloud/github-workflows/.github/workflows/publish-image.yaml@<full-commit-sha> with: version: ${{ github.ref_name }} secrets: inheritThe workflow expects version without a leading v. If your release
tags are shaped like v1.2.3, strip the prefix before calling it.
This is the recommended path if you want the simplest supported setup.
Why Sign Your Images
Section titled “Why Sign Your Images”Image signing proves:
- Who built it — the signature is tied to your CI identity (e.g., your GitHub Actions workflow)
- It hasn’t been tampered with — any modification after signing invalidates the signature
- When it was built — an immutable timestamp is recorded in a public transparency log
Without signing, you’re trusting that the image in your registry is exactly what your CI built. Signing removes that trust assumption.
Alternative: Sign Your Images Manually in GitHub Actions
Section titled “Alternative: Sign Your Images Manually in GitHub Actions”If you need a different registry, custom build logic, or full control
over tags and build arguments, keep the workflow local. Prefer publishing an
immutable Git SHA tag for every image build, and keep tags like
latest or release tags only as convenience aliases:
name: Build and Pushon: push: branches: [main]
jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write # Required for keyless signing
steps: - uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v3
- uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push id: build uses: docker/build-push-action@v6 with: push: true tags: | ghcr.io/your-org/your-image:${{ github.sha }} ghcr.io/your-org/your-image:latest
- name: Sign image run: | cosign sign --yes \ ghcr.io/your-org/your-image@${{ steps.build.outputs.digest }}That’s it. The cosign sign --yes command:
- Requests a short-lived signing certificate from Sigstore’s CA using your GitHub OIDC token
- Signs the image digest with an ephemeral key
- Records the signature in a public transparency log
- Attaches the signature to your image in the registry
- Destroys the ephemeral key — no secrets to manage
Key Points
Section titled “Key Points”- No keys to manage — signing uses your GitHub OIDC identity, not long-lived secrets
- No infrastructure — uses free public Sigstore services
id-token: write— this permission is required for GitHub to issue the OIDC token- Prefer immutable tags — publish a Git SHA tag for every build and use that in deployments when possible
- Sign the digest, not the tag — tags are mutable, digests are not
Verifying Signatures Locally
Section titled “Verifying Signatures Locally”You can verify any signed image:
# Install cosignbrew install cosign
# Verify a signed imagecosign verify \ --certificate-identity "https://github.com/your-org/your-repo/.github/workflows/build.yaml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/your-org/your-image:${GIT_SHA}If verification succeeds, you’ll see the signing certificate details. If the image is unsigned or the signature doesn’t match, the command fails.
Enforcing Verification in Your Cluster
Section titled “Enforcing Verification in Your Cluster”If you want to ensure only signed images run in your managed cluster, contact support to discuss a tenant-specific verification policy. A typical rollout:
- checks your images against the signing identity you use in CI
- can start in audit mode so you can see what would be blocked
- can move to enforcement once your team is ready
Related
Section titled “Related”- Vulnerability Scanning — CVE scanning for your images
- Container Security — security context requirements