Red Hat Training

A Red Hat training course is available for RHEL 8

Chapter 4. Signing container images

You can use a GNU Privacy Guard (GPG) signature or a sigstore signature to sign your container image. Both signing techniques are generally compatible with any OCI compliant container registries. You can use Podman to sign the image before pushing it into a remote registry and configure consumers so that any unsigned image is rejected. Signing container images helps to prevent supply chain attacks.

Signing using GPG keys requires deploying a separate lookaside server to distribute signatures. The lookaside server can be any HTTP server. Starting with Podman version 4.2, you can use the sigstore format of container signatures. Compared to the GPG keys, the separate lookaside server is not required because the sigstore signatures are stored in the container registry.

4.1. Signing container images with GPG signatures

You can sign images using a GNU Privacy Guard (GPG) key.

Prerequisites

  • The GPG tool is installed.
  • The lookaside web server is set up and you can publish files on it.

    • You can check the system-wide registries configuration in the /etc/containers/registries.d/default.yaml file. The lookaside-staging option references a file path for signature writing and is typically set on hosts publishing signatures.

      # cat /etc/containers/registries.d/default.yaml
      docker:
          <registry>:
              lookaside: https://registry-lookaside.example.com
              lookaside-staging: file:///var/lib/containers/sigstore
      ...

Procedure

  1. Generate a GPG key:

    # gpg --full-gen-key
  2. Export the public key:

    # gpg --output <path>/key.gpg --armor --export <username>@redhat.com
  3. Build the container image using Containerfile in the current directory:

    $ podman build -t <registry>/<namespace>/<image>

    Replace <registry>, <namespace>, and <image> with the container image identifiers. For more details, see Container registries.

  4. Sign the image and push it to the registry:

     $  podman push \
        --sign-by <username>@redhat.com \
        <registry>/<namespace>/<image>
    Note

    If you need to sign existing images while moving them across container registries, you can use the skopeo copy command.

  5. Optional. Display the new image signature:

    # (cd /var/lib/containers/sigstore/; find . -type f)
    ./<image>@sha256=<digest>/signature-1
  6. Copy your local signatures to the lookaside web server:

    # rsync -a /var/lib/containers/sigstore user@registry-lookaside.example.com:/registry-lookaside/webroot/sigstore

The signatures are stored in the location determined by the lookaside-staging option, in this case, /var/lib/containers/sigstore directory.

Verification

Additional resources

4.2. Verifying GPG image signatures

You can verify that a container image is correctly signed with a GPG key using the following procedure.

Prerequisites

  • The web server for a signature reading is set up and you can publish files on it.

    • You can check the system-wide registries configuration in the /etc/containers/registries.d/default.yaml file. The lookaside option references a web server for signature reading. The lookaside option has to be set for verifying signatures.

      # cat /etc/containers/registries.d/default.yaml
      docker:
          <registry>:
              lookaside: https://registry-lookaside.example.com
              lookaside-staging: file:///var/lib/containers/sigstore
      ...

Procedure

  1. Update a trust scope for the <registry>:

    $ podman image trust set -f <path>/key.gpg <registry>/<namespace>
  2. Optional. Verify the trust policy configuration by displaying the /etc/containers/policy.json file:

    $ cat /etc/containers/policy.json
    {
      ...
      "transports": {
        "docker": {
          "<registry>/<namespace>": [
            {
              "type": "signedBy",
              "keyType": "GPGKeys",
              "keyPath": "<path>/key.gpg"
            }
          ]
        }
      }
    }
    Note

    Typically, the /etc/containers.policy.json file is configured at a level of organization where the same keys are used. For example, <registry>/<namespace> for a public registry, or just a <registry> for a single-company dedicated registry.

  3. Pull the image:

    # podman pull <registry>/<namespace>/<image>
    ...
    Storing signatures
    e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a

    The podman pull command enforces signature presence as configured, no extra options are required.

Note

You can edit the system-wide registry configuration in the /etc/containers/registries.d/default.yaml file. You can also edit the registry or repository configuration section in any YAML file in the /etc/containers/registries.d directory. All YAML files are read and the filename can be arbitrary. A single scope (default-docker, registry, or namespace) can only exist in one file within the /etc/containers/registries.d directory.

Important

The system-wide registries configuration in the /etc/containers/registries.d/default.yaml file allows accessing the published signatures. The sigstore and sigstore-staging options are now deprecated. These options refer to signing storage, and they are not connected to the sigstore signature format. Use the new equivalent lookaside and lookaside-staging options instead.

Additional resources

  • podman-image-trust man page
  • podman-pull man page

4.3. Signing container images with sigstore signatures

Starting with Podman version 4.2, you can use the sigstore format of container signatures.

Prerequisites

  • The public and private key pair exists.
Note

The generation of public and private keys is not implemented. You must use the upstream Cosign project to generate a public and private key pair:

  1. Install the cosign tool:

    $ git clone https://github.com/sigstore/cosign
    $ cd cosign
    $ make ./cosign
  2. Generate a public and private key pair:

    $ ./cosign generate-key-pair
    ...
    Private key written to cosign.key
    Public key written to cosign.pub

Procedure

  1. Add the following content to the /etc/containers/registries.d/default.yaml file:

    docker:
        <registry>:
            use-sigstore-attachments: true

    By setting the use-sigstore-attachments option, Podman and Skopeo can read and write the container sigstore signatures together with the image and save them in the same repository as the signed image.

    Note

    You can edit the system-wide registry configuration in the /etc/containers/registries.d/default.yaml file. You can also edit the registry or repository configuration section in any YAML file in the /etc/containers/registries.d directory. All YAML files are read and the filename can be arbitrary. A single scope (default-docker, registry, or namespace) can only exist in one file within the /etc/containers/registries.d directory.

  2. Build the container image using Containerfile in the current directory:

    $ podman build -t <registry>/<namespace>/<image>
  3. Sign the image and push it to the registry:

    $ podman push --sign-by-sigstore-private-key ./cosign.key <registry>/<namespace>/image>

    The podman push command pushes the <registry>/<namespace>/<image> local image to the remote registry as <registry>/<namespace>/<image>. The --sign-by-sigstore-private-key option adds a sigstore signature using the cosign.key private key to the <registry>/<namespace>/<image> image. The image and the sigstore signature are uploaded to the remote registry.

Note

If you need to sign existing images while moving them across container registries, you can use the skopeo copy command.

Verification

Additional resources

4.4. Verifying sigstore image signatures

You can verify that a container image is correctly signed using the following procedure.

Procedure

  1. Add the following content to the /etc/containers/registries.d/default.yaml file:

    docker:
        <registry>:
            use-sigstore-attachments: true

    By setting the use-sigstore-attachments option, Podman and Skopeo can read and write the container sigstore signatures together with the image and save them in the same repository as the signed image.

    Note

    You can edit the system-wide registry configuration in the /etc/containers/registries.d/default.yaml file. You can also edit the registry or repository configuration section in any YAML file in the /etc/containers/registries.d directory. All YAML files are read and the filename can be arbitrary. A single scope (default-docker, registry, or namespace) can only exist in one file within the /etc/containers/registries.d directory.

  2. Edit the /etc/containers/policy.json file to enforce sigstore signature presence:

    ...
    "transports": {
                "docker": {
                    "<registry>/<namespace>": [
                        {
                            "type": "sigstoreSigned",
                            "keyPath": "/some/path/to/cosign.pub"
                        }
                    ]
                }
            }
    ...

    By modifying the /etc/containers/policy.json configuration file, you change the trust policy configuration. Podman, Buildah, and Skopeo enforce the existence of the container image signatures.

  3. Pull the image:

    $ podman pull <registry>/<namespace>/<image>

The podman pull command enforces signature presence as configured, no extra options are required.