Karanvir's Blog

How to sign container images with custom signature?

Published 2 days ago5 min read4 comments

Signatures on container images are crucial for security, authenticity, and trust in containerized environments. They play a significant role in ensuring that the images used in production or development environments are secure and have not been tampered with. Here are the key reasons for using signatures on container images:

  • Integrity Verification and Preventing Unauthorized Modifications
  • By verifying the signature, users can ensure that the image's content matches what the author originally intended. Without a signature, an image could be maliciously modified, and those changes would be undetectable. Signing ensures that any alteration of the image after it was signed would invalidate the signature.

  • Authentication of the Image Source.
  • A signature provides a way to verify the identity of the image's creator or publisher. Users can confirm that the image indeed comes from a trusted source and is not a forgery

Prerequisites

Before you start, ensure you have

  • Access to registry where you want to push the signed images.
  • A RHEL based system with podman installed,

Create GPG key to sign the container image

  1. Create key and list it.
    								
    gpg --full-gen-key
    gpg --list-keys
    								
    							
  2. Then export the gpg key to a file. In place of kay@example.com you have to provide the owner name which you given while creating the key.
    								
    mkdir /root/signing-container-images;cd signing-container-images
    gpg --output ./mykey.gpg --armor --export kay@example.com
    cat ./mykey.gpg
    								
    							

Now build the image

Clone the repository from git and then build the image. That containerfile is using a redhat ubi image which can be accessed from redhat registry wihtout any authentication

								
git clone https://github.com/kaybee-singh/signing-container-images /root/signing-container-images
cd /root/signing-container-images
podman build -t webs:1 -f containerfile .
								
							

Tag and push to the registry

Tag image by using podman, you have to replace the quay-all.example.com with your registry URL

								
podman tag localhost/webs:1 quay-all.example.com/quayuser/a/webs:1
podman login quay-all.example.com --tls-verify=false
podman push quay-all.example.com/quayuser/a/webs:1 --tls-verify=false
								
							

Configure web server to host the signature files

In this example we are configuring the web server on the same system where our registry is configured, as our registry is utilizing the 80 and 443 port we are configuring the web server on 90 port.

								
yum install httpd -y
sed -i 's/^Listen 80$/Listen 90/' /etc/httpd/conf/httpd.conf
systemctl enable httpd --now
firewall-cmd --add-port 90/tcp --permanent
firewall-cmd --reload
setenforc 0
								
							

Copy the signature directory to webserver document root.

								
cp -R /var/lib/containers/sigstore/quayuser /var/www/html
ls -l /var/www/html/
total 3
drwxr-xr-x 3 root root  15 Aug 30 08:35 quayuser
								
							

Configure podman to verify signature while pulling the images

On the system where we want to pull the images we have to modify policy.json file and also create a regisrty.yaml file or we can modify the existing default.yaml file. GPG Public key should also be placed on that system to verify the images.

  • We are modifiying the existing default.yaml file.
    								
    # cat /etc/containers/registries.d/default.yaml
    docker:
    	quay-all.example.com:
    		sigstore: http://192.168.0.10:90
    								
    							
  • Configure policy.json file. Type reject means if an image is pulled and does not meet any specific policies defined under "transports", it will be rejected
    								
    # cat /etc/containers/policy.json 
    {
    	"default": [
    	{
    		"type": "reject"
    	}
    	],
    	"transports": {
    	"docker": {
    		"quay-all.example.com": [
    		{
    			"type": "signedBy",
    		"keyType": "GPGKeys",
    			"keyPath": "/etc/pki/kay-pub.asc"
    		}
    		]
    	}
    	}
    }
    								
    							
  • Copy the GPG public key to the above specified keyPath. If you have generated the gpgkey on a different system and then signed the image then you may have to scp the gpgkey to the system where you are pulling the image.
    								
    gpg --export kay@example.com > kay-pub.asc
    scp kay-pub.asc root@podman-client:/etc/pki/kay-pub.asc
    								
    							

Try to pull the image and verify if signature verification works.

								

podman pull quay-all.example.com/quayuser/a/webs:1 --tls-verify=false
Trying to pull quay-all.example.com/quayuser/a/realtest1:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob fb875368e78f skipped: already exists  
Copying blob b18b624f97a4 skipped: already exists  
Copying blob 9d1dcd11a3fd skipped: already exists  
Copying config a8c7f5a20e done   | 
Writing manifest to image destination
Storing signatures
a8c7f5a20e6e2bcafafd520bbec6e6c2ff6032e7af5a75c36244ba32bba59ac3