Docker container image vulnerability scan using Trivy

Trivy is a vulnerability scanning tool mainly used for containers images, that helps cybersecurity professionals to obtain such information from them by either running it on demand, or automating its execution in any CI pipeline security testing step.

It is a really lightweight tool and also extremely easy to deploy it and to start using it without any cumbersome setup, so it will be an interesting tool to experiment with. Besides, Trivy does not only serve for vulnerability scanning, but also can provide useful information like harcoded secrets or any weak configuration on infrastructure as code (IaC).

Unlike other well known vulnerability scan tools, Trivy does not require any previous readiness before executing the tool, so it doesn’t need to download or update the vulnerability database before the first run. That makes Trivy ideal for DevSecOps.

In this post, I will explain how to deploy the tool, what information can be obtained from scans, the admitted targets on the scan and how to execute the tool.

What can Trivy scan and what information can be obtained?

Trivy is able to detect with its different scanning commands:

  • Vulnerabilities: From OS package vulnerabilities (NVD, OVAL, CVRF, …) to different programming languages advisories.
  • Misconfigurations: It will flag issues like clear text secrets, passwords, api tokens, keys and any weak configuration or best practice recommendations.

The Trivy scanner supports the following components:

  • Container Images: docker images from Docker Hub or the images you have downloaded previously.
  • Filesystems and Rootfs: A target path to scan with Trivy to analyse configuration files or IaC files.
  • Git Repositories: Repository URL to scan. It will use git to perform such scan.
  • Kubernetes: Different Kubernetes components scan like pods, deployments, services and so on.

How to deploy Trivy in Linux

Trivy provides a wide variety of ways to install it so, no matter what is your deployment use case, probably you can use any of the supported ways to install the tool and starting to add security testing on your process.

For this purpose, you can install Trivy by using the Linux package management like yum (RHEL/CentOS):

$ RELEASE_VERSION=$(grep "^VERSION_ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"')
$ cat << EOF | sudo tee -a /etc/yum.repos.d/trivy.repo
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$RELEASE_VERSION/\$basearch/
gpgcheck=0
enabled=1
EOF
$ sudo yum -y install trivy

You may also install it through RPM:

$ rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v0.29.2/trivy_0.29.2_Linux-64bit.rpm

For Debian/Ubuntu distros:

$ curl -sfL https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
$ echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
$ sudo apt-get update
$ sudo apt-get install trivy

By using deb package:

$ wget https://github.com/aquasecurity/trivy/releases/download/v0.29.2/trivy_0.29.2_Linux-64bit.deb
$ sudo dpkg -i trivy_0.29.2_Linux-64bit.deb

Trivy also provides a non OS dependant installation way by using install script:

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

Or if you want to do it in a more self contained way without installing Trivy, you can run it through docker:

$ sudo docker run --rm -v /tmp/:/root/.cache/ aquasec/trivy:0.29.2 image

Scanning with Trivy. Some use cases

The most basic scan that you can do, it’s to do it on a target container image, without even downloading it or installing docker. The command that needs to be passed to Trivy is “image”:

$ trivy image nginx:1.21.0-alpine

The outcome will look similar to the below picture with the findings that Trivy detected. It is based on Mitre CVE data base and the scoring follows the CVSS:

trivy vulnerability scan report of a container image

In order to scan any misconfiguration on IaC in Terraform or Dockerfile, the command to be used is “config”.

In the following example, I’m going to scan the Dockerfile of a well known open source project called “DefectDojo”:

$ git clone --depth 1 https://github.com/DefectDojo/django-DefectDojo

Once “DefectDojo” is ready to be installed, then proceed to scan it with Trivy:

$ trivy config django-DefectDojo/

The config report will show the improvements and best practice that it can be applied in the IaC scanned. In the picture below, you can appreciate an issue found by Trivy in one of the Dockerfiles:

Trivy scanning for misconfigurations on DefectDojo

Similarly, Trivy can be used to scan the supported config files from a target path by using the command “filesystem”. This will search for secrets and weak configurations on non IaC files:

$ trivy filesystem django-DefectDojo/

The “filesystem” command gets findings like the below picture on which you can see some potential secrets found in the path scanned:

Trivy scanning a path to find misconfigurations and secrets on non IaC files.

The results are similar if you use the command “repository” from Trivy but, instead or targeting a path, you can reference to a git repository so you don’t need to clone it previously into your system:

$ trivy repository https://github.com/DefectDojo/django-DefectDojo

Note that the findings and the files analysed are similar to the command “filesystem”:

Another command that works similar to “filesystem” and “repository” its the “rootfs”. You can use it by running Trivy in the following way:

$ trivy rootfs django-DefectDojo/

Although the report looks similar to the previous commands, note that “rootfs” is taking into consideration a different file which is the “Node.js”. Therefore, there are differences between these commands:

Using rootfs Trivy command to scan. It detects different files than filesystem and repository.

For more information about what kind of files is supported by each of the commands, check the following Trivy documentation entry: Trivy Language-specific Packages

Other useful Trivy usages

In terms of CI/CD pipelines, it could be useful to fail a pipeline’s step or stage when a finding from high severity is found. To do so, combine the options “–exit-code” and “–severity” to leverage Trivy command behavior:

$ trivy image --exit-code 1 --severity CRITICAL,HIGH nginx:1.21.0-alpine

It is possible to tweak Trivy with different options to make it more lightweight when running on CI/CD. By using the “–light” option alongside with a command, it tells Trivy to download a more light vulnerability DB version where additional references and descriptions are not present and make it run faster:

$ trivy image --light --exit-code 1 --severity CRITICAL,HIGH nginx:1.21.0-alpine

An alternate way to keep things fast, it is updating the DB previously and then run Trivy without this vulnerability DB update check:

$ trivy image --download-db-only
$ trivy image --skip-db-update --exit-code 1 --severity CRITICAL,HIGH nginx:1.21.0-alpine

Trivy normally stores it’s cache in “~/.cache” path but, if you want to change the location, you may use the option “–cache-dir” to change it in a custom location:

$ trivy image --cache-dir /tmp/mycache/ nginx:1.21.0-alpine

Moreover, another interesting option is to change the report format of Trivy results. Therefore, if you want to obtain a json instead of the default table format:

$ trivy image -f json -o myresults.json nginx:1.21.0-alpine

It is possible to get HTML or XML report formats by using sprig templates. To do so, you can get the default templates that are present on Trivy repo:

$ curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/html.tpl -o html.tpl
$ trivy image --format template --template "@html.tpl" -o myreport.html nginx:1.21.0-alpine