DevSecOps Part 4: Scanning Docker Images With Trivy

This article is part of a series about integrating security tooling in the development process. You can find the rest of the articles here:

Note: This tutorial is based on the repository resulting from part 3. If you haven't achieved it yet, no worries! You can directly fetch the result from GitHub. If you already have done the three first parts of the tutorial, you can switch to the next section.

First, fork https://github.com/glimow/dvpwa

Then, clone your newly forked repository and switch to the right branch:

git clone -b tutorial-part-3 git@github.com:<your-username>/dvpwa.git

Docker Security, As Simple As It Should Be. 💫

In the last tutorial, we used nuclei to search for known security bad practices in our live application.

But nuclei can only detect the tip of the iceberg: the vulnerabilities existing in your live web app for which the community has created custom, handmade tests.

What if the various packages or files you use in your docker images for deploying into production contain vulnerabilities themselves?

Could those vulnerabilities be used by an evil attacker? Sometimes, they can.

Fortunately, thanks to trivy, one can scan its docker images to know literally in seconds if they contain packages with known vulnerabilities. Even cooler, trivy is free, open-source, and well maintained.

As you know, our vulnerable python app, dvpwa, comes with a Dockerfile for both development and production. Let's scan it and hunt for vulnerabilities 👀

Running trivy on a Docker Image 🐳

First, let's install trivy

Fortunately, they have a unique script to install it on macOS or your favorite Linux distro:

 curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.21.0

and then

trivy

Which should get you something like:

Our trivy install is up and running, cool! Now let's scan our Dockerfile 🐳

First, go into your local dvpwa directory.

You will notice there are two Dockerfiles: one for the app itself, and one for the database. The priority is to scan the app's Dockerfile itself because it's directly exposed to the internet through the app's web server.

First, let's build our Docker image:

docker build . --file Dockerfile.app -t dvpwa.app

 You should obtain a result ending with the following:

Our image is ready to be scanned! 💥

Now it's very simple, just run:

trivy image dvpwa.app:latest

If you are lucky, you should almost instantly see a (huge!) list of vulnerabilities

If you go through the results, you will notice that there is also a Python section, which contains PyYAML critical vulnerabilities:

Wait, haven't we seen that somewhere already? Yes, those are the same vulnerabilities we detected in DevSecOps part 1!

Again, more than just telling you about the vulnerabilities, trivy even tells you the version of each package that fixes them 🤩

Now, as a developer, appsec, or an engineering manager, wouldn't it be cool if we could avoid deploying code if all critical vulnerabilities aren't fixed?

Let's do that with our old friend GitHub Actions.

Integrating trivy in GitHub Actions 🔨

Integrating trivy in GitHub Actions is very simple because aquasecurity, its authors, have published a GitHub Action template for it.

Just add the following at the end of your .github/workflows/main.yaml

 container_scanning:
    name: Scan Container for Security Issues
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      
      - name: Build an image from Dockerfile
        run: |
          docker build --file Dockerfile.app -t dvpwa.app:${{ github.sha }} .
      
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'dvpwa.app:${{ github.sha }}'
          format: 'table'
          exit-code: '1'
          ignore-unfixed: true
          vuln-type: 'os,library'
          severity: 'CRITICAL'

As you can see, in the last part we tell trivy to return a non-zero exit code if it finds critical vulnerabilities in the OS packages or the software libraries.

Now, just commit and push the new config:

git add .github
git commit -m "Add container security scanning"
git push origin master

After a few minutes, you should see the result in your GitHub Actions panel:

Again, our action failed, which is exactly what you would want for a real-world application that contains critical vulnerabilities.

Conclusion! 😎

In just a few minutes, we set up a tool that automatically detects vulnerable packages inside our docker images, directly in the CI/CD.

Since the beginning of this tutorial series, we built a full security pipeline in the CI/CD by checking

Follow Escape on Twitter, or check our website

for more human-readable appsec content!

Bonus: Scanning Dockerfiles using Trivy 🎁

In fact, trivy can scan way more than only docker images: filesystems, requirements.txt, package.json, and even Dockerfiles and Kubernetes configs!

For instance, in our dvpwa repository, we can scan our Dockerfiles just by running

trivy config .

Which will scan automatically our two Dockerfiles Dockerfile.app and Dockerfile.db :

Escape

Escape