Signing Docker Images with GPG
Prerequisites
The following prerequisites are required to use the Unbound signing with CORE.
- CORE (UKC) 2.0.2001 or newer
- Client device, with an OS listed in the System Requirements, and the following software:
- CORE (UKC) client 2.0.2001 or newer
- GPG
GNU Privacy Guard - PGP cryptography implementation 2.1
- Docker CE and Docker CLI
Command Line Interface (installation described below)
- Skopeo 0.1.40 (installation described below)
Prepare the Client Environment
The following procedure is needed to prepare the client.
- Install skopeo. See skopeo for more information.
- Add the Docker CE repository.
- Install the Docker CLI
Command Line Interface.
- Start Docker
sudo yum install skopeo
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce
sudo service docker start
Signing
This procedure details signing a docker image using GPGGNU Privacy Guard - PGP cryptography implementation and CORE.
- List keys in CORE. You will use the key name in the next step.
- Load key to GPG
GNU Privacy Guard - PGP cryptography implementation keyring.
- Export the public key.
- (Optional) The certificate can be used to verify the origin of the public key. Export the certificate:
- Edit /etc/containers/registries.d/default.yaml.
- Copy an image.
- Sign and push the image.
- Compress the folder with the signature created in the previous step.
For example: - Send public.gpg and certificate to the verifying machine. You only need to do this step once.
- Send the compressed folder with the signature to the verifying machine. You need to send a new signature for every release, and you may want to use a signature repository.
ucl list
ucl pgp-key -p <PARTITION> -n <KEYNAME>
gpg2 --armor --output public.gpg --export <KEYNAME>
ucl export -p <PARTITION NAME> -w <PASSWORD> -u <UID OF THE CERTIFICATE> -o certificate.pem
# This is a default registries.d configuration file. You may
# add to this file or create additional files in registries.d/.
#
# sigstore: indicates a location that is read and write
# sigstore-staging: indicates a location that is only for write
#
# sigstore and sigstore-staging take a value of the following:
# sigstore: {schema}://location
#
# For reading signatures, schema may be http, https, or file.
# For writing signatures, schema may only be file.
# This is the default signature write location for docker registries.
default-docker:
# sigstore: file:///var/lib/atomic/sigstore
sigstore-staging: file:///var/lib/atomic/sigstore
# The 'docker' indicator here is the start of the configuration
# for docker registries.
#
# docker:
#
# privateregistry.com:
# sigstore: http://privateregistry.com/sigstore/
# sigstore-staging: /mnt/nfs/privateregistry/sigstore
skopeo copy docker://busybox:latest dir:busybox
skopeo copy dir:busybox docker://<USER_NAME>/busybox:tag --dest-creds <USER_NAME>:<USER_PASS> --sign-by <KEYNAME>
sudo tar -zcvf signature.tar /var/lib/atomic/sigstore/<USER_NAME>/busybox@sha256\=afe605d272837ce1732f390966166c2afff5391208ddd57de10942748694049d/
To verify the signature:
- Copy the public key and sigstore to the same place.
- Edit /etc/containers/registries.d/default.yaml.
- Uncompress the signature folder that was copied to the verifying machine.
For example, using the path specified in the previous step:sudo tar -zvxf signature.tar -C /var/lib/atomic/sigstore/<USER_NAME>/
- Edit /etc/containers/policy.json.
- Pull the image.
- (Optional) Certificate verification, if you exported the certificate above.
- You can validate with the certificate authority directly using this command:
- Check if the certificate is active using this command. Note that DigiCert is used as an example.
- OpenSSL shows the content of X509 certificate using the command:
- GPG
GNU Privacy Guard - PGP cryptography implementation shows the content of the public key file using the command:
- Check the details of the public key and see that the same modulus and public exponent exists in both files, i.e. this is the same key/certificate.
# This is a default registries.d configuration file. You may
# add to this file or create additional files in registries.d/.
#
# sigstore: indicates a location that is read and write
# sigstore-staging: indicates a location that is only for write
#
# sigstore and sigstore-staging take a value of the following:
# sigstore: {schema}://location
#
# For reading signatures, schema may be http, https, or file.
# For writing signatures, schema may only be file.
# This is the default signature write location for docker registries.
default-docker:
sigstore: file:///var/lib/atomic/sigstore
# sigstore-staging: file:///var/lib/atomic/sigstore
# The 'docker' indicator here is the start of the configuration
# for docker registries.
#
# docker:
#
# privateregistry.com:
# sigstore: http://privateregistry.com/sigstore/
# sigstore-staging: /mnt/nfs/privateregistry/sigstore
{
"default":[
{
"type":"reject"
}
],
"transports":{
"docker":{
"docker.io/<USER_NAME>":[
{
"type":"signedBy",
"keyType":"GPGKeys",
"keyPath":"/<path-to-public-key>/public.gpg"
}
]
}
}
}
sudo skopeo copy docker://<USER_NAME>/busybox:tag dir:busybox
Example success message:
Getting image source signatures
Checking if image destination supports signatures
Copying blob 0669b0daf1fb done
Copying config 83aa35aa1c done
Writing manifest to image destination
Storing signatures
Example failure message:
FATA[0001] Source image rejected: A signature was required, but no signature exists
openssl x509 -inform pem -in certificate.pem -noout -text
This proves to the verifier that it was signed by the signer.
openssl ocsp -no_nonce -issuer <OSCPCHAINPUBLICKEYNAME> -cert certificate.pem -VAfile <OSCPCHAINPUBLICKEYNAME> -text -url http://ocsp.digicert.com -respout ocsptest
This shows that the certificate is active.
If the certificate is valid, the output is:
Response verify OK
openssl x509 -text -in <X509-certificate-file>
gpg2 -v --list-packets public.gpg
Note
It is recommended to verify that the certificate is valid before sharing it with anyone who will be verifying images.