Skip to content

Trivy Operator

Scan running containers for vulnerabilities, misconfigurations, and exposed secrets with the Trivy Operator.

Time: ~15 minutes Difficulty: Intermediate

  • How Trivy Operator automatically scans container images in your cluster
  • Reading VulnerabilityReport CRDs to find CVEs by severity
  • Comparing scan results between vulnerable and hardened images
  • ConfigAuditReport for Kubernetes misconfiguration detection
  • How vulnerability scanners work with CVE databases

Trivy Operator must be installed via Helm before running this demo:

Terminal window
helm repo add aquasecurity https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aquasecurity/trivy-operator \
--namespace trivy-system \
--create-namespace \
--set trivy.ignoreUnfixed=true

Wait for Trivy Operator to be ready:

Terminal window
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=trivy-operator -n trivy-system --timeout=120s

Navigate to the demo directory:

Terminal window
cd demos/trivy-operator

Create the namespace:

Terminal window
kubectl apply -f manifests/namespace.yaml

Deploy the vulnerable applications (these use old images with known CVEs):

Terminal window
kubectl apply -f manifests/vulnerable-app.yaml

Deploy the secure application for comparison:

Terminal window
kubectl apply -f manifests/secure-app.yaml

Check that all pods are running:

Terminal window
kubectl get pods -n trivy-demo

Wait 60-90 seconds for Trivy Operator to scan the running pods. The operator watches for new pods and automatically triggers image scans.

Terminal window
kubectl get vulnerabilityreports -n trivy-demo

You should see reports for each container:

NAME REPOSITORY TAG SCANNER AGE
replicaset-old-nginx-xxx-nginx nginx 1.21 Trivy 2m
replicaset-old-redis-xxx-redis redis 6.0 Trivy 2m
replicaset-secure-nginx-xxx-nginx nginx 1.25... Trivy 2m
Terminal window
kubectl get vulnerabilityreports -n trivy-demo -o wide

The output shows the severity breakdown. Old nginx should have CRITICAL and HIGH findings.

To see the actual vulnerabilities, extract them from a report:

Terminal window
kubectl get vulnerabilityreports -n trivy-demo \
-l trivy-operator.resource.name=old-nginx \
-o jsonpath='{range .items[0].report.vulnerabilities[*]}{.severity}{"\t"}{.vulnerabilityID}{"\t"}{.title}{"\n"}{end}' | head -20

Sample output:

CRITICAL CVE-2023-44487 HTTP/2 Rapid Reset Attack
HIGH CVE-2023-4911 glibc buffer overflow
HIGH CVE-2022-3715 bash improper input validation
MEDIUM CVE-2022-48174 busybox denial of service
...

The secure nginx (1.25.3-alpine) should have far fewer findings:

Terminal window
kubectl get vulnerabilityreports -n trivy-demo \
-l trivy-operator.resource.name=secure-nginx \
-o jsonpath='{.items[0].report.summary}'

Expected output shows minimal or zero critical/high vulnerabilities:

{"criticalCount":0,"highCount":0,"mediumCount":2,"lowCount":5}

Trivy Operator also scans for Kubernetes misconfigurations:

Terminal window
kubectl get configauditreports -n trivy-demo

View details for a specific deployment:

Terminal window
kubectl get configauditreports -n trivy-demo -o wide

These reports flag missing security settings like:

  • No securityContext defined
  • Running as root
  • Missing readiness/liveness probes
  • No resource limits

The secure-nginx deployment should have fewer warnings because it includes securityContext.

manifests/
namespace.yaml # trivy-demo namespace
vulnerable-app.yaml # old-nginx (nginx:1.21) and old-redis (redis:6.0)
secure-app.yaml # secure-nginx (nginx:1.25.3-alpine with securityContext)

Trivy Operator workflow:

  1. Watches for new pods in the cluster using a Kubernetes controller
  2. Reads the container image references from pod specs
  3. Scans each image for vulnerabilities using the Trivy scanner
  4. Creates a VulnerabilityReport CRD for each container
  5. Runs Kubernetes best practice checks and creates ConfigAuditReport CRDs
  6. Updates reports when images are updated or new pods are created

VulnerabilityReport contains:

  • List of CVEs with severity (CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN)
  • Package name and version
  • Fixed version (if available)
  • Links to CVE database entries (NVD, GitHub Advisory)

ConfigAuditReport contains:

  • Checks against CIS Kubernetes Benchmark
  • Pod Security Standards violations
  • Best practices (resource limits, health probes, security contexts)

Why the old images have vulnerabilities:

  • nginx:1.21 was released in 2021 and contains unpatched OS packages (Debian)
  • redis:6.0 has known CVEs in the base Alpine image
  • Newer images (1.25.3-alpine) receive security patches and updates
  1. Deploy a pod with an older Alpine image to see new scan results appear:

    Terminal window
    kubectl run test-alpine --image=alpine:3.14 -n trivy-demo --command -- sleep infinity

    Wait 60 seconds, then check:

    Terminal window
    kubectl get vulnerabilityreports -n trivy-demo -l trivy-operator.resource.name=test-alpine
  2. Extract the summary counts for all reports using jq:

    Terminal window
    kubectl get vulnerabilityreports -n trivy-demo -o json | jq '.items[].report.summary'
  3. Filter for only CRITICAL vulnerabilities across all reports:

    Terminal window
    kubectl get vulnerabilityreports -n trivy-demo -o json \
    | jq '.items[].report.vulnerabilities[] | select(.severity == "CRITICAL") | {id: .vulnerabilityID, title: .title, pkg: .resource}'
  4. Check if Trivy found any embedded secrets in container images:

    Terminal window
    kubectl get secrets-reports -n trivy-demo

    (This requires enabling the --secret-scanner flag in the Trivy Operator Helm values)

  5. Review the ConfigAuditReport for the old-nginx deployment:

    Terminal window
    kubectl get configauditreports -n trivy-demo \
    -l trivy-operator.resource.name=old-nginx \
    -o yaml

    Look at the checks section to see best practice violations.

  6. Trigger a manual rescan by deleting a report (it will be recreated):

    Terminal window
    kubectl delete vulnerabilityreports -n trivy-demo -l trivy-operator.resource.name=old-nginx

    Wait 30 seconds and the report reappears.

Delete the demo namespace:

Terminal window
kubectl delete namespace trivy-demo

To remove Trivy Operator entirely:

Terminal window
helm uninstall trivy-operator -n trivy-system
kubectl delete namespace trivy-system

See docs/deep-dive.md for a detailed explanation of how CVE databases work, Trivy’s scanning architecture, VulnerabilityReport CRD structure, integrating Trivy into CI/CD pipelines, and comparing Trivy with other scanners like Grype, Snyk, and Clair.

Move on to Falco Runtime Security to learn how to detect threats in running containers using behavioral monitoring.