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:
GPG tool(s)
Podman
Container registry where Podman can pull or push the containers to.
DigiCert® Software Trust Manager Smart Card Daemon for GPG (ssm-scd)
Sign
To sign a container image with Podman:
Run a local container registry.
sudo podman run -d -p 5000:5000 docker.io/registry
Pull a standard hello-world container image from Docker Hub.
sudo podman pull docker://docker.io/hello-world:latest
Retag the image for the local registry.
sudo podman tag hello-world localhost:5000/hello-world
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
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)
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 []
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
Remove the local image for verification.
sudo podman image rm localhost:5000/hello-world
Write a policy to enforce that the signature has to be valid:
Add a new rule in
/etc/containers/policy.json
Copy the
docker
entry into thetransports
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" } ] } } }
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
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: …