Trivy in CI/CD: Container en IaC Vulnerability Scanning Automatiseren in 2026
Praktische gids voor het inzetten van Trivy in je CI/CD-pipeline: container-images en IaC scannen, SBOM's genereren, false positives filteren met VEX, en de Trivy Operator draaien in Kubernetes.
Trivy is een open-source vulnerability scanner van Aqua Security die container-images, IaC-bestanden, Kubernetes-manifests en Git-repositories analyseert op CVE's, misconfiguraties en gelekte secrets - en die scan hoort in je CI/CD-pipeline te draaien, niet op de laptop van de laatste ontwikkelaar die het toevallig lokaal probeerde. In deze gids laat ik zien hoe ik Trivy in 2026 uitrol op Linux-runners, hoe ik false positives beheersbaar houd met VEX en .trivyignore, en welke pipeline-snippets je direct kunt overnemen voor GitHub Actions, GitLab CI en de Trivy Operator in Kubernetes.
Trivy 0.58+ scant containers, filesystems, Git-repos, Kubernetes-clusters, Terraform, CloudFormation, Helm en Dockerfiles vanuit één binary.
Draai scans in CI met --exit-code 1 --severity HIGH,CRITICAL zodat kwetsbare builds automatisch falen zonder over LOW/MEDIUM te struikelen.
Upload SARIF-output naar GitHub Advanced Security of GitLab's container_scanning-artifact voor inline PR-comments.
Gebruik VEX-documenten (OpenVEX) in plaats van .trivyignore als je auditbare, ondertekende uitzonderingen wilt.
De Trivy Operator geeft continue scans van Pods, ConfigAuditReports en compliance-checks tegen CIS-benchmarks in een levend Kubernetes-cluster.
Voor supply-chain-veiligheid combineer je Trivy's SBOM-output (CycloneDX/SPDX) met Cosign-handtekeningen zoals in de SLSA-gids beschreven.
Wat is Trivy en waarom staat het in bijna elke DevSecOps-pipeline?
Trivy is een unified security scanner die in één command image-lagen, packages, Go/Python/Node-dependencies, IaC-templates en zelfs gelekte tokens onder de loep neemt. Waar oudere tools zoals Clair of Anchore vaak een aparte database-service en registry-integratie nodig hadden, draait Trivy als een enkele statisch gelinkte binary die zijn kwetsbaarhedendatabase (op basis van GitHub Security Advisories, NVD en distro-bronnen zoals Red Hat OVAL en Debian Security Tracker) periodiek pulled vanuit een OCI-artifact op ghcr.io. Dat maakt hem uitermate geschikt voor ephemere CI-runners: geen daemon, geen persistente state, gewoon downloaden en scannen.
In mijn eigen pipelines gebruik ik Trivy op vier momenten. Ten eerste in de build-stage om Dockerfiles te lint'en op onveilige patterns zoals ADD vanaf externe URL's of missing USER-directives. Ten tweede na de build om de resulterende image te scannen op OS- en applicatie-CVE's voordat hij naar de registry wordt gepusht. Ten derde tijdens de deploy-stage om Kubernetes-manifests en Helm-charts te controleren op misconfiguraties zoals ontbrekende resource limits of privileged: true. En ten slotte draait de Trivy Operator continue in productie-clusters zodat je nieuwe CVE's meteen ziet, ook voor images die al maanden draaien. Deze vier controlepunten samen dekken het grootste deel van de OWASP Top 10 voor CI/CD.
# Een enkele smoke-test: draait Trivy en pullt de laatste DB
trivy image --download-db-only
trivy version
Trivy installeren op Linux en in containers
Op Debian- en Ubuntu-runners gebruik ik de officiële APT-repository omdat die versies snel worden bijgewerkt en pinnen eenvoudig is. Op RHEL, Rocky en Alma-runners werkt de dnf-repository identiek. Voor ephemere pipelines waar je geen root wilt gebruiken is het meestal handiger om de tarball binary direct te downloaden en te verifiëren met Cosign. Vergrendel altijd op een specifieke versie in CI - Trivy's ontwikkeltempo is hoog en een silent upgrade kan bestaande .trivyignore-bestanden ineens anders interpreteren.
De meest voorkomende use-case is een net-gebouwde image scannen voordat deze naar de registry gaat. In tegenstelling tot wat de default output suggereert, wil je in CI vrijwel altijd filteren op HIGH en CRITICAL. Als je op MEDIUM gaat blokkeren, zal je pipeline binnen een week rood staan op transitieve dependencies waar geen fix voor is, en dan beginnen teams met --ignore-unfixed te gooien in plaats van gericht te triageren. (Ik heb dat bij een klant precies zo zien gebeuren binnen tien dagen.) De volgende invocation gebruik ik als basissetup, aangevuld met een .trivyignore-bestand voor bewuste uitzonderingen.
# Scan een lokaal gebouwde image, faal alleen op fixable HIGH/CRITICAL
trivy image \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--exit-code 1 \
--format table \
--scanners vuln,secret,misconfig \
--timeout 10m \
registry.example.com/api:$CI_COMMIT_SHA
De optie --scanners vuln,secret,misconfig laat Trivy in dezelfde run ook controleren op hardcoded API-tokens (via de trufflehog-achtige regels die Aqua onderhoudt) en misconfiguraties in Dockerfile-lagen. Voor images die je op je eigen private registry hebt staan geef je credentials mee via de standaard Docker-config (~/.docker/config.json) of environment variables TRIVY_USERNAME en TRIVY_PASSWORD. Bij het scannen van registries met Cosign-handtekeningen is het verstandig eerst de handtekening te verifiëren en pas daarna te scannen, zoals ik ook beschreef in mijn eerdere gids over software supply chain beveiliging met SBOM, Sigstore en SLSA.
Trivy in GitHub Actions integreren
Voor GitHub Actions is de officiële aquasecurity/trivy-action de eenvoudigste weg, maar in productie gebruik ik liever de binary direct met caching - dat scheelt 30-60 seconden per run en houdt controle over de precieze versie. De volgende workflow bouwt een image, scant hem, en upload de bevindingen als SARIF zodat ze verschijnen in het Security-tabblad van je repository en in PR-annotaties. Dit werkt in combinatie met GitHub Advanced Security, maar de SARIF-upload zelf is gratis voor publieke repositories.
GitLab heeft een native container_scanning-artifact-type dat de output direct integreert in de Merge Request-widget, wat een fijne developer experience oplevert. Trivy kan dat formaat direct produceren via --format template --template "@contrib/gitlab.tpl" of de nieuwere --format gitlab. Voor GitLab Ultimate-gebruikers verschijnt de output ook in het Security Dashboard. In de onderstaande job maak ik gebruik van de needs-keyword zodat de scan direct start zodra de build klaar is, zonder op de rest van de stage te wachten.
Trivy heeft de misconfiguratie-scanner van tfsec en cfsec geïntegreerd (die projecten zijn in 2023 gearchiveerd ten gunste van Trivy), dus je kunt met één tool je Terraform, CloudFormation, Kubernetes-manifests, Helm-charts en Dockerfiles controleren. De regels komen uit de Aqua Vulnerability Database en bevatten checks als "S3-bucket zonder encryption", "container met root user" en "Kubernetes Pod zonder securityContext". Dit is de laag waar ik in mijn projecten de meeste ROI zie: één Dockerfile-fix voorkomt tientallen container-CVE's later.
# Scan alle IaC in de huidige repo, output JSON voor verdere verwerking
trivy config \
--severity HIGH,CRITICAL \
--format json \
--output iac-report.json \
.
# Alleen Kubernetes-manifests, met eigen policy-directory
trivy config \
--policy ./policies \
--namespaces custom \
./deploy/k8s/
Voor teams die al aan compliance werken zoals CIS Benchmarks of PCI-DSS is er een handige --compliance-flag. Ik combineer deze scans meestal met de aanpak uit mijn gids over Linux security auditing met Lynis en OpenSCAP - Trivy dekt de "shift-left" IaC-kant, Lynis en OpenSCAP dekken de "runtime" kant van dezelfde compliance-doelen. Je krijgt dan een compleet auditspoor van code tot productie.
# CIS Kubernetes v1.24 benchmark tegen een live cluster
trivy k8s --compliance k8s-cis-1.24 --report summary cluster
SBOM en VEX genereren met Trivy
Onder de EU Cyber Resilience Act moet iedere software-leverancier vanaf 2027 een SBOM aanleveren, en Trivy is een van de eenvoudigste manieren om die verplichting te vervullen. Trivy produceert SBOM's in CycloneDX 1.6 en SPDX 2.3, de twee formaten die door de meeste downstream-tools worden geaccepteerd. Belangrijker nog: je kunt Trivy zowel voeden met een SBOM (dan hoeft hij de image niet opnieuw uit te pakken) als er een uit laten rollen, wat SBOM-based scanning tot een repeatable process maakt in plaats van elke keer opnieuw.
# Genereer een CycloneDX SBOM
trivy image --format cyclonedx --output api.cdx.json registry.example.com/api:v1.4.0
# Scan later diezelfde SBOM tegen de laatste CVE-database
trivy sbom --severity HIGH,CRITICAL api.cdx.json
Voor exploitability context is er sinds Trivy 0.50 volledige ondersteuning voor OpenVEX. Een VEX-document beschrijft voor elke CVE of jouw specifieke gebruik van een package daadwerkelijk kwetsbaar is - handig als je 200 CVE's ziet in een Alpine-image maar er slechts 3 relevant zijn omdat je de affected code path niet gebruikt. VEX vervangt in mijn workflow steeds vaker de plompe .trivyignore, omdat het auditbaar en ondertekenbaar is, en je het samen met je SBOM aan klanten kunt leveren.
# Draai een scan met VEX-document als context
trivy image --vex ./vex/api-v1.4.0.openvex.json registry.example.com/api:v1.4.0
Wat is het verschil tussen Trivy en Grype?
Trivy en Grype van Anchore zijn de twee gangbare open-source scanners in het DevSecOps-landschap, en ze worden vaak in dezelfde adem genoemd. Het praktische verschil zit vooral in de scope: Trivy scant containers, IaC, secrets, licenses en Kubernetes-clusters uit één binary, terwijl Grype zich strikt op vulnerability scanning van packages en SBOM's richt en Anchore-tools zoals Syft (SBOM), Grant (licentie) en Kubebench voor de andere taken gebruikt.
Feature
Trivy
Grype
Container scanning
Ja
Ja
IaC / misconfig scanning
Ja (Terraform, K8s, Docker)
Nee (aparte tool)
Secret scanning
Ja, ingebouwd
Nee
SBOM-generatie
CycloneDX, SPDX
Nee (via Syft)
Kubernetes cluster-scan
Ja, incl. Operator
Nee
VEX-ondersteuning
Ja (OpenVEX, CSAF)
Beperkt
DB-grootte / snelheid
~200 MB, gemiddeld
~150 MB, iets sneller
License
Apache 2.0
Apache 2.0
Voor teams die één tool willen inzetten kies ik in de regel Trivy vanwege de bredere scope en de sterke Kubernetes-integratie. Grype is te overwegen als je al zwaar leunt op de Anchore-stack (Syft voor SBOM's, Grant voor licenties) - dan is de integratie tussen die tools iets natuurlijker. Meten is weten: draai beide een week naast elkaar op je grootste image en vergelijk de findings. In mijn ervaring verschillen ze zelden op de CRITICAL's, wel op de long-tail van LOW-findings.
False positives negeren met .trivyignore en VEX
Elke serieuze CI-integratie loopt binnen twee weken tegen findings aan die niet oplosbaar zijn of niet exploiteerbaar in de context van jouw applicatie. Als je die niet gestructureerd afhandelt, gebeurt er een van twee dingen: teams krijgen "alert fatigue" en zetten Trivy uit, of ze gaan || true in de pipeline zetten. Beide zijn erger dan geen scan. Trivy heeft drie manieren om uitzonderingen te maken, en welke je kiest hangt af van hoeveel audit-traceerbaarheid je nodig hebt.
Snel en dirty: .trivyignore
# .trivyignore - één regel per CVE, met optionele expiration en reden
CVE-2023-45853 # zlib, alleen in build-tools, niet in runtime image (expires: 2026-12-31)
CVE-2024-7264 # libcurl, niet reachable vanuit onze code
Één-shot scans in CI vangen kwetsbaarheden af op het moment van de build, maar nieuwe CVE's worden dagelijks gepubliceerd - een image die vandaag "clean" was, kan morgen een CRITICAL hebben. De Trivy Operator lost dit op door continu alle draaiende Pods in een cluster te scannen en de resultaten als CustomResources te publiceren (VulnerabilityReport, ConfigAuditReport, ExposedSecretReport, ClusterComplianceReport). Deze CR's kunnen vervolgens door Prometheus worden gescraped voor alerts, of door OPA/Kyverno-policies worden gebruikt om deploys van te-kwetsbare images te blokkeren.
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system --create-namespace \
--set trivy.severity="HIGH,CRITICAL" \
--set operator.metricsFindingsEnabled=true \
--set compliance.cron="0 */6 * * *"
# Nadat de operator draait, bekijk je bevindingen als reguliere K8s-objecten
kubectl get vulnerabilityreports -A
kubectl get clustercompliancereports
In combinatie met een intrusion-detection-setup zoals beschreven in mijn gids over Wazuh voor inbraakdetectie en SIEM krijg je een tweeledige verdediging: Trivy vertelt je welke bekende kwetsbaarheden je containers dragen, Wazuh detecteert of iemand actief probeert die uit te buiten. Dit is de setup die ik in 2026 zou aanraden voor iedere production-workload die niet 100% air-gapped draait.
# Prometheus-alert die triggert als er een niet-fixed CRITICAL in de cluster zit
- alert: TrivyCriticalUnfixedVulnerability
expr: trivy_image_vulnerabilities{severity="Critical",fixed_version!=""} > 0
for: 15m
labels:
severity: page
annotations:
summary: "Fixable CRITICAL CVE in {{ $labels.image_repository }}"
runbook: "https://runbooks.example.com/trivy-critical"
Veelgestelde vragen
Is Trivy gratis voor commercieel gebruik?
Ja. Trivy is open-source onder de Apache 2.0-licentie en volledig gratis voor commercieel gebruik, ook in gesloten productomgevingen. Aqua Security biedt daarnaast een commerciële Aqua Platform-versie met extra features zoals runtime protection en managed policy libraries.
Waarom faalt mijn Trivy-scan met een "Too Many Requests"-error?
ghcr.io rate-limit anonieme downloads van de Trivy-database tot ~100 requests per uur per IP. Op gedeelde CI-runners is dat snel op. Cache ~/.cache/trivy/db tussen runs, of authenticeer bij ghcr.io met een GitHub token via TRIVY_REGISTRY_TOKEN.
Hoe scan je een private image die tokens vereist?
Gebruik dezelfde credential-mechanismen als docker: een ~/.docker/config.json met een docker login-sessie, of environment variables TRIVY_USERNAME en TRIVY_PASSWORD. Voor cloud-registries zoals ECR, ACR en GCR gebruikt Trivy automatisch de aanwezige cloud-CLI-credentials.
Wat is het verschil tussen trivy image en trivy fs?
trivy image scant een OCI-image, inclusief alle lagen, base-OS packages en applicatie-dependencies. trivy fs scant een directory op de host - handig voor het scannen van je source-checkout of van een uitgepakte artifact voordat er een image wordt gebouwd. Voor Git-repos direct van remote gebruik je trivy repo.
Kan Trivy Kubernetes secrets in etcd scannen?
Nee, Trivy scant geen etcd-inhoud direct. Wel scant trivy k8s ExposedSecrets in ConfigMaps en decodeert Secret-objects om per ongeluk gecommitte credentials te vinden. Voor at-rest encryption van etcd zelf is secrets management met sops en age een betere aanpak.
Leer hoe je Linux-containers grondig beveiligt met Podman 5.8. Van rootless containers en seccomp-profielen tot SELinux-integratie en Trivy-scanning — met productieklare voorbeelden die je direct kunt toepassen.