Trivy Container Vulnerability Scanning
Trivy is a comprehensive, open-source vulnerability scanner for container images, filesystems, and code repositories that detects CVEs, misconfigurations, and exposed secrets in CI/CD pipelines. This guide covers installing Trivy on Linux, scanning container images, generating SBOMs, integrating with CI/CD, and enforcing security policies.
Prerequisites
- Linux server (Ubuntu 22.04/Debian 12 or CentOS/Rocky 9)
- Docker installed (for container image scanning)
- Internet access for vulnerability database updates
- 1 GB+ free disk space for the vulnerability database
Install Trivy
# Ubuntu/Debian
sudo apt install -y wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo gpg --dearmor -o /usr/share/keyrings/trivy.gpg
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" \
| sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt update && sudo apt install -y trivy
# CentOS/Rocky
sudo tee /etc/yum.repos.d/trivy.repo << 'EOF'
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF
sudo dnf install -y trivy
# Verify installation
trivy --version
# Update the vulnerability database
trivy image --download-db-only
Scan Container Images
Scan a Docker image from Docker Hub or a local image:
# Scan a public image
trivy image nginx:1.25-alpine
# Scan a specific image tag
trivy image python:3.12-slim
# Scan a local Docker image (built but not pushed)
docker build -t myapp:latest .
trivy image myapp:latest
# Scan an image from a private registry
docker login registry.example.com
trivy image registry.example.com/myapp:v1.0
# Scan without pulling (using already-pulled image)
trivy image --input /path/to/image.tar # exported with docker save
Example output:
myapp:latest (alpine 3.19.0)
=================================
Total: 5 (HIGH: 3, MEDIUM: 2)
┌─────────────────────┬────────────────┬──────────┬───────────────────┬────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │
├─────────────────────┼────────────────┼──────────┼───────────────────┼────────────────┤
│ openssl │ CVE-2024-0727 │ HIGH │ 3.1.4-r2 │ 3.1.4-r3 │
│ libcrypto3 │ CVE-2024-0727 │ HIGH │ 3.1.4-r2 │ 3.1.4-r3 │
└─────────────────────┴────────────────┴──────────┴───────────────────┴────────────────┘
Scan Filesystems and Repositories
# Scan the current directory (source code, Dockerfiles, configs)
trivy fs .
# Scan a specific path
trivy fs /var/www/html
# Scan a Git repository
trivy repo https://github.com/org/repo
# Scan a local git repo
trivy repo /path/to/local/repo
# Scan only configuration files for misconfigurations
trivy config .
# Scan for secrets (API keys, passwords, etc.)
trivy fs --scanners secret .
# Combined scan: vulns + misconfigs + secrets
trivy fs --scanners vuln,config,secret /app
Severity Filtering and Output Formats
# Only show HIGH and CRITICAL vulnerabilities
trivy image --severity HIGH,CRITICAL nginx:latest
# Ignore unfixed vulnerabilities (no patch available)
trivy image --ignore-unfixed nginx:latest
# Exit with code 1 if any CRITICAL or HIGH vulnerabilities found
# (useful for CI/CD - fails the build)
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
# JSON output for machine processing
trivy image --format json --output results.json nginx:latest
# Table output (default)
trivy image --format table nginx:latest
# Template-based output
trivy image --format template --template '@html.tpl' --output report.html nginx:latest
# SARIF output (for GitHub Advanced Security)
trivy image --format sarif --output trivy-results.sarif myapp:latest
# JUnit XML for CI/CD reporting
trivy image --format template \
--template '@/usr/share/trivy/templates/junit.tpl' \
--output junit-results.xml myapp:latest
SBOM Generation
Generate a Software Bill of Materials (SBOM) documenting all components in a container:
# Generate SBOM in CycloneDX format
trivy image --format cyclonedx --output sbom-cyclonedx.json myapp:latest
# Generate SBOM in SPDX format
trivy image --format spdx-json --output sbom-spdx.json myapp:latest
# Generate SBOM and then scan the SBOM for vulnerabilities
trivy image --format cyclonedx --output sbom.json myapp:latest
trivy sbom sbom.json
# Generate SBOM from a filesystem
trivy fs --format cyclonedx --output sbom-fs.json /app
# Attest SBOM to an image using cosign (supply chain security)
trivy image --format cosign-vuln --output vuln.json myapp:latest
cosign attest --predicate vuln.json --type vuln myapp:latest
CI/CD Integration
GitHub Actions:
# .github/workflows/trivy-scan.yml
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
exit-code: 1
ignore-unfixed: true
- name: Upload results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
GitLab CI:
# .gitlab-ci.yml
container-scanning:
image:
name: aquasec/trivy:latest
entrypoint: [""]
stage: test
script:
- trivy image
--exit-code 1
--severity HIGH,CRITICAL
--ignore-unfixed
--format json
--output trivy-report.json
${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
artifacts:
reports:
container_scanning: trivy-report.json
allow_failure: false
Jenkins Pipeline:
// Jenkinsfile
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Security Scan') {
steps {
sh '''
trivy image \
--exit-code 0 \
--severity HIGH,CRITICAL \
--format json \
--output trivy-report.json \
myapp:${BUILD_NUMBER}
'''
// Fail on CRITICAL only
sh 'trivy image --exit-code 1 --severity CRITICAL myapp:${BUILD_NUMBER}'
}
post {
always {
archiveArtifacts artifacts: 'trivy-report.json'
}
}
}
}
}
Policy Enforcement with OPA
Use Trivy with OPA (Open Policy Agent) to enforce custom policies:
# Create a Rego policy file
cat > /tmp/trivy-policy.rego << 'EOF'
package trivy
# Deny if any CRITICAL vulnerabilities exist
deny[msg] {
input.Results[_].Vulnerabilities[_].Severity == "CRITICAL"
msg := sprintf("CRITICAL vulnerability found: %v", [input.Results[_].Vulnerabilities[_].VulnerabilityID])
}
# Deny if image runs as root
deny[msg] {
input.Metadata.ImageConfig.config.User == ""
msg := "Container runs as root - specify a non-root USER in Dockerfile"
}
EOF
# Scan with custom policy
trivy image --policy /tmp/trivy-policy.rego myapp:latest
# Scan Kubernetes manifests against policies
trivy config --policy /tmp/k8s-policy.rego /path/to/k8s-manifests/
Troubleshooting
Vulnerability database update fails:
# Update manually
trivy image --download-db-only
# Use an offline database (for air-gapped environments)
# Download on connected system:
trivy image --download-db-only
# Copy ~/.cache/trivy to the air-gapped system
trivy image --skip-db-update --offline-scan myapp:latest
# Check database location
trivy info
High false positive rate:
# Create a .trivyignore file to suppress known false positives
cat > .trivyignore << 'EOF'
# Ignore a specific CVE
CVE-2022-12345
# Ignore a specific CVE in a specific package
CVE-2023-67890 libssl
EOF
trivy image --ignorefile .trivyignore myapp:latest
Scan taking too long:
# Limit to specific vulnerability types
trivy image --scanners vuln myapp:latest # skip config and secret scanning
# Use --light mode (faster, less data)
trivy image --severity HIGH,CRITICAL myapp:latest
# Cache the database locally
trivy image --cache-dir /var/cache/trivy myapp:latest
Conclusion
Trivy provides fast, accurate vulnerability scanning for container images, filesystems, and source code with minimal configuration. Integrating Trivy into CI/CD pipelines with --exit-code 1 and --severity HIGH,CRITICAL ensures vulnerable images never reach production. SBOM generation provides a complete inventory of software components for compliance audits and supply chain security, and the SARIF output format integrates directly with GitHub's security alerting dashboard.


