Introduction
A vulnerable library in a Docker image is a time bomb. It can reach production unnoticed and wait for months — until someone exploits it. The solution is automatic scanning of every image before deployment. Trivy does it in tens of seconds and integrates with every popular CI/CD system.
What Does Trivy Scan?
Trivy scans:
├── Docker Images
│ ├── Vulnerabilities in system packages (apt, rpm, apk)
│ ├── Vulnerabilities in application libraries (pip, npm, gem, go)
│ └── Hard-coded secrets and passwords
├── Kubernetes configuration files
│ ├── Missing resource limits
│ ├── Containers running as root
│ └── Privileged containers
└── Git repositories
└── Secrets in commit history
GitLab CI Integration
Basic integration — blocking the pipeline
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
stages:
- build
- security
- deploy
build-image:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_NAME .
- docker push $IMAGE_NAME
trivy-scan:
stage: security
image:
name: aquasec/trivy:0.48.3
entrypoint: [""]
variables:
TRIVY_USERNAME: $CI_REGISTRY_USER
TRIVY_PASSWORD: $CI_REGISTRY_PASSWORD
TRIVY_AUTH_URL: $CI_REGISTRY
TRIVY_CACHE_DIR: .trivy-cache
cache:
paths:
- .trivy-cache/
script:
- trivy image
--exit-code 1
--severity CRITICAL,HIGH
--no-progress
$IMAGE_NAME
allow_failure: false
deploy-production:
stage: deploy
needs:
- trivy-scan
script:
- echo "Deploy only if trivy-scan passed"
Extended integration with reports
trivy-scan-full:
script:
- trivy image
--format template
--template "@/contrib/gitlab.tpl"
--output gl-container-scanning-report.json
$IMAGE_NAME
- trivy image
--format template
--template "@/contrib/html.tpl"
--output trivy-report.html
$IMAGE_NAME
artifacts:
when: always
reports:
container_scanning: gl-container-scanning-report.json
paths:
- trivy-report.html
GitHub Actions Integration
name: Security Scan
on:
push:
branches: [ main, develop ]
jobs:
trivy-scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- 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: CRITICAL,HIGH
exit-code: 1
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
Secret Detection
trivy fs --scanners secret .
# In pipeline:
trivy-secret-scan:
script:
- trivy fs --scanners secret --exit-code 1 --severity CRITICAL,HIGH .
Exceptions — Ignoring False Positives
# .trivyignore
CVE-2023-12345 # false positive — library is not used
CVE-2023-67890 # waiting for upstream fix
Severity Thresholds Policy
| Severity | Pipeline action |
|---|---|
| CRITICAL | Block — no deployment |
| HIGH | Block (recommended) |
| MEDIUM | Warning |
| LOW | Information in reports |
Summary
Integrating Trivy with a pipeline is one of the best returns on investment in security — a dozen lines of configuration eliminates an entire class of vulnerabilities. Key principles:
- Scan every image before deployment
- Block pipeline on CRITICAL — always
- Use cache for the vulnerability database — speeds scans from ~2min to ~15s
- Integrate reports with GitLab Security Dashboard or GitHub Security