KCSA — Container Image Security
Why Container Image Security Matters
Every workload in Kubernetes runs inside a container, and every container starts from an image. If that image contains vulnerabilities, malware, or unnecessary packages, your cluster is at risk before a single request is served.
Container image security is part of the Container layer in the 4Cs model, but it also overlaps with the Code layer (dependencies baked into the image) and the supply chain (where the image and its components came from).
Think of a container image like the blueprint for a building. If the blueprint specifies a weak foundation or flawed materials, the building is vulnerable — no matter how good the security guards are.
Base Image Selection
The base image is the starting point for your container. Choosing the right base image is one of the simplest and most effective security decisions you can make.
Base Image Comparison
| Base Image | Size | Packages | Attack Surface | Use Case |
|---|---|---|---|---|
| Ubuntu / Debian | 70–130 MB | Many (apt, shell, networking tools) | Large | General purpose, debugging |
| Alpine | ~5 MB | Minimal (musl libc, busybox) | Small | Most production workloads |
| Distroless | ~2–20 MB | Application runtime only (no shell, no package manager) | Very small | Production — maximum security |
| Scratch | 0 MB | Nothing at all | Minimal | Statically compiled Go/Rust binaries |
| Red Hat UBI | ~70 MB | Curated, enterprise-supported | Medium | Enterprise / regulated environments |
Recommendations
- ✅ Use distroless or Alpine for production images — fewer packages mean fewer vulnerabilities
- ✅ Use scratch for statically compiled binaries (Go, Rust) — zero unnecessary components
- ❌ Avoid full OS images (Ubuntu, CentOS) in production unless absolutely needed
- ✅ Always pin the base image version — don’t use
latestor unversioned tags
A distroless image doesn’t have a shell, package manager, or any utility except the application runtime. This means even if an attacker gets code execution inside the container, they have almost nothing to work with.
Image Scanning
Image scanning is the process of analyzing a container image for known vulnerabilities (CVEs), misconfigurations, and sensitive data.
When to Scan
| Phase | Timing | Purpose |
|---|---|---|
| Development | Developer machine / IDE | Catch issues early |
| CI/CD | Build pipeline (before push) | Gate — block vulnerable images |
| Registry | After push to registry | Ongoing detection of new CVEs |
| Runtime | In-cluster scanning | Detect vulnerabilities in running images |
Image Scanning Tools
| Tool | Type | Key Features |
|---|---|---|
| Trivy | Open source (Aqua Security) | Fast, comprehensive, scans images + IaC + SBOM; most popular OSS scanner |
| Grype | Open source (Anchore) | Vulnerability scanner for containers and filesystems |
| Clair | Open source (Quay/Red Hat) | Static analysis of container images; used by Quay registry |
| Snyk | Commercial (free tier) | Developer-friendly; integrates with IDE and Git |
| Docker Scout | Commercial (Docker) | Built into Docker Desktop; vulnerability analysis |
| Harbor Scanner | Built-in to Harbor | Integrates Trivy or Clair for automatic scanning on push |
For the KCSA exam, Trivy is the most commonly referenced open-source scanner. Know what it does and when it’s used.
Example: Scanning with Trivy
# Scan a container image for vulnerabilities
trivy image nginx:1.25
# Scan and fail if HIGH or CRITICAL vulnerabilities are found
trivy image --severity HIGH,CRITICAL --exit-code 1 nginx:1.25
# Scan a local Dockerfile
trivy config Dockerfile
Vulnerability Databases
Image scanners check against vulnerability databases to identify known issues:
| Database | Description |
|---|---|
| CVE (Common Vulnerabilities and Exposures) | Unique identifiers for publicly known vulnerabilities (e.g., CVE-2021-44228 for Log4Shell) |
| NVD (National Vulnerability Database) | US government database that enriches CVEs with severity scores (CVSS) |
| OSV (Open Source Vulnerabilities) | Google’s database for open-source vulnerabilities |
| GitHub Advisory Database | Vulnerability advisories linked to specific packages and versions |
CVSS Severity Ratings
| Severity | CVSS Score | Action |
|---|---|---|
| Critical | 9.0–10.0 | Fix immediately — block deployment |
| High | 7.0–8.9 | Fix before next release |
| Medium | 4.0–6.9 | Plan remediation |
| Low | 0.1–3.9 | Monitor; fix when convenient |
Image Signing and Verification
How do you know that the image you’re deploying is actually the one your CI pipeline built? Image signing provides this assurance.
Why Sign Images?
- Integrity — prove the image hasn’t been tampered with since it was built
- Authenticity — prove the image was built by a trusted pipeline or person
- Non-repudiation — create an auditable record of who built what
Signing Tools
| Tool | Description |
|---|---|
| cosign (Sigstore) | Modern, keyless image signing; the most widely adopted tool in the cloud native ecosystem |
| Notary v2 | Docker Content Trust / CNCF project for image signing |
| in-toto | Framework for supply chain integrity (broader than just image signing) |
Example: Signing with cosign
# Sign an image (keyless mode using OIDC identity)
cosign sign myregistry.io/myapp:v1.2.3
# Verify an image signature
cosign verify myregistry.io/myapp:v1.2.3 \
--certificate-identity=ci@mycompany.com \
--certificate-oidc-issuer=https://accounts.google.com
For the KCSA exam, know that cosign is the standard tool for signing container images, and Sigstore is the project behind it. Sigstore also includes Rekor (transparency log) and Fulcio (certificate authority).
Dockerfile Best Practices for Security
How you write your Dockerfile directly impacts the security of the resulting image.
Security Best Practices
| Practice | Why It Matters | Example |
|---|---|---|
| Use minimal base images | Reduce attack surface | FROM gcr.io/distroless/static |
| Don’t run as root | Limit privilege escalation | USER 1001 |
| Pin image versions | Avoid pulling unexpected changes | FROM node:20.11.0-alpine (not FROM node:latest) |
| Use multi-stage builds | Keep build tools out of the final image | Build in stage 1, copy binary to stage 2 |
| Don’t store secrets in images | Secrets in layers are recoverable | Use build-time secrets or external secret managers |
| Minimize layers | Reduce complexity and size | Combine related RUN commands |
Use .dockerignore |
Prevent secrets/unnecessary files from entering the build context | Exclude .git, .env, node_modules |
Set COPY over ADD |
ADD can auto-extract archives and fetch URLs — unexpected behavior |
Use COPY unless you specifically need ADD behavior |
| Scan during build | Catch issues before the image is pushed | Integrate Trivy into your build pipeline |
Example: Secure Multi-Stage Dockerfile
# --- Stage 1: Build ---
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
# --- Stage 2: Runtime ---
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER 65534:65534
ENTRYPOINT ["/server"]
What’s good about this:
- Multi-stage build — Go toolchain isn’t in the final image
- Distroless base — no shell, no package manager
- Non-root user — runs as user 65534 (nobody)
- Static binary — no runtime dependencies needed
Multi-Stage Builds for Smaller Attack Surface
Multi-stage builds are a powerful technique to separate the build environment from the runtime environment.
| Stage | Contents | In Final Image? |
|---|---|---|
| Build stage | Compiler, build tools, source code, test frameworks | ❌ |
| Runtime stage | Application binary, runtime libraries only | ✅ |
Benefits:
- Dramatically smaller images — often 10–100x smaller than single-stage builds
- No build tools in production — attackers can’t use compilers or package managers
- Fewer CVEs — less software = fewer vulnerabilities to scan and patch
Image Digest vs Tag — Why Tags Are Mutable
This is a critical concept for the KCSA exam.
Tag vs Digest
| Property | Tag | Digest |
|---|---|---|
| Example | nginx:1.25 |
nginx@sha256:a8281ce42... |
| Mutable? | ✅ Yes — can point to different images over time | ❌ No — cryptographic hash of the image content |
| Trustworthy? | ❌ Can be overwritten silently | ✅ Guarantees exactly which image you get |
| Human-friendly? | ✅ Easy to read | ❌ Long hash string |
Why This Matters
- If you deploy
nginx:latest, someone could push a malicious image with the same tag — and your cluster pulls it without question - Digests are immutable —
sha256:a8281ce42...always refers to the same content - For production deployments, always reference images by digest
A tag is like a sticky note on a box — anyone can move it. A digest is like a fingerprint — it uniquely and permanently identifies the contents.
Container Registry Security
The registry is where your images live. If the registry is compromised, every image pulled from it is potentially tainted.
Registry Security Practices
- ✅ Use private registries — don’t pull from public registries in production
- ✅ Enable vulnerability scanning on push (Harbor, ECR, GCR all support this)
- ✅ Enforce access control — limit who can push vs. who can pull
- ✅ Enable content trust — require signed images
- ✅ Use immutable tags — prevent overwriting existing image tags
- ✅ Audit access logs — know who pushed what and when
- ✅ Set up image retention policies — automatically clean up old, unpatched images
We’ll cover registry security in more detail in the Artifact Repository & Registry Security topic.
Key Takeaways for the Exam
- Use minimal base images (distroless, Alpine, scratch) to reduce the attack surface
- Trivy is the most widely referenced open-source image scanner — know what it does
- CVE is a unique identifier for vulnerabilities; NVD provides severity scores (CVSS)
- cosign (part of Sigstore) is the standard tool for image signing and verification
- Multi-stage builds keep build tools out of production images
- Image tags are mutable; digests are immutable — use digests for production deployments
- Never store secrets in images — they persist in image layers and can be extracted
- Non-root containers are a fundamental security practice — enforce with
runAsNonRoot: true - Scan images at every stage: development, CI/CD, registry, and runtime
- Dockerfile best practices = minimal base image + pin versions + non-root user + multi-stage build + no secrets
Next up: Software Supply Chain Security — securing the entire pipeline from source code to deployed artifacts.