Skip to main content

Sign containers with Podman using GPG Smart Card Daemon (SCD)

Podman is an open-source container runtime that provides a compatible interface with the Docker command-line interface (CLI) while functioning as a daemonless container engine. Podman is used to sign and verify container images.

Podman uses your private key to sign the container image and embedding the corresponding public key within the image. Users can then verify the image's authenticity and integrity by checking the attached signature against a set of trusted public keys. Podman integrates into existing container ecosystems and provides a user-friendly experience for managing containers, while maintaining compatibility and consistency with other OCI-compliant container runtimes

Tip

Podman's CLI that will feel familiar to anyone who has used the Docker Container Engine. If you have scripts or commands that use Docker, you can easily switch to Podman by replacing the alias docker with podman (alias docker=podman).

Podman relies on an OCI-compliant Container Runtime (such as runc, crun, runv, etc.) to interact with the operating system and create running containers. This adherence to the Open Container Initiative (OCI) standards ensures that containers created by Podman are almost indistinguishable from those created by any other common container engine.

Follow these instructions to sign directly with Podman and securely reference your private key stored in Software Trust Manager using our GPG Smart Card Daemon. Alternatively, Software Trust Manager offers container signing via CoSign or Podman.

Prerequisites

The following must be set up on the system used for signing:

Sign

To sign a container image with Podman:

  1. Run a local container registry.

    sudo podman run -d -p 5000:5000 docker.io/registry
  2. Pull a standard hello-world container image from Docker Hub.

    sudo podman pull docker://docker.io/hello-world:latest
  3. Retag the image for the local registry.

    sudo podman tag hello-world localhost:5000/hello-world
  4. Check all the hello-world images are listed.

    sudo podman images hello-world

    Command output

    REPOSITORY  TAG  IMAGE ID  CREATED  SIZE
    docker.io/library/hello-world  latest  feb5d9fea6a5  9 months ago  19.9 kB
    localhost:5000/hello-world-podman  latest  feb5d9fea6a5  9 months ago  19.9 kB
  5. Podman pushes the image and signs it in one command. Modify our systemwide registries configuration at /etc/containers/registries.d/default.yaml.

    default-docker:
    sigstore: http://localhost:8000 # Added by us
    sigstore-staging: [file:///var/lib/containers/sigstore](/var/lib/containers/sigstore)
  6. List the GPG keys.

    gpg --list-keys

    Command output

    /home/name/.gnupg/pubring.gpg
    ---------------------------------
    pub  rsa3072 2022-06-10 [C]
    73246D13C71FC3A494C1466811860DF055A7F994
    uid  [ unknown] John <john.doe@example.com>
    sub  rsa3072 2022-06-10 []
    pub  rsa3072 2022-06-10 [SC]
    17F83D90B6BA87C9D32124ED3300944D8D32931A
    uid  [ unknown] John <john.doe@example.com>
    sub  rsa3072 2022-06-10 []
  7. Sign the image using Podman and push it to registry.

    sudo -E GNUPGHOME=$HOME/.gnupg podman push –tls-verify=false –sign-by 17F83D90B6BA87C9D32124ED3300944D8D32931A localhost:5000/hello-world

    Command output

    Getting image source signatures
    Copying blob 24302eb7d908 done
    Writing manifest to image destination
    Signing manifest
    Storing signatures
  8. Remove the local image for verification.

    sudo podman image rm localhost:5000/hello-world
  9. Write a policy to enforce that the signature has to be valid:

    1. Add a new rule in /etc/containers/policy.json

    2. Copy the docker entry into the transports section of your policy.json file.

      gpg --output /tmp/key.gpg --armor --export john2
      vi /etc/containers/policy.json

      Command output

      {
      "default": [{ "type": "insecureAcceptAnything" }],
      "transports": {
      "docker": {
      "localhost:5000": [
      {
      "type": "signedBy",
      "keyType": "GPGKeys",
      "keyPath": "/tmp/key.gpg"
      }
      ]
      }
      }
      }
  10. Pull the image with the correct key at /tmp/key.gpg

    sudo podman pull --tls-verify=false localhost:5000/alpine

    Command output

    Trying to pull localhost:5000/alpine:latest...
    Getting image source signatures
    Checking if image destination supports signatures
    Copying config e66264b987 done
    Writing manifest to image destination
    Storing signatures
    e66264b98777e12192600bf9b4d663655c98a090072e1bab49e233d7531d1294
  11. If you specify the wrong key at /tme/key.gpg and attempt to pull the image:

    sudo podman pull --tls-verify=false localhost:5000/hello-world
    Trying to pull localhost:5000/hello-world...
    Error: error pulling image "localhost:5000/hello-world": unable to pull localhost:5000/hello-world: unable to pull image: Source image rejected: Invalid GPG signature: …