Welcome!

Vulnerability scanning for container images

It's important to scan your container images for known vulnerabilities. These are flaws in the packages your code depends on, that could be exploited by an attacker.

Why you should scan container images

A vulnerability scanner looks at the versions of all the packages in a container image to determine whether they contain any known vulnerabilities. If your image only holds your own custom binary code - which is often the case if you're building an image from scratch with a self-contained binary built from, say, Golang code - then there are no package dependencies to worry about. So why would you still need to worry about scanning your images?

In many cases you'll be running third-party images alongside your own custom code. For example you may be using databases, message queues, logging, and other components that are delivered and deployed in the form of container images.

It's a good idea to scan these third-party images you're running in your deployment to make sure they don't include vulnerable packages.

Also, even if you and your team are writing self-contained binary executables, it's possible that other people in your organisation are using languages like Java, Ruby or Python, with dependencies on third-party packages. It's good practice to scan all images as part of a CI/CD pipeline, to be sure you are checking everything that's making its way to your production deployment.

Vulnerability scanning with MicroScanner

There are several vulnerability scanners for container images - some open source, some free, and some commercial. In this section we'll look at scanning container images for vulnerabilities with the free tool, MicroScanner. Microscanner is very simple to incorporate into your build processes because it's added as a few steps in your Dockerfile.

Congratulations!

You've completed the scenario!

Scenario Rating

Congratulations, you've finished this scenario! Here's what you have covered:

Steps

Vulnerability scanning for container images

Get your MicroScanner token

Before you can scan images with MicroScanner you'll need to sign up for a token - this is used to rate-limit the number of images each user can scan.

Step 1 - Sign up for a new token

If you have previously signed up and you already have a MicroScanner token, you can skip this step and go straight to step 2.

read -p "Enter your email address: " email

docker run --rm -it aquasec/microscanner --register $email

You'll receive your token by email.

Step 2 - Enter your token

The next command will prompt you for the token so that it can be stored in an environment variable:

read -p "Enter your MicroScanner token: " token

Next step

Now that you have a token, let's move on to writing a Dockerfile that will run MicroScanner on your image.

Scan an image

As an example, we'll scan an old version of the mongo image which is known to have several vulnerabilities.

Scanning an image with vulnerabilties

Copy this Dockerfile into the editor (the file will save automatically).

# Start with the image we want to scan
FROM mongo:3.2.1
# Add the microscanner binary
ADD https://get.aquasec.com/microscanner /
# Make it executable
RUN chmod +x /microscanner
# Indicate that the token will be passed in as a build argument
ARG TOKEN
# Run microscanner to scan the image contents
RUN /microscanner ${TOKEN}

Run docker build to execute the steps in this Dockerfile.

docker build --build-arg TOKEN=$token --no-cache .

The --build-arg parameter passes in an argument that matches the ARG command in the Dockerfile

It's a good idea to used the --no-cache argument to force the scan to be re-run even if the image hasn't changed. See the notes for more on why this is necessary.

If microscanner finds a high severity vulnerability, that step will fail, and hence the image build will fail. You should see The command '/bin/sh -c /microscanner ${TOKEN}' returned a non-zero code: 4 as the last line of the output.

Scan an image without high severity vulnerabilties

Edit the Dockerfile to scan mongo:latest instead of the old mongo:3.2.1 image.

Run the build command, and this time it should succeed:

docker build --build-arg TOKEN=$token --no-cache .

You should see the command output finishes with Successfully built <image ID>.

Notes and next step

Security researchers find new vulnerabilities in existing software all the time. This means that even if you don't change a piece of code, a new vulnerability might be published in it. This means that you need to re-scan your container images on a regular basis even if you don't change their contents, so that the scanner can alert you to any newly published vulnerabilities it might include. Typically at Aqua we find that our enterprise customers scan at least the container images they're using in production on a daily basis.

This is why you need to pass in the --no-cache parameter on the docker build command, otherwise caching would mean that the scanner wouldn't run if the underlying base image doesn't change.

With the current Dockerfile, you will end up with an image that also includes the microscanner executable. In the next step we'll see a couple of approaches you can use to avoid this being included in your final image.

Clean image

Since you would typically scan each image as part of the CI/CD pipeline, there is no need for the MicroScanner executable to live in the image that you deploy. There are two ways you can ensure that you end up with an image that doesn't include the extraneous MicroScanner executable.

Add a step that removes microscanner from the image

Multi-stage build

An extra step in the Dockerfile

# Remove the microscanner exectuable
RUN rm /microscanner

Run the build and it should succeed.

docker build --build-arg TOKEN=$token --no-cache .

Multi-stage build

By having a second FROM command in the Dockerfile, we create a multi-stage build. In most multi-stage builds, some contents from the first stage are copied into the second stage. Here, however, we are relying on the fact that the first stage wil fail if the image contains high-severity vulnerabilties.

# Start with the image we want to scan
FROM mongo:latest
# Add the microscanner binary
ADD https://get.aquasec.com/microscanner /
# Make it executable
RUN chmod +x /microscanner
# Indicate that the token will be passed in as a build argument
ARG TOKEN
# Run microscanner to scan the image contents
RUN /microscanner ${TOKEN}
# Multi-stage build
# We will only get to this if the scanning succeeds
FROM mongo:latest

docker build --build-arg TOKEN=$token --no-cache .

Try this with both mongo:3.2.1 and mongo:latest by editing the Dockerfile. Note that you have to change the image in two places in the Dockerfile!

Next step

It's annoying to have to change the image name (in two places!) when you want to scan an image, right? In the next step we'll see how we can pass in the image name as an argument.

Scan any image

We can use a single Dockerfile to scan any image, using a multistage build.

# The image name will be passed in as a build argument
ARG IMAGE
# Start with the image we want to scan
FROM ${IMAGE}
# Add the microscanner binary
ADD https://get.aquasec.com/microscanner /
# Make it executable
RUN chmod +x /microscanner
# Indicate that the token will be passed in as a build argument
ARG TOKEN
# Run microscanner to scan the image contents
RUN /microscanner ${TOKEN} --no-verify
# Multi-stage build
# We will only get to this if the scanning succeeds
FROM ${IMAGE}

The last line of this Dockerfile starts a new stage, which consists simply of the image we wanted to scan in the first place. The build will only reach this step if the scan succeeds. The benefit is that the image output by the build includes nothing except the contents of the original image.

Here in Katacoda let's use an environment variable to hold the name of the image you want to scan. Click the following command so you're prompted for the image name:

A note on the --no-verify flag

In this example Dockerfile we specify the --no-verify flag when running microscanner.

Running microscanner with the --no-verify flag means that you're not checking the certificates that identify the Aqua "CyberCenter" where the vulnerability database is held, when microscanner makes an HTTPS connection. This leaves you susceptible to the possibility that an attacker could spoof the CyberCenter address, and return inaccurate results for your scan.

It is much better practice to omit this flag, but this requires the container image to have CA (certificate authority) certificates installed. Many images will have this already, but you may need to add steps to the Dockerfile to add them if they aren't there already. The command you need for this depends on the Linux distribution used in the base image.

Opening...

Help

Katacoda offerings an Interactive Learning Environment for Developers. This course uses a command line and a pre-configured sandboxed environment for you to use. Below are useful commands when working with the environment.

cd <directory>

Change directory

ls

List directory

echo 'contents' > <file>

Write contents to a file

cat <file>

Output contents of file

Vim

In the case of certain exercises you will be required to edit files or text. The best approach is with Vim. Vim has two different modes, one for entering commands (Command Mode) and the other for entering text (Insert Mode). You need to switch between these two modes based on what you want to do. The basic commands are: