Encrypting container images with skopeo

Brandon Lum
4 min readJan 13, 2020

--

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 key mypubkey.pem that we just generated, and the prefix jwe: 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 encrypt
  • oci: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!

--

--

Brandon Lum

Security, Container Cloud, IBM Research. #NablaContainers, Encrypted OCI Containers, Portieris, SPIFFE Tornjak