CKS — Image Vulnerability Scanning
Image Vulnerability Scanning
CKS Exam Domain: Supply Chain Security (20%)
Vulnerability scanning is the process of inspecting container images for known CVEs (Common Vulnerabilities and Exposures) in OS packages, application libraries, and configuration files. On the CKS exam, Trivy is the go-to tool — you are expected to know its core commands, severity filtering, and output interpretation.
An image that passes a scan today may fail tomorrow as new CVEs are disclosed. Scanning must happen at build time, before push to a registry, and continuously on images already running in the cluster.
1. Trivy — The Standard Scanner
Trivy is an open-source vulnerability scanner from Aqua Security. It scans container images, filesystems, Git repositories, and Kubernetes clusters.
Install Trivy
# macOS
brew install trivy
# Linux (Debian/Ubuntu)
sudo apt-get install -y wget apt-transport-https gnupg lsb-release
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 /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install -y trivy
# Or run as a container
docker run --rm aquasec/trivy image nginx:1.27
2. Scanning Container Images
Basic Image Scan
trivy image nginx:1.27
Output (truncated):
nginx:1.27 (debian 12.5)
=========================
Total: 142 (UNKNOWN: 2, LOW: 85, MEDIUM: 38, HIGH: 14, CRITICAL: 3)
┌──────────────────┬────────────────┬──────────┬─────────────────────┬───────────────┬──────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
├──────────────────┼────────────────┼──────────┼─────────────────────┼───────────────┼──────────────────────────────────┤
│ libcurl4 │ CVE-2024-XXXXX │ CRITICAL │ 7.88.1-10+deb12u5 │ 7.88.1-10+... │ curl: use-after-free in ... │
│ openssl │ CVE-2024-YYYYY │ HIGH │ 3.0.11-1~deb12u2 │ 3.0.13-1~... │ openssl: buffer overread in ... │
│ zlib1g │ CVE-2023-ZZZZZ │ MEDIUM │ 1:1.2.13-1 │ │ zlib: heap buffer overflow in ..│
└──────────────────┴────────────────┴──────────┴─────────────────────┴───────────────┴──────────────────────────────────┘
Filter by Severity
# Show only CRITICAL and HIGH vulnerabilities
trivy image --severity CRITICAL,HIGH nginx:1.27
# Show only CRITICAL
trivy image --severity CRITICAL nginx:1.27
CKS Exam Tip: When the exam says “find images with critical vulnerabilities,” use
--severity CRITICALto filter. This is a commonly tested flag.
Exit Code for CI/CD
# Exit with code 1 if CRITICAL or HIGH vulnerabilities found
trivy image --severity CRITICAL,HIGH --exit-code 1 nginx:1.27
echo $?
# 1 (vulnerabilities found)
# Exit 0 if only LOW/MEDIUM
trivy image --severity CRITICAL,HIGH --exit-code 1 gcr.io/distroless/static-debian12
echo $?
# 0 (no CRITICAL/HIGH vulnerabilities)
3. Severity Levels
Trivy classifies vulnerabilities by severity based on CVSS scores:
| Severity | CVSS Score | Action Required | Example |
|---|---|---|---|
| CRITICAL | 9.0 – 10.0 | Immediate remediation. Block deployment. | Remote code execution, privilege escalation |
| HIGH | 7.0 – 8.9 | Fix within days. Consider blocking. | Buffer overflow, SQL injection |
| MEDIUM | 4.0 – 6.9 | Fix in next release cycle. | Information disclosure |
| LOW | 0.1 – 3.9 | Track but low priority. | Minor information leak |
| UNKNOWN | N/A | Unscored CVE. Assess manually. | Newly disclosed, not yet scored |
4. Scanning Filesystems and Repositories
Trivy can scan more than just container images.
Scan Local Filesystem
# Scan the current directory for vulnerabilities in dependencies
trivy fs .
# Scan a specific path
trivy fs /path/to/project
# Scan only for misconfigurations (Dockerfile, Kubernetes YAML, Terraform)
trivy config .
Scan a Git Repository
trivy repo https://github.com/example/app
Scan a Running Kubernetes Cluster
# Summary report of all vulnerabilities in running workloads
trivy k8s --report summary
# Detailed report
trivy k8s --report all
# Scan a specific namespace
trivy k8s --namespace production --report summary
5. Output Formats
# Default: table format (human-readable)
trivy image nginx:1.27
# JSON (for programmatic parsing)
trivy image --format json --output results.json nginx:1.27
# SARIF (for GitHub Code Scanning, Azure DevOps)
trivy image --format sarif --output results.sarif nginx:1.27
# Template output (custom format)
trivy image --format template --template "@/path/to/template.tpl" nginx:1.27
Parse JSON Output with jq
# Get CRITICAL vulnerability IDs
trivy image --format json nginx:1.27 | \
jq -r '.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL") | .VulnerabilityID'
# Count vulnerabilities by severity
trivy image --format json nginx:1.27 | \
jq '[.Results[].Vulnerabilities[].Severity] | group_by(.) | map({(.[0]): length}) | add'
6. Grype — An Alternative Scanner
Grype by Anchore is another popular open-source vulnerability scanner.
Install Grype
# macOS
brew install grype
# Linux
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
Scan with Grype
# Basic scan
grype nginx:1.27
# Filter by severity
grype nginx:1.27 --only-fixed --fail-on critical
# Output as JSON
grype nginx:1.27 -o json
# Scan a directory (SBOM-based)
grype dir:/path/to/project
Trivy vs Grype
| Feature | Trivy | Grype |
|---|---|---|
| Maintainer | Aqua Security | Anchore |
| Image scanning | ✅ | ✅ |
| Filesystem scanning | ✅ | ✅ |
| Kubernetes scanning | ✅ (trivy k8s) |
❌ (separate tool) |
| Misconfig scanning | ✅ (trivy config) |
❌ |
| SBOM generation | ✅ (trivy sbom) |
Via Syft |
| CKS exam relevance | Primary | Supplementary |
CKS Exam Tip: Focus on Trivy. It is the scanner you are most likely to use on the exam.
7. Scanning Images Running in a Cluster
A common CKS exam task: “Find all images in namespace X with CRITICAL vulnerabilities.”
Step 1: List All Images in a Namespace
# Get all unique images in the "production" namespace
kubectl get pods -n production -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u
Example output:
nginx:1.25
registry.example.com/api:v2.3
redis:7.2
postgres:16.1
Step 2: Scan Each Image
# Scan all images and report only CRITICAL/HIGH
for img in $(kubectl get pods -n production -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u); do
echo "=== Scanning: $img ==="
trivy image --severity CRITICAL,HIGH --quiet "$img"
echo ""
done
Step 3: Delete Pods with Vulnerable Images
# Find Pods running a specific vulnerable image
kubectl get pods -n production -o json | \
jq -r '.items[] | select(.spec.containers[].image == "nginx:1.25") | .metadata.name'
# Delete the offending Pods
kubectl delete pod <pod-name> -n production
One-Liner: Scan and List Vulnerable Pods
# For each image, scan and if CRITICAL found, list Pods using that image
for img in $(kubectl get pods -n production -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u); do
if trivy image --severity CRITICAL --exit-code 1 --quiet "$img" 2>/dev/null; then
: # clean image
else
echo "CRITICAL CVEs in: $img"
kubectl get pods -n production -o json | \
jq -r --arg IMG "$img" '.items[] | select(.spec.containers[].image == $IMG) | .metadata.name'
fi
done
8. Integrating Scanning into CI/CD
GitHub Actions Example
name: Scan Container Image
on:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:$ .
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:$
format: table
severity: CRITICAL,HIGH
exit-code: 1 # Fail the build if vulnerabilities found
Pipeline Strategy
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Build │───→│ Scan │───→│ Sign │───→│ Push │
│ Image │ │ (Trivy) │ │ (Cosign) │ │ Registry │
└──────────┘ └────┬─────┘ └──────────┘ └──────────┘
│
▼ CRITICAL/HIGH found?
┌──────────┐
│ FAIL │
│ Build │
└──────────┘
Scanning must happen before push to registry. An image that enters the registry without scanning has bypassed the gate.
9. Ignoring Known False Positives
# Create a .trivyignore file
cat > .trivyignore <<EOF
# Accepted risk: library not reachable in our code path
CVE-2023-44487
# Disputed vulnerability
CVE-2024-12345
EOF
# Scan with ignore file
trivy image --ignorefile .trivyignore nginx:1.27
Caution: Only ignore vulnerabilities after explicit security review. Document the reason in the ignore file.
10. Scanning Image Layers
# Show which layer introduced each vulnerability
trivy image --list-all-pkgs nginx:1.27
# Use dive to inspect layer contents
dive nginx:1.27
Understanding which layer introduced a vulnerability helps decide whether to update the base image or the application dependencies.
Summary
| Command | Purpose |
|---|---|
trivy image <image> |
Scan a container image |
trivy image --severity CRITICAL,HIGH <image> |
Filter by severity |
trivy image --exit-code 1 <image> |
Fail CI pipeline |
trivy image --format json <image> |
Machine-readable output |
trivy fs . |
Scan local filesystem |
trivy k8s --report summary |
Scan running cluster |
trivy config . |
Scan for misconfigurations |
grype <image> |
Alternative scanner |
Practice Questions
Question 1
Scan the nginx:1.25 image and list only CRITICAL vulnerabilities. What command do you use?
Show Answer
trivy image --severity CRITICAL nginx:1.25
For a quiet output showing only the vulnerability table:
trivy image --severity CRITICAL --quiet nginx:1.25
Question 2
You need to find all Pods in the web namespace running images with HIGH or CRITICAL vulnerabilities and delete those Pods. Describe your approach.
Show Answer
# Step 1: Get all unique images in the namespace
kubectl get pods -n web -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u > /tmp/images.txt
# Step 2: Scan each image
while read -r img; do
echo "=== $img ==="
trivy image --severity CRITICAL,HIGH --exit-code 1 --quiet "$img"
if [ $? -ne 0 ]; then
echo "VULNERABLE: $img"
# Step 3: Find and delete Pods using this image
kubectl get pods -n web -o json | \
jq -r --arg IMG "$img" '.items[] | select(.spec.containers[].image == $IMG) | .metadata.name' | \
xargs -I {} kubectl delete pod {} -n web
fi
done < /tmp/images.txt
Question 3
What is the difference between trivy image, trivy fs, and trivy config?
Show Answer
| Command | Scans | Detects |
|---|---|---|
trivy image <image> |
Container image layers | OS package CVEs, library CVEs |
trivy fs <path> |
Local filesystem | Application dependency CVEs (package-lock.json, go.sum, requirements.txt) |
trivy config <path> |
Configuration files | Misconfigurations in Dockerfiles, Kubernetes YAML, Terraform, CloudFormation |
trivy imagepulls and scans the image filesystemtrivy fsscans source code dependency filestrivy configchecks for insecure configurations (e.g.,USER rootin Dockerfile, missingsecurityContextin Pod spec)
Question 4
How do you make a CI/CD pipeline fail if a container image has any CRITICAL vulnerabilities?
Show Answer
Use the --exit-code flag:
trivy image --severity CRITICAL --exit-code 1 myapp:latest
- If CRITICAL vulnerabilities are found, Trivy exits with code
1, which fails the CI step - If no CRITICAL vulnerabilities are found, Trivy exits with code
0, and the pipeline continues
In a CI pipeline:
# GitHub Actions
- name: Scan image
run: trivy image --severity CRITICAL --exit-code 1 myapp:$
Question 5
After scanning, you find that redis:7.2 has 3 CRITICAL vulnerabilities but redis:7.2.4 has 0. The cluster is running redis:7.2. What should you do?
Show Answer
-
Update the image tag in the Deployment/StatefulSet to
redis:7.2.4:kubectl set image deployment/redis-cache redis=redis:7.2.4 -n production -
Verify the rollout:
kubectl rollout status deployment/redis-cache -n production -
Re-scan the new image to confirm:
trivy image --severity CRITICAL redis:7.2.4 # Should show 0 CRITICAL -
Pin by digest to prevent future tag mutation:
# Get digest docker pull redis:7.2.4 docker inspect --format='{{index .RepoDigests 0}}' redis:7.2.4 # redis@sha256:abc123... # Update deployment to use digest kubectl set image deployment/redis-cache redis=redis@sha256:abc123... -n production -
Set up continuous scanning so you are alerted when new CVEs are disclosed against
redis:7.2.4.