Detecting Vulnerabilities in Docker Images
Scanning Docker Images with Trivy
Trivy is one of the most popular open-source tools that, among other features, scans Docker images for vulnerabilities. It supports scanning images stored in local registries, remote registries, and container runtimes. It uses vulnerability databases like NVD, Red Hat Security Data, and Ubuntu CVE Tracker to identify known vulnerabilities in the image layers.
To use Trivy, you can run it as a standalone tool or integrate it into your CI/CD pipeline. In this section, we will demonstrate how to install, configure, and use Trivy. Let's start with the installation.
The easiest way to install Trivy is by using the installation script that installs Trivy from GitHub Release:
# Configure the version you want to install
VERSION=v0.60.0
# Install Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | \
sudo sh -s -- -b \
/usr/local/bin \
$VERSION
When it comes to image scanning, Trivy supports 2 distinct targets:
- Files inside container images: This includes scanning the filesystem of the image for vulnerabilities, misconfigurations, and secrets.
- Container image metadata: Every container image has metadata that describes the image, such as the layers, labels, and environment variables. Trivy can scan this metadata for vulnerabilities and misconfigurations.
To scan a Docker image with Trivy, you can use the following command:
# Scan a Docker image
trivy image
If you want to enable only vulnerability scanning, you can use --scanners vuln.
trivy image --scanners vuln
To filter vulnerabilities by severity, you can use the --severity flag. For example, to only keep high and critical vulnerabilities, you can run:
trivy image --severity HIGH,CRITICAL
We can also ignore specific CVEs using a file .trivyignore in the root directory of the project. This is an example that ignores CVE-2022-40897:
# .trivyignore
CVE-2022-40897
It is also possible to filter by type of vulnerability, such as OS or library vulnerabilities:
# Scan for OS vulnerabilities
trivy image --vuln-type os
# Scan for library vulnerabilities
trivy image --vuln-type library
The default output of a scan is a table, but you can also generate JSON, template, or machine-friendly output formats. Here are some examples:
# Scan and output the results in JSON format
trivy image --format json
# Scan and output the results in template format
trivy image --format template --template "{{ range . }} {{ .Target }} {{ end }}"
# A second example of template format that counts the number of critical and high vulnerabilities
trivy image --format template --template '{{- $critical := 0 }}{{- $high := 0 }}{{- range . }}{{- range .Vulnerabilities }}{{- if eq .Severity "CRITICAL" }}{{- $critical = add $critical 1 }}{{- end }}{{- if eq .Severity "HIGH" }}{{- $high = add $high 1 }}{{- end }}{{- end }}{{- end }}Critical: {{ $critical }}, High: {{ $high }}'
You can find out more about the available formats in the official documentation.
In CI/CD pipelines, you have the option to fail the build if vulnerabilities are found. This can be achieved by using the --exit-code flag:
# Fail the build if vulnerabilities are found
trivy image --exit-code 1
We have these images available for scanning:
$GITLAB_REGISTRY_URL/menu-service:v0.1.0$GITLAB_REGISTRY_URL/qr-service:v0.1.0$GITLAB_REGISTRY_URL/menu-postgresql:v0.1.0
We can scan them using the following commands (we will only scan for high and critical vulnerabilities):
# Scan the menu service image
trivy image \
--severity HIGH,CRITICAL \
--vuln-type os,library \
$GITLAB_REGISTRY_URL/menu-service:v0.1.0 \
--exit-code 1 \
-f json
# Scan the qr service image
trivy image \
--severity HIGH,CRITICAL \
--vuln-type os,library \
$GITLAB_REGISTRY_URL/qr-service:v0.1.0 \
--exit-code 1 \
-f json
# Scan the menu-postgresql image
trivy image \
--severity HIGH,CRITICAL \
--vuln-type os,library \
$GITLAB_REGISTRY_URL/menu-postgresql:v0.1.0 \
--exit-code 1 \
-f json
Understanding Trivy Reports
The table report format is more human-readable, but the JSON format is easier to parse and integrate into CI/CD pipelines. In our case, the JSON report should contain two key sections:
os-pkgs: Lists vulnerabilities in operating system packages (e.g., Debian, Ubuntu, Alpine packages installed viaapt,yum,apk).lang-pkgs: Lists vulnerabilities in language-specific application dependencies (e.g., Python packages installed viapip, Node.js dependencies vianpm).
Example:
[
{
"Target": "registry.gitlab.com/restqr/restqr/menu-service:v0.1.0 (debian 12.9)",
"Class": "os-pkgs",
"Type": "debian",
"Vulnerabilities": [
// truncaDevSecOps in Practice
A Hands-On Guide to Operationalizing DevSecOps at ScaleEnroll now to unlock current content and receive all future updates for free. Your purchase supports the author and fuels the creation of more exciting content. Act fast, as the price will rise as the course nears completion!
