
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.
| Model | Content | Where the signature is produced |
|---|---|---|
| Cosign / Sigstore | Container images | Client side (cosign, podman), Pulp hosts the signature |
| GPG signing service | Container manifests, RPM metadata, Ansible collections | Pulp 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.
-
Push an image to a repository hosted by Pulp:
Terminal window podman login pulp.exemple.compodman tag busybox:1.36 pulp.exemple.com/demo/busybox:1.36podman push pulp.exemple.com/demo/busybox:1.36 -
Generate a Cosign key pair (private key protected by a password):
Terminal window cosign generate-key-pairThe command produces
cosign.key(private) andcosign.pub(public). Keep the private key in a vault, never in a Git repository. -
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.36Cosign 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:
cosign verify --key cosign.pub pulp.exemple.com/demo/busybox:1.36The 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.
-
Write the signing script. For a container manifest, it calls
skopeo standalone-signand returns the signature path in the JSON format expected by Pulp:#!/usr/bin/env bashMANIFEST_PATH=$1skopeo standalone-sign \"$MANIFEST_PATH" "$REFERENCE" "$PULP_SIGNING_KEY_FINGERPRINT" \--output "$SIG_PATH"if [ $? -eq 0 ]; thenecho "{\"signature_path\": \"$SIG_PATH\"}"elseexit 1fiThe variables
REFERENCE,PULP_SIGNING_KEY_FINGERPRINTandSIG_PATHare provided by Pulp; do not rename them. The GPG key must be installed in the keyring of thepulpuser. -
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:ManifestSigningServicePulp validates the script at registration, which prevents you from saving a broken script.
-
Attach the service to the repository through its
manifest_signing_servicefield. 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>.sigtag. cosign verify --key cosign.pubvalidates 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
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
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Pulp air-gapped: self-signed HTTPS and offline transfer
A new full quiz with random questions
Work again only on the questions you missed