Encrypting container images with skopeo
In this blog post, @harche and I will bring you through how to the new Encrypted Container Images feature with skopeo! We will bring you through the steps as we did it in our previous blog post about encrypting container images using containerd. And in the upcoming posts, we will show how we’ve integrated this into cri-o and show an end-to-end demo with kubernetes!
For an introduction to what encrypted container images are how they are defined, check out our previous blog post.
Let’s encrypt some images with skopeo!
We will show how to use skopeo to encrypt and decrypt encrypted container images!
Requirements
The only requirements on top of the standard golang environment is:
- The latest version of skopeo
- openssl
- docker (optional: if you would like to set up your own local registry)
- gpg (optional: only if encryption with GPG is required)
Encrypting an image
Creating some keys
We will generate some RSA keys with openssl. The following commands will generate a public and private key pair.
$ openssl genrsa --out mykey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................+++++
............................+++++
e is 65537 (0x010001)
$ openssl rsa -in mykey.pem -pubout -out mypubkey.pem
writing RSA key
Pulling an image
We will download an image to encrypt:
$ skopeo copy docker://docker.io/library/nginx:latest oci:local_nginx:latest
Getting image source signatures
Copying blob 8ec398bc0356 done
Copying blob 465560073b6f done
Copying blob f473f9fd0a8c done
Copying config a06a445ca0 done
Writing manifest to image destination
Storing signatures
Encrypting the image
By using the skopeo copy
command with the encryption-key
flag, we can encrypt the existing image.
$ skopeo copy --encryption-key jwe:./mypubkey.pem oci:local_nginx:latest oci:nginx_encrypted:latest
Getting image source signatures
Copying blob 8ec398bc0356 done
Copying blob 465560073b6f done
Copying blob f473f9fd0a8c done
Copying config a06a445ca0 done
Writing manifest to image destination
Storing signatures
The arguments are:
--encryption-key jwe:mypubkey.pem
: This indicates that we want to encrypt the image using the public keymypubkey.pem
that we just generated, and the prefixjwe:
indicates that we want to use the encryption scheme JSON web encryption scheme for our encryption metadata.oci:local_nginx:latest
- The local OCI image to encryptoci:nginx_encrypted:latest
- The local OCI encrypted image to be created
Note: A current restriction in skopeo is that in order for encryption to be done, the source image has to be an OCI image.
Pushing to registry (Optional)
Let us set up a local registry that we can push the encrypted image to. Note that by default, the docker/distribution
registry version 2.7.1 and above supports encrypted OCI images out of the box.
Note: as of present, current Docker Hub registry version does not take the encrypted OCI images. IBM Container Registry does support it if you wish to skip setting up your own registry.
$ sudo docker run -d -p 5000:5000 --restart=always --name registry registry:2.7.1
By using the regular skopeo copy
command without any additional flags, the encrypted image can be pushed and pulled to and from the registry while still encrypted.
The following command will upload our encrypted image to the local registry:
$ skopeo copy --dest-tls-verify=false oci:nginx_encrypted:latest docker://localhost:5000/nginx_encrypted:latest
Getting image source signatures
Copying blob 8ec398bc0356 done
Copying blob 465560073b6f done
Copying blob f473f9fd0a8c done
Copying config a06a445ca0 done
Writing manifest to image destination
Storing signatures
The following command will pull our encrypted image from the local registry:
$ skopeo copy --src-tls-verify=false docker://localhost:5000/nginx_encrypted:latest oci:nginx_encrypted:latest
Getting image source signatures
Copying blob 8ec398bc0356 done
Copying blob 465560073b6f done
Copying blob f473f9fd0a8c done
Copying config a06a445ca0 done
Writing manifest to image destination
Storing signatures
Decrypting the image
We will decrypt the image using the skopeo copy
command as well but instead passing in the --decryption-key
flag.
Note: that if --decryption-key
is NOT specified, the encrypted image will just copy without performing decryption.
Failed Decryption with the wrong key
We will first show failure to decrypt with an incorrect key.
We first generate a new dummy key with openssl.
$ openssl genrsa -out wrongkey.pem 1024
Generating RSA private key, 1024 bit long modulus
.....................++++++
......++++++
e is 65537 (0x10001)
We try to perform the decryption with the incorrect key using the --decryption-key
option.
$ skopeo copy --decryption-key ./wrongkey.pem oci:nginx_encrypted:latest oci:nginx_decrypted:latest
Getting image source signatures
Copying blob 2da43daa37bd done
Copying blob 70330c5d48f1 done
Copying blob dddfee0b5d41 done
FATA[0000] Error decrypting layer sha256:2da43daa37bd3bdc2d71e0736dd51c5003075f25a953f0fac5c9b9aa5f7bae9c: no suitable key unwrapper found or none of the private keys could be used for decryption
Successful Decryption with our key
We will now use the original private key to decrypt the image successfully:
$ skopeo copy --decryption-key ./mykey.pem oci:nginx_encrypted:latest oci:nginx_decrypted:latest
Getting image source signatures
Copying blob 2da43daa37bd done
Copying blob 70330c5d48f1 done
Copying blob dddfee0b5d41 done
Copying config a06a445ca0 done
Writing manifest to image destination
Storing signatures
What’s next?
In the next blogpost, we will show how to run an encrypted container image with cri-o and show an end-to-end demo with kubernetes!