Skip to content

Enabling Container Signing

Galaxy as a container registry can handle container manifest signatures, the server can accept image push with signatures attached and can also create signatures on-demand via UI and API using a Pulp Signing Service.

Enabling signing on pulp installer

Pulp installer can also be configured to enable container signing if you configures using pulp-installer you can skip the Creating Container Signing Service section of this page.

Pushing images with signatures

Pushing a tagged image altogether with its signature to the Galaxy Registry pass the --sign-by argument to the client.

podman push --tls-verify=false --sign-by localhost:5001

Signing Service

A signing service is an object defined on the pulp backend that combines a GPG key and the absolute path of an executable script.

Creating a signing service

To create a signing service on the Galaxy server it is needed to access the django-admin utility and execute:


The command must run inside the Python Environment where galaxy_ng workers are installed.

Bash Command
django-admin add-signing-service \  #(1)
    unique-name \  #(2)
    /abs/path/to/ \ #(3)
    GPG_KEY_ID #(4)
    --class container:ManifestSigningService #(5)
  1. A django management command.
  2. Name the signing_service should get in the database.
  3. Absolute path to Shell script where the signing is performed.
  4. Key id of the public key.
  5. For container it is needed to inform the --class argument

The positional arguments to the add-signing-service command:

The named argument --class

  • --class needs to be set to value container:ManifestSigningService so additional checks can be performed.


When importing the key to the keyring the trust level must be set to some value higher than 3. ex: echo "${KEY_FINGERPRINT}:6:" | gpg --batch --import-ownertrust

The signing script

Pulp Container provides an example of a script that uses skopeo to produce signatures

#!/usr/bin/env bash
# This GPG_TTY variable might be needed on a container image that is not running as root.
# export GPG_TTY=$(tty)

# pulp_container SigningService will pass the next 4 variables to the script.

# Create container signature using skopeo
skopeo standalone-sign \
  --output $SIGNATURE_PATH

# Optionally pass the passphrase to the key if password protected.
# --passphrase-file /path/to/key_password.txt

# Check the exit status
if [ $STATUS -eq 0 ]; then
  echo {\"signature_path\": \"$SIGNATURE_PATH\"}
  exit $STATUS

Using the key and script to create the signing service

Creating the signing service
django-admin add-signing-service container-default \
  /var/lib/pulp/scripts/ \
  --class container:ManifestSigningService

Configuring Galaxy to use the Signing Service

To tell galaxy which signing service to use you need to set it in the pulp settings.

Option 1:


  1. The name of the created signing service

Option 2:

Environment Variable

  1. The name of the created signing service

Signing via API

Galaxy calls Pulp Container API to trigger sign so refer to Pulp docs in case you need to use the API for signing

Signing via UI

On the UI under the Execution Environment menu there are buttons to trigger sign for a specific container image.


To perform signing actions the user must be a superuser (admin) or need modify_content_containerpushrepository permissions provided also by the Roles execution environment admin, execution environment publisher, execution environment namespace owner, execution environment collaborator

Signature verification

Obtaining the public key

The public keys for the signing services on a Galaxy Server are exposed on the URL https://FQDN/pulp/api/v3/signing-services/ and the respective UI page calles Public Keys

If the signature comes from a remote server then its public key must be found on the remote directly.

Verification during installation

In order to verify a container image signature the client (podman, docker) needs to be configured with the policy file as described in


cat  /etc/containers/policy.json
  "default": [{"type": "reject"}],
  "transports": {
    "docker": {
       "": [
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/path-to-pupsik-key.gpg"
    "containers-storage": {
    "": [{"type": "insecureAcceptAnything"}] /* Allow copy operations on any images stored in containers storage (e.g. podman push) */

The execution of client command must be like described on

Client verifying container image signature
podman pull
Trying to pull
Getting image source signatures
Checking if image destination supports signatures
Copying blob 58147e24f776 skipped: already exists
Copying config 829374d342 done
Writing manifest to image destination
Storing signatures