TL;DR
Using tags like latest or numeric versions (3.23.5) in your Dockerfile is dangerous because they are mutable and can change without warning.
These changes can break your application in production or expose you to supply chain attacks if the base image is compromised.
It is crucial to always pin your base image to a specific SHA (e.g., FROM alpine@sha256:...) since it is the only way to guarantee immutability.
Intro
If you work in tech, you've probably heard of Docker and the famous containers. If not, the main thing you need to know is that it's a technology that allows you to package applications into containers, thus solving the legendary "it works on my machine, but it won't even open on yours" problem.
These containers run our application from an image, which includes our code and everything it needs to work.
To build this image, we use a file called Dockerfile.
So what is the Dockerfile?
The Dockerfile is just a plain text file. It doesn't even use a specific extension. You simply need to name the file Dockerfile for Docker to recognize it.
In this file, we define the instructions we want Docker to follow to create an image. Something along the lines of:
- Use this base image
- Run commands XYZ
- Remove these services
- and so on and so on
Once we're done, creating the image is super simple. We just need to open the CLI, navigate to the location where the Dockerfile is, and run the command docker build . -t <Name>
The Base Image
To build any image in Docker, we start with a base image. This image will be like a canvas on which we will install our application and add our own dependencies.
It is important to keep in mind that it's not a blank canvas: the image already comes with pre-installed software that may or may not be useful to us. If it isn't, the best practice is to uninstall it to reduce the attack surface, but that's a topic for another post.
For example, if we run the command with the following code, Docker will create a local image starting from the latest available version of alpine:
FROM alpine:latest
# Print to console to verify execution
CMD ["echo", "Hi!"]
The Problem with Selecting Versions
There are three main ways to select a base image version: using latest, a specific version, or the hash identifier (SHA).
You've probably already come across many tutorials or blogs that use base images like this:
FROM alpine:latest
FROM alpine:3.23.5
The reason why is no mystery: it's simple, readable, and it's easy to understand which version is being used.
If we use latest, we ensure that at build time, the most recent version is used, and if we use the second option, we make it explicitly clear that we want to use version 3.23.5.
However, selecting the base image version in these ways can open the door to two major security risks:
- Supply chain attacks
- Introduction of vulnerabilities
(If you've used GitHub Actions before, this problem will be familiar to you).
The problem with using latest is that, as its name suggests, it can change from one day to the next. This can cause our application to stop working due to the introduction of breaking changes.
Even worse, if an attacker manages to compromise the accounts of the maintainers, they could publish a malicious version that would be downloaded by pipelines using latest.
We might think that specifying a version would free us from this problem, but that's not the case. A version like 3.23.5 is actually a tag, just like latest, that points to specific code.
These tags are mutable, which means maintainers could make changes such as updating the base image to patch vulnerabilities and have the 3.23.5 tag point to this new code.
The solution to these problems is to pin the base image to a specific SHA.
Always Pin the Image
The only way to achieve immutability is through this method. This guarantees that we always use exactly the same version, since any minor change in the image's code will produce a different SHA.
There is no excuse not to use it.
FROM alpine@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40 # 3.22.5
It is a good practice to include a comment on the same line indicating the version the SHA refers to.
Top comments (0)