Remove Security Vulnerabilities and decrease the weight of Docker Images using Google Distroless Images

Eduardo Andres Giraldo Gomez
Globant
Published in
4 min readAug 31, 2020

In our work as developers, there are a lot of applications and technologies with which we interact on a daily basis, one of them is Docker. It’s used to launch applications that we develop through containers and images, this technology has allowed developers and DevOps to work more efficiently and safely. It is also a good way to maintain a uniform environments, but as we work with this technology, we are facing new challenges such as image security and vulnerabilities, size and the packages installed inside it.

When you use Docker, as a developer you’re always challenged to optimize and secure the images, maintaining a small size and creating a good scenario for production and development stages.

To do that, you can use Multi Stage builds to maintain the size down, creating multiple stages to compile, copy dependencies and install all the packages that you need and a final one to execute leaving behind the unwanted processes from the final image.

In order to try to increase security without affect the size, there are two basic types of distributions that can be used to mount the apps:

Use Alpine as Base Distro:

The first option is to use an Alpine Image, Alpine is a Linux Distro based on Musl, designed for resource efficiency and security. This Image is a really good solution in terms of size and is compatible with almost all programing languages.

Alpine is a really good solution for standard base apps, as main disadvantage, managing backward compatibility is really hard and is not compatible with some advanced libraries in a native way. So it can be a little hard to install some packages or update them to other versions.

Use Debian Slim as Base Distro:

With this approach you can use a Debian distro with Apt, this kind of images, have the great advantage of been compatible with almost all the packages for advanced and common use, also, this allows to update and maintain the packages in a very easy way.

The two main pain points in this kind of images is the amount of unwanted pre installed packages, and, when you install a new package, all the sub-dependencies can use a lot of space and creating potentially security breaches on image.

Another possibility is the use of Distroless Images, and that’s the technique I’m going to show to improve our Docker Images.

What is Distroless?

Distroless is an image creation technique used by Google, with this, they give us an image without shell, package installer and other packages that may be harmful for our applications, with this the image size is reduced considerably, this kind of images are created based on Bazel, with this, we can decrease our security issues giving us more time to focus on our code, right now these are the supported languages by them:

  • Java
  • Python 2 and 3
  • Go Lang
  • Node JS
  • .Net
  • Rust

These images are based on Debian 10 Buster, so we don’t need to think so much about compatibility. To work with these images, we also need to create a Multi-Stage file to generate the complete process with our software.

Distroless Example:

For this example, we’re going to use a simple Flask endpoint on Python 3:

from flask import Flaskapp = Flask(__name__)@app.route(‘/’)def hello():  return “Hello World!!”if __name__ == ‘__main__’:  app.run(host=’0.0.0.0')

This are the requirements to install in the image:

Flask==1.1.2requests==2.23.0

We’re going to create three images to do the comparison, this is the file for an Alpine Image:

FROM python:3.7-alpine3.11ADD requirements.txt /app/requirements.txtWORKDIR /appRUN pip install -r requirements.txtADD . /appENTRYPOINT [ “python”, “app.py” ]

Here we create a Debian Slim image who is the nearest OS for our test in comparison to the Distroless ones:

FROM python:3.7-slimADD requirements.txt /app/requirements.txtWORKDIR /appRUN pip install -r requirements.txtADD . /appENTRYPOINT [ “python”, “app.py” ]

And this is our Distroless Image, here you can see the use of a Multi-Stage creation:

FROM python:3.7-slim AS buildADD . /appWORKDIR /appRUN pip install — upgrade pipRUN pip install -r ./requirements.txtFROM gcr.io/distroless/python3COPY — from=build /app /appCOPY — from=build /usr/local/lib/python3.7/site-packages/usr/local/lib/python3.5/site-packagesWORKDIR /appENV PYTHONPATH=/usr/local/lib/python3.5/site-packagesEXPOSE 5000CMD [“app.py”]

After the build finishes we can compare the size of the images with the current app:

At the bottom, we can see the final weight for distroless image

As we can see here, the image size has been reduced by more than 50% than the Debian Slim image, it is even smaller than the one we created with Alpine. In larger projects also you can see differences between the creation and the used layers.

Distroless and Security:

To verify security in distroless applications, we tested the referenced images using Anchore, this repo allows us to analyze the images that we have published in Docker Hub for vulnerabilities and security issues.

After running a scan we found the following results:

Security Comparison between images — number of security alerts

As you can see there, the Distroless image only has two vulnerabilities compare to the sixty three from Slim image, giving you more time to focus on other development tasks.

--

--