k8s-security.pro
#22 Supply Chain Security professional

Trivy Vulnerability Scanning

Automated container image vulnerability scanning with Trivy using CronJobs for continuous security monitoring of running workloads.

CIS Benchmark
5.4.15.4.2
MITRE ATT&CK
T1195.002

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

  1. Create the namespace: kubectl create namespace security-tools
  2. Apply the template: kubectl apply -f 22_trivy_vulnerability_scan.yaml
  3. Trigger a manual scan: kubectl create job trivy-manual --from=cronjob/trivy-image-scan -n security-tools
  4. 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

Get Full Access to This Template

This template is included in the Professional tier and above.

View Pricing Plans