Skip to content

Container Image Scanning

Why Image Scanning Matters

Container images are the fundamental unit of deployment in Kubernetes. Every running workload is built from an image, and that image may contain:

  • Known vulnerabilities (CVEs) in OS packages or application dependencies
  • Malware injected through compromised base images or supply chain attacks
  • Misconfigurations such as running as root, exposing unnecessary ports, or including secrets
  • Outdated packages with known exploits

A single vulnerable image deployed to your cluster can serve as an entry point for attackers to escalate privileges, move laterally, or exfiltrate data. Image scanning is your first automated line of defense.

CKS Exam Relevance

Trivy is the primary image scanning tool on the CKS exam. You must be able to install it, scan images, filter by severity, interpret results, and use different output formats. Expect at least one question involving Trivy.

Image Scanning Pipeline

Trivy Overview

Trivy is an open-source, comprehensive security scanner developed by Aqua Security. It is fast, easy to use, and covers multiple scanning targets.

What Trivy Can Scan

TargetDescriptionCommand Prefix
Container ImagesOS packages and language dependenciestrivy image
FilesystemsLocal project directoriestrivy fs
Git RepositoriesRemote repositoriestrivy repo
KubernetesRunning cluster resourcestrivy k8s
Configuration FilesIaC misconfigurationstrivy config
SBOMSoftware Bill of Materialstrivy sbom

What Trivy Detects

ScannerDescription
Vulnerabilities (vuln)Known CVEs in OS packages and libraries
Misconfigurations (misconfig)IaC issues in Dockerfiles, Kubernetes manifests, Terraform
Secrets (secret)Hardcoded passwords, API keys, tokens
Licenses (license)Software license compliance issues

Installing Trivy

Exam Note

Trivy will be pre-installed on the CKS exam environment. However, knowing how to install it helps during practice.

Install on Ubuntu/Debian

bash
# Add the Trivy repository
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
  gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb \
  $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy -y

Install via Binary

bash
# Download the latest release
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | \
  sh -s -- -b /usr/local/bin v0.50.0

# Verify installation
trivy version

Scanning Container Images

Basic Image Scan

bash
trivy image nginx:1.25

Sample Output:

nginx:1.25 (debian 12.4)
=========================
Total: 142 (UNKNOWN: 0, LOW: 82, MEDIUM: 46, HIGH: 12, CRITICAL: 2)

┌─────────────────────┬────────────────┬──────────┬────────────────────┬───────────────┬──────────────────────────────────────┐
│      Library        │ Vulnerability  │ Severity │ Installed Version  │ Fixed Version │              Title                   │
├─────────────────────┼────────────────┼──────────┼────────────────────┼───────────────┼──────────────────────────────────────┤
│ libssl3             │ CVE-2024-0727  │ CRITICAL │ 3.0.11-1~deb12u2   │ 3.0.13-1      │ openssl: denial of service via null  │
│                     │                │          │                    │               │ dereference                          │
├─────────────────────┼────────────────┼──────────┼────────────────────┼───────────────┼──────────────────────────────────────┤
│ libexpat1           │ CVE-2023-52425 │ CRITICAL │ 2.5.0-1            │ 2.6.0-1       │ expat: parsing large tokens can      │
│                     │                │          │                    │               │ trigger a denial of service           │
├─────────────────────┼────────────────┼──────────┼────────────────────┼───────────────┼──────────────────────────────────────┤
│ curl                │ CVE-2024-0853  │ HIGH     │ 7.88.1-10+deb12u5  │ 7.88.1-10+    │ curl: OCSP verification bypass with  │
│                     │                │          │                    │ deb12u6       │ TLS session reuse                    │
├─────────────────────┼────────────────┼──────────┼────────────────────┼───────────────┼──────────────────────────────────────┤
│ libc6               │ CVE-2024-2961  │ HIGH     │ 2.36-9+deb12u4     │ 2.36-9+       │ glibc: buffer overflow in iconv()    │
│                     │                │          │                    │ deb12u7       │                                      │
└─────────────────────┴────────────────┴──────────┴────────────────────┴───────────────┴──────────────────────────────────────┘

Filter by Severity

In the CKS exam, you are often asked to find only CRITICAL or HIGH vulnerabilities:

bash
# Show only CRITICAL and HIGH vulnerabilities
trivy image --severity CRITICAL,HIGH nginx:1.25

Common Mistake

The --severity flag uses comma-separated values with NO spaces: CRITICAL,HIGH -- not CRITICAL, HIGH.

Sample Output:

nginx:1.25 (debian 12.4)
=========================
Total: 14 (HIGH: 12, CRITICAL: 2)

┌─────────────────────┬────────────────┬──────────┬────────────────────┬───────────────┐
│      Library        │ Vulnerability  │ Severity │ Installed Version  │ Fixed Version │
├─────────────────────┼────────────────┼──────────┼────────────────────┼───────────────┤
│ libssl3             │ CVE-2024-0727  │ CRITICAL │ 3.0.11-1~deb12u2   │ 3.0.13-1      │
│ libexpat1           │ CVE-2023-52425 │ CRITICAL │ 2.5.0-1            │ 2.6.0-1       │
│ curl                │ CVE-2024-0853  │ HIGH     │ 7.88.1-10+deb12u5  │ 7.88.1-10+    │
│ ...                 │                │          │                    │ deb12u6       │
└─────────────────────┴────────────────┴──────────┴────────────────────┴───────────────┘

Filter Only Fixable Vulnerabilities

bash
# Show only vulnerabilities that have a fix available
trivy image --ignore-unfixed nginx:1.25

This is useful for actionable remediation -- only showing CVEs where an upgraded package version exists.

Scan Only for Specific Types

bash
# Scan for OS vulnerabilities only (skip language-specific)
trivy image --vuln-type os nginx:1.25

# Scan for language library vulnerabilities only
trivy image --vuln-type library python:3.11

# Scan for both (default behavior)
trivy image --vuln-type os,library nginx:1.25

Understanding Severity Levels

Trivy uses standardized CVSS-based severity classifications:

SeverityCVSS ScoreMeaningAction Required
CRITICAL9.0 - 10.0Easily exploitable, severe impactImmediate fix required
HIGH7.0 - 8.9Significant risk, likely exploitableFix as soon as possible
MEDIUM4.0 - 6.9Moderate risk, may require conditionsPlan remediation
LOW0.1 - 3.9Minimal risk, difficult to exploitFix when convenient
UNKNOWNN/ANot yet scoredInvestigate manually

Exam Strategy

On the CKS exam, you will typically need to identify images with CRITICAL or HIGH vulnerabilities. Use --severity CRITICAL,HIGH to quickly filter results.

Output Formats

Trivy supports multiple output formats for different use cases.

Table Format (Default)

bash
trivy image nginx:1.25
# Default human-readable table output

JSON Format

bash
trivy image --format json nginx:1.25

# Save to file
trivy image --format json -o results.json nginx:1.25

Sample JSON output (abbreviated):

json
{
  "Results": [
    {
      "Target": "nginx:1.25 (debian 12.4)",
      "Class": "os-pkgs",
      "Type": "debian",
      "Vulnerabilities": [
        {
          "VulnerabilityID": "CVE-2024-0727",
          "PkgName": "libssl3",
          "InstalledVersion": "3.0.11-1~deb12u2",
          "FixedVersion": "3.0.13-1",
          "Severity": "CRITICAL",
          "Title": "openssl: denial of service via null dereference"
        }
      ]
    }
  ]
}

Template Format

bash
# Use a custom Go template
trivy image --format template \
  --template '{{range .Results}}{{range .Vulnerabilities}}{{.VulnerabilityID}} {{.Severity}} {{.PkgName}}{{"\n"}}{{end}}{{end}}' \
  nginx:1.25

Sample Output:

CVE-2024-0727 CRITICAL libssl3
CVE-2023-52425 CRITICAL libexpat1
CVE-2024-0853 HIGH curl
CVE-2024-2961 HIGH libc6

SARIF Format (for CI/CD Integration)

bash
trivy image --format sarif -o trivy-results.sarif nginx:1.25

Scanning for Misconfigurations

Trivy can scan Kubernetes manifests and Dockerfiles for security misconfigurations:

Scan Kubernetes Manifests

bash
trivy config /path/to/k8s-manifests/

Sample Output:

Dockerfile (dockerfile)
========================
Tests: 23 (SUCCESSES: 19, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 1)

HIGH: Specify at least 1 USER command in Dockerfile
═══════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation.

MEDIUM: Add HEALTHCHECK instruction in your Dockerfile
═══════════════════════════════════════════════════════
HEALTHCHECK instruction allows Docker to test the application health.

deployment.yaml (kubernetes)
============================
Tests: 28 (SUCCESSES: 22, FAILURES: 6, EXCEPTIONS: 0)
Failures: 6 (HIGH: 3, MEDIUM: 2, LOW: 1)

HIGH: Container 'app' should set 'securityContext.runAsNonRoot' to true
═══════════════════════════════════════════════════════════════════════
HIGH: Container 'app' should set 'securityContext.readOnlyRootFilesystem' to true
═════════════════════════════════════════════════════════════════════════════════

Scan a Dockerfile

bash
trivy config --file-patterns "dockerfile:Dockerfile.prod" .

Scanning Running Containers in Kubernetes

Trivy can scan images of running containers in a Kubernetes cluster:

bash
# Scan all images in the cluster
trivy k8s --report summary cluster

# Scan a specific namespace
trivy k8s --namespace default --report summary

# Scan and get detailed vulnerability info
trivy k8s --report all --namespace production

Sample Summary Output:

Summary Report for kubernetes cluster
══════════════════════════════════════

Workload Assessment
┌───────────┬──────────────────────┬───────────────┬────────────────┬────────────────┐
│ Namespace │ Resource             │ Critical      │ High           │ Medium         │
├───────────┼──────────────────────┼───────────────┼────────────────┼────────────────┤
│ default   │ Deploy/nginx         │       2       │      12        │      46        │
│ default   │ Deploy/redis         │       0       │       3        │      18        │
│ kube-sys  │ Deploy/coredns       │       0       │       1        │       5        │
└───────────┴──────────────────────┴───────────────┴────────────────┴────────────────┘

Scanning a Tar Archive Image

If you have a saved image archive:

bash
# Save an image to a tar file
docker save nginx:1.25 -o nginx.tar

# Scan the tar archive
trivy image --input nginx.tar

Exam Scenario

In the CKS exam, you may be asked to scan an image that is available as a tar file on the node. Use trivy image --input <file.tar> for this.

CI/CD Integration

Exit Codes for Pipeline Gates

Trivy returns non-zero exit codes when vulnerabilities are found, making it ideal for CI/CD gates:

bash
# Exit with code 1 if CRITICAL vulnerabilities are found
trivy image --exit-code 1 --severity CRITICAL nginx:1.25

# Different exit codes for different severities
trivy image --exit-code 0 --severity MEDIUM,LOW \
  --exit-code 1 --severity CRITICAL,HIGH nginx:1.25

GitHub Actions Example

yaml
name: Security Scan
on: push

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:${{ github.sha }}'
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'
          ignore-unfixed: true

Jenkins Pipeline Example

groovy
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'docker build -t myapp:${BUILD_NUMBER} .'
            }
        }
        stage('Security Scan') {
            steps {
                sh '''
                    trivy image \
                      --exit-code 1 \
                      --severity CRITICAL,HIGH \
                      --ignore-unfixed \
                      --format json \
                      -o trivy-report.json \
                      myapp:${BUILD_NUMBER}
                '''
            }
            post {
                always {
                    archiveArtifacts artifacts: 'trivy-report.json'
                }
            }
        }
    }
}

Advanced Trivy Usage

Using a Trivy Cache

Trivy downloads vulnerability databases on first run. In air-gapped environments:

bash
# Download the DB manually
trivy image --download-db-only

# Skip DB update (use cached)
trivy image --skip-db-update nginx:1.25

# Specify cache directory
trivy image --cache-dir /tmp/trivy-cache nginx:1.25

Ignoring Specific CVEs

Create a .trivyignore file to suppress known false positives:

# .trivyignore
# Suppress specific CVEs with optional comments
CVE-2024-0727
CVE-2023-52425   # Accepted risk: not exploitable in our config
bash
# Trivy will automatically read .trivyignore in the current directory
trivy image nginx:1.25

# Or specify a custom ignore file
trivy image --ignorefile /path/to/.trivyignore nginx:1.25

Scanning Specific Image Layers

bash
# Show which layer introduced each vulnerability
trivy image --list-all-pkgs nginx:1.25

Quick Reference: Common Trivy Commands

CommandPurpose
trivy image <image>Scan a container image
trivy image --severity CRITICAL,HIGH <image>Filter by severity
trivy image --ignore-unfixed <image>Show only fixable CVEs
trivy image --format json -o out.json <image>JSON output to file
trivy image --input image.tarScan a saved image archive
trivy image --exit-code 1 <image>Exit with error if vulns found
trivy fs /pathScan a filesystem
trivy config /pathScan for misconfigurations
trivy k8s --report summary clusterScan a Kubernetes cluster
trivy image --skip-db-update <image>Use cached vulnerability DB

Key Takeaways

Summary

  1. Trivy is the go-to tool for the CKS exam -- know its commands cold
  2. Severity filtering with --severity CRITICAL,HIGH is essential for exam tasks
  3. JSON output with --format json -o file.json is useful for programmatic analysis
  4. Exit codes enable automated pipeline gates (--exit-code 1)
  5. Misconfiguration scanning with trivy config extends beyond just CVEs
  6. Image archives can be scanned with --input flag
  7. Always check for fixed versions -- --ignore-unfixed shows actionable items only

Common Exam Pitfalls

  • Forgetting the --severity flag syntax (comma-separated, no spaces)
  • Not knowing the --input flag for tar archives
  • Confusing trivy image (CVEs) with trivy config (misconfigurations)
  • Not filtering by severity when the question asks for specific severity levels

Released under the MIT License.