Trivy Vulnerability Scanning
Automated container image vulnerability scanning with Trivy using CronJobs for continuous security monitoring of running workloads.
Overview
This template deploys Trivy as a scheduled vulnerability scanner within your Kubernetes cluster. While CI/CD scanning catches vulnerabilities at build time, new CVEs are discovered daily. Running Trivy as a CronJob scans your deployed images for newly published vulnerabilities, ensuring you’re alerted when a previously safe image becomes a risk.
Security threat addressed: Images that pass CI/CD scans can become vulnerable days or weeks later when new CVEs are published. Without continuous scanning, your production environment can harbor critical vulnerabilities without your knowledge.
When to use: Deploy this alongside your CI/CD image scanning. The CronJob scans all images running in specified namespaces and reports findings.
Threat Model
- Zero-day response: Continuous scanning detects newly published CVEs in your running images, enabling rapid patching.
- Drift detection: Identifies images that were deployed before scanning was implemented or that bypassed CI/CD checks.
- Compliance monitoring: Maintains an up-to-date vulnerability inventory for SOC2, PCI-DSS, and HIPAA audit requirements.
MITRE ATT&CK:
- T1195.002 — Supply Chain Compromise: Compromise Software Supply Chain: Detects compromised or vulnerable dependencies in your container images.
YAML Source
# ServiceAccount for Trivy scanner
apiVersion: v1
kind: ServiceAccount
metadata:
name: trivy-scanner
namespace: security-tools # ACTION REQUIRED: Change to your namespace
labels:
app.kubernetes.io/name: trivy-scanner
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
automountServiceAccountToken: false
---
# ClusterRole — read-only access to pods
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: trivy-scanner
labels:
app.kubernetes.io/name: trivy-scanner
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: trivy-scanner
labels:
app.kubernetes.io/name: trivy-scanner
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: trivy-scanner
subjects:
- kind: ServiceAccount
name: trivy-scanner
namespace: security-tools # ACTION REQUIRED: Match namespace above
---
# CronJob — Daily vulnerability scan
apiVersion: batch/v1
kind: CronJob
metadata:
name: trivy-image-scan
namespace: security-tools # ACTION REQUIRED: Match namespace above
labels:
app.kubernetes.io/name: trivy-scanner
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
spec:
schedule: "0 6 * * *" # Daily at 6 AM UTC
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
serviceAccountName: trivy-scanner
automountServiceAccountToken: true
restartPolicy: Never
containers:
- name: trivy
image: aquasec/trivy:latest # ACTION REQUIRED: Pin to specific version
args:
- image
- --severity=CRITICAL,HIGH
- --exit-code=0
- --format=json
- --output=/reports/scan-results.json
resources:
limits:
cpu: "1"
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
Deployment
- Create the namespace:
kubectl create namespace security-tools - Apply the template:
kubectl apply -f 22_trivy_vulnerability_scan.yaml - Trigger a manual scan:
kubectl create job trivy-manual --from=cronjob/trivy-image-scan -n security-tools - Check results:
kubectl logs -n security-tools job/trivy-manual
Verification
# Check CronJob status
kubectl get cronjobs -n security-tools
# View latest scan results
kubectl logs -n security-tools $(kubectl get pods -n security-tools -l job-name --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}')
# Verify RBAC is minimal
kubectl auth can-i --as=system:serviceaccount:security-tools:trivy-scanner list pods --all-namespaces
Further Reading
- Kubernetes Security Tools Compared — Trivy vs other scanning tools
- Kubernetes Supply Chain Security — Full supply chain hardening
- Template 25: Cosign Image Signing — Verify image integrity before deployment
- Template 11: Kyverno Disallow Latest Tag — Enforce image pinning policies
Get Full Access to This Template
This template is included in the Professional tier and above.
View Pricing Plans