Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Signing Pulp artifacts with Cosign and GPG

By updated

Improve this page

Pulp logo

This guide signs a container image with Cosign and then verifies it, all hosted by Pulp, and shows how to declare a GPG signing service to sign manifests, RPMs and Ansible collections on the server side. Intended audience: teams that want to prove the integrity and provenance of their artifacts. Prerequisites: a Pulp instance with the container registry enabled (see Installing Pulp), plus cosign, podman and gpg on the client machine.

Signing an artifact means attaching a cryptographic proof that it has not been altered since it was produced and that it really comes from you. Pulp handles signing in two complementary ways: Cosign/Sigstore signatures for container images (client side, Pulp hosts), and GPG signing services to sign manifests, RPM metadata and Ansible collections on the server side.

What you will learn

  • Tell apart a Cosign signature from a GPG signing service.
  • Sign a container image with Cosign and store it in Pulp.
  • Verify a Cosign signature served by Pulp.
  • Declare a GPG manifest signing service and attach it to a repository.

Two signing models

Pulp does not sign everything the same way depending on the content type: a container image and an RPM package follow two distinct paths. Understanding the difference saves you from looking for a command that does not exist.

ModelContentWhere the signature is produced
Cosign / SigstoreContainer imagesClient side (cosign, podman), Pulp hosts the signature
GPG signing serviceContainer manifests, RPM metadata, Ansible collectionsPulp server side, through a signing script

The first model relies on the Sigstore ecosystem: since Pulp is an OCI registry, it natively stores a Cosign signature as an OCI image next to the signed image. The second model relies on GPG and a script run by Pulp at publication time.

Signing an image with Cosign

The scenario: you push an image into Pulp, you sign it with Cosign (the signer of the Sigstore project), and any consumer can then verify the signature. Pulp hosts the signature with no particular server configuration. If Cosign is new to you, the dedicated Cosign guide details key generation and the role of Rekor.

  1. Push an image to a repository hosted by Pulp:

    Terminal window
    podman login pulp.exemple.com
    podman tag busybox:1.36 pulp.exemple.com/demo/busybox:1.36
    podman push pulp.exemple.com/demo/busybox:1.36
  2. Generate a Cosign key pair (private key protected by a password):

    Terminal window
    cosign generate-key-pair

    The command produces cosign.key (private) and cosign.pub (public). Keep the private key in a vault, never in a Git repository.

  3. Sign the image in Pulp. You authenticate to the registry, then sign. You pass the tag, but the tag is not what gets signed: Cosign resolves it to a digest and signs that digest, which designates a content and cannot be moved to another one.

    Terminal window
    cosign login pulp.exemple.com -u admin -p "MotDePasse"
    cosign sign --key cosign.key pulp.exemple.com/demo/busybox:1.36

    Cosign computes the digest, signs, and pushes the signature to Pulp under a dedicated tag of the form sha256-<digest>.sig. It is an OCI object like any other, hosted next to the image.

Verifying a Cosign signature

Verification proves that the image matches a signature issued by your key. The consumer only needs the public key:

Terminal window
cosign verify --key cosign.pub pulp.exemple.com/demo/busybox:1.36

The output confirms that the signatures were verified with the public key and shows the payload, with the type cosign container image signature and the image digest. An unsigned or modified image makes the command fail, which lets you block a deployment in a pipeline.

Signing on the server side with a GPG signing service

For container manifests, RPM metadata and Ansible collections, Pulp signs on the server side through a signing service: a script that Pulp runs, backed by a GPG key.

  1. Write the signing script. For a container manifest, it calls skopeo standalone-sign and returns the signature path in the JSON format expected by Pulp:

    #!/usr/bin/env bash
    MANIFEST_PATH=$1
    skopeo standalone-sign \
    "$MANIFEST_PATH" "$REFERENCE" "$PULP_SIGNING_KEY_FINGERPRINT" \
    --output "$SIG_PATH"
    if [ $? -eq 0 ]; then
    echo "{\"signature_path\": \"$SIG_PATH\"}"
    else
    exit 1
    fi

    The variables REFERENCE, PULP_SIGNING_KEY_FINGERPRINT and SIG_PATH are provided by Pulp; do not rename them. The GPG key must be installed in the keyring of the pulp user.

  2. Declare the signing service with the class that matches the content (here, container manifests):

    Terminal window
    pulpcore-manager add-signing-service ma-signature \
    /var/lib/pulp/scripts/sign-manifest.sh \
    45ACE14E3EBB9BBA \
    --class container:ManifestSigningService

    Pulp validates the script at registration, which prevents you from saving a broken script.

  3. Attach the service to the repository through its manifest_signing_service field. Images published from this repository are then signed automatically on the server side.

Managing keys securely

A signature is only worth as much as the secrecy of its private key. A few non-negotiable reflexes:

  • Never put a private key in Git or in an image: store it in a vault such as HashiCorp Vault (in French).
  • Protect the Cosign key with a password (COSIGN_PASSWORD) and a GPG key with a passphrase.
  • Publish the public key widely: it is what enables verification, and it does not need to be secret.
  • Combine with a scan: signing proves integrity, not the absence of vulnerabilities. Chain it with Trivy in CI.

Hands-on practice

The lab has you push two images into your repository, sign only one with Cosign, then verify both. The validation requires both results: verification accepts the signed image and rejects the other. A signature that lets everyone through protects nothing, and this is the only check that shows it. The lab also reveals that a signature is a registry object, not a file next to it: a read-only repository rejects it.

Key takeaways

  • Pulp signs in two ways: Cosign/Sigstore for images (client), GPG signing services for manifests, RPMs and collections (server).
  • A Cosign signature is stored by Pulp as an OCI object under a sha256-<digest>.sig tag.
  • cosign verify --key cosign.pub validates the image; a failure can block a deployment.
  • Pulp copies Cosign signatures during a synchronization.
  • A signing service (add-signing-service) runs a GPG script on the server side, attached to the repository through its signing field.
  • The private key never leaves a vault; signing does not replace vulnerability scanning.

Knowledge check

Eight questions on signing: what Cosign actually signs, where the signature is stored, and why a verification that accepts everything proves nothing.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn