The world of containers consists of images, which are made out of layers. Normally, everybody with access to the image can investigate it, check out what's in there and how it works. Encrypting your images can prevent others to see the content of your images, even if they get access to it.
Let's see how you can encrypt images, push them to a registry and start a container from it. Shall we?
Podman is a rootless and daemonless drop-in replacement for Docker. You can start and stop container, build and push images, and basically everything you can do with Docker.
There are some huge benefits, when it comes to Podman. It is a systemd native, which means you can control your containers with systemd services easily. It has various options to run containers as root or user. Not only that, but it also provides features that don't even exist in Docker, like auto-updates, running Pods and even Kubernetes deployments.
You can find a couple of articles in my blog, too.
Why is encrypting needed?
Having container images in a registry like Docker hub or quay.io has its benefits, for sure. You can also create private registries, that are protected, and the transport will be encrypted via TLS, too. Why even bother about image encryption?
Well, what happens if you have a couple of unattended machines like IoT devices? Downloading images from a private registry also requires you to login to said registry. Furthermore, the image will be unprotected on your servers, too.
And lastly, another layer of security for your intellectual property or most valuable code won't hurt, right? Protecting your images in case they are stolen might make the difference between long nights and a well deserved sleep.
Let's encrypt something. It's so easy, you might not even look back afterward.
You will need to have Podman installed to follow the below guide.
If you need a kick-start for related topics, I have linked you articles from my Blog explaining "How to get started".
We will start with a super simple Dockerfile/Containerfile. But, we will put in some "super secure information". First, create a little project directory, looking like the below:
Next, we will put our secure information in the
index.html file. Yeah, I know, "Why should we put something secure in a web file?". Because it's an example, a really trivial one, and we only want to ensure that nobody can see the information by stealing the image.
Next, we will need a Containerfile for the new image.
That's it. Everything normal so far. You can basically build and work with the content as usual.
Build & Push
Now, let's build and push our image. It's super easy. The build works exactly the same way as always. If you never build an image, you may take a look at "Podman - Images", first.
# Build image $ podman build -t docker.io/dschier/wtdexamples:unencrypted . # Check the image $ podman image ls REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/dschier/wtdexamples unencrypted 972a95e09895 51 seconds ago 147 MB # Run the image for testing podman run -dt -P --name example docker.io/dschier/wtdexamples:unencrypted 9a5e3abf42c2b66bfb5478e7646d19bc796cf83c241b78aad9da8a7a6ec26f35 # Check for state $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9a5e3abf42c2 docker.io/dschier/wtdexamples:unencrypted nginx -g daemon o... 3 seconds ago Up 3 seconds 0.0.0.0:36227->80/tcp example # Check if its working $ curl localhost:36227 <h1>Super Secure Site</h1> <p>The secret passphrase is: "P0dM4n"</p>
This worked, so far. But what about encryption? Well, this is happening, if you want to share the image by pushing it to a registry. So, let's do this. I am using Docker Hub here, but this will work with all major container registries.
# Log in to your registry $ podman login docker.io # Push the unencrypted image (for reference) $ podman push docker.io/dschier/wtdexamples:unencrypted
So far, nothing unusual. But, the above allows everybody in the world to pull the image and see the above password. Furthermore, one can inspect the image and see the layers:
# Inspect the image $ podman inspect docker.io/dschier/wtdexamples:unencrypted
One can also run the image, as we have done above, without any kind of security measures. Therefore, we want to upload the image in an encrypted way.
Podman supports a multitude of encryption options. I will stick to JWE (JSON Web Encryption) for this article. It provides a strong algorithm and is easy to share across machines.
The easiest method to encrypt an image is JSON Web Encryption (JWE). To do this, you will need a TLS key pair. We can create one without installing any dependencies.
# Create a directory $ mkdir certs $ cd certs/ # Create the private key $ podman run -it -v $PWD:/work:z docker.io/library/nginx openssl genrsa -out /work/wtdexample_encrypt_private.pem # Create the public key $ podman run -it -v $PWD:/work:z docker.io/library/nginx openssl rsa -in /work/wtdexample_encrypt_private.pem -pubout -out /work/wtdexample_encrypt_public.pem # Check the keys $ ls wtdexample_encrypt_private.pem wtdexample_encrypt_public.pem
Well, now we can use these keys to encrypt and decrypt our image. I will create a new tag, so we can compare each of the images afterward.
# Tag a new image $ podman tag docker.io/dschier/wtdexamples:unencrypted docker.io/dschier/wtdexamples:jwe # Push the image, but encrypt it $ podman push --encryption-key jwe:wtdexample_encrypt_public.pem docker.io/dschier/wtdexamples:jwe
To validate, that this worked, let's delete and pull the image.
# delete the tagged image $ podman image rm docker.io/dschier/wtdexamples:jwe Untagged: docker.io/dschier/wtdexamples:jwe # delete other, related images $ podman image rm docker.io/dschier/wtdexamples:unencrypted Untagged: docker.io/dschier/wtdexamples:unencrypted Deleted: c9d7871a629ac0790ebf0de7f3ede5f73bc53ce7a8146b6fcac15a924c600663 # Pull the image again $ podman pull docker.io/dschier/wtdexamples:jwe Trying to pull docker.io/dschier/wtdexamples:jwe... Getting image source signatures ... Error: writing blob: adding layer with blob "sha256:649ec709baac4a5e31a098e28723de907b5e7e16f755ec31c105df9983ccd609": processing tar file(archive/tar: invalid tar header): exit status 1
As you can see, this wasn't working. So, let's try pulling it with our private key.
# Pull the image and provide a key $ podman pull --decryption-key wtdexample_encrypt_private.pem docker.io/dschier/wtdexamples:jwe Trying to pull docker.io/dschier/wtdexamples:jwe... Getting image source signatures ... WARN Compressor for blob with digest sha256:c47b86cfcc03bce8dbf4e8684582dc4fc80c10bde85da272d87294c8405396c6 previously recorded as uncompressed, now gzip Copying config c9d7871a62 done Writing manifest to image destination Storing signatures c9d7871a629ac0790ebf0de7f3ede5f73bc53ce7a8146b6fcac15a924c600663 temp/encrypted-containers/certs took 8s $ podman image ls REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/dschier/wtdexamples jwe c9d7871a629a 5 days ago 147 MB
Well, this worked fine. Now, we can start it, as usual.
Docs & Links
The feature seems to be super new, but is already supported from Podman, Buildah and Skopeo. Therefore, let me link to the related documentation.
Well, well, finally we can encrypt images, push them to a public registry and still protect the content.
What is the use case? Managing access to images, based on roles/users and permissions on a registry, can be cumbersome and annoying. Sharing one secret file, to get access to exactly one (or multiple) images, is pretty easy and can be used on many devices without a huge management overhead.
What do you think? Is this useful for you? Let me know!