Skip to content

Lab 02 — Build Minimal and Secure Container Images

Item Details
Lab ID K8S-IMAGE-SECURITY-LAB-02
Difficulty Intermediate to Advanced
Estimated Time 4–5 Hours
Environment Local Docker Environment / Kubernetes Training Cluster
Platform Docker Desktop, Podman, Amazon ECR, Azure ACR, Google Artifact Registry
Cost Free (Local) / Cloud Registry Charges May Apply
Primary Role DevSecOps Engineer
Supporting Roles Kubernetes Security Engineer, Platform Engineer, Cloud Security Engineer
Module Kubernetes Container Image & Supply Chain Security
Previous Lab Lab 01 — Scan Container Images
Next Lab Lab 03 — Secure Private Container Registries

CloudNova Technologies has modernised its Kubernetes platform and introduced mandatory container image scanning before deployment.

Although image scanning has reduced deployment risk, the security team discovered another major issue.

Many development teams are still building containers that include:

  • Full Linux distributions
  • Development tools
  • Package managers
  • Shell utilities
  • Compilers
  • Unnecessary libraries
  • Root users
  • Large attack surfaces
  • Excessive image sizes
  • Poor Dockerfile practices

During a Red Team assessment, attackers successfully exploited an application vulnerability.

Because the container image contained unnecessary tools, the attacker was able to:

  • Download additional payloads
  • Install new packages
  • Execute shells
  • Explore the container filesystem
  • Increase persistence opportunities

The investigation concluded that although the application vulnerability was the initial entry point, the oversized container image significantly increased the attack surface.

The CISO has mandated that all future Kubernetes workloads must use hardened production images that follow enterprise container security standards.

Your mission is to redesign an insecure container image into a production-ready hardened image suitable for Kubernetes deployment.


By completing this lab, you will learn how to:

  • Understand secure container image design
  • Build production-ready Dockerfiles
  • Reduce container attack surface
  • Use minimal base images
  • Implement multi-stage builds
  • Remove unnecessary build dependencies
  • Configure non-root users
  • Configure file permissions
  • Protect secrets during image builds
  • Optimise image size
  • Generate deterministic builds
  • Pin image versions
  • Use immutable image digests
  • Scan hardened images
  • Compare insecure and secure images
  • Produce an enterprise image hardening assessment

Developer
Source Code
Multi-Stage Build
Minimal Runtime Image
Image Security Scan
SBOM Generation
Image Signing
Private Registry
Kubernetes Deployment

Trusted Base Image
Minimal Packages
Multi-Stage Build
Non-Root User
Least Privilege
Image Scan
SBOM
Image Signing
Deployment

By the end of this lab, you will have:

  • Built an insecure image
  • Built a hardened production image
  • Implemented multi-stage builds
  • Removed unnecessary packages
  • Reduced image size
  • Configured non-root execution
  • Hardened file permissions
  • Compared image layers
  • Compared attack surfaces
  • Scanned both images
  • Generated SBOMs
  • Produced an enterprise image hardening report

Before beginning ensure you have:

  • Docker Desktop or Docker Engine
  • Trivy
  • Git
  • Visual Studio Code
  • Internet connectivity
  • Kubernetes cluster (optional)
  • Completion of Lab 01

Tool Purpose
Docker Build images
Trivy Vulnerability scanning
Visual Studio Code Dockerfile development
Git Bash / PowerShell CLI
jq JSON processing
Syft SBOM generation (optional)

lab-02-build-secure-images/
├── app/
│ ├── app.py
│ ├── requirements.txt
│ └── Dockerfile.insecure
├── secure/
│ ├── Dockerfile.secure
│ └── .dockerignore
├── reports/
│ ├── insecure-scan.json
│ ├── secure-scan.json
│ ├── comparison.md
│ ├── sbom.json
│ └── build-report.md
└── evidence/

Task 01 — Review the Insecure Dockerfile

Section titled “Task 01 — Review the Insecure Dockerfile”

Create:

Dockerfile.insecure

Example:

FROM python:3.11
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python","app.py"]

Review problems.

Identify:

  • Large base image
  • Root user
  • No version pinning
  • No multi-stage build
  • Package manager retained
  • Entire build context copied
  • Large attack surface

Terminal window
docker build \
-t insecure-demo:v1 \
-f Dockerfile.insecure .

Verify.

Terminal window
docker images

Terminal window
docker run \
-it \
--rm \
insecure-demo:v1

Inside.

Terminal window
id

Expected.

uid=0(root)

Review.

Terminal window
whoami

Confirm.

Application runs as root.


Terminal window
docker images

Record.

Image Size
insecure-demo

Terminal window
docker history \
insecure-demo:v1

Review.

  • Layer count
  • Layer size
  • Package installation
  • Build commands

Terminal window
trivy image \
insecure-demo:v1

Export.

Terminal window
trivy image \
--format json \
--output reports/insecure-scan.json \
insecure-demo:v1

Record:

  • Critical
  • High
  • Medium
  • Low

Task 07 — Create a Multi-Stage Dockerfile

Section titled “Task 07 — Create a Multi-Stage Dockerfile”

Create.

Dockerfile.secure

Example.

FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
-r requirements.txt
COPY . .
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app .
CMD ["python","app.py"]

Review.

  • Build stage
  • Runtime stage
  • Reduced layers
  • Smaller runtime

Update.

RUN useradd \
-r \
-u 10001 \
appuser

Switch.

USER appuser

Build.

Terminal window
docker build \
-t secure-demo:v1 \
-f Dockerfile.secure .

Terminal window
docker run \
-it \
--rm \
secure-demo:v1 id

Expected.

uid=10001

Confirm.

Application no longer runs as root.


Review.

pip install \
--no-cache-dir

Benefits.

  • Smaller image
  • Less attack surface

Create.

.dockerignore

Example.

.git
.env
.vscode
__pycache__
*.log
*.pem
*.key

Explain.

Sensitive files should never enter build context.


Review insecure example.

COPY .env .

Never do this.

Instead use:

  • Kubernetes Secrets
  • BuildKit Secrets
  • Vault
  • External Secrets Operator

Avoid.

FROM python:latest

Use.

FROM python:3.11.10-slim

Better.

FROM python@sha256:<digest>

COPY \
--chown=appuser:appuser \
. .

Validate ownership.


Combine commands.

Instead of.

RUN apt update
RUN apt install
RUN rm

Use.

RUN apt update && \
apt install -y curl && \
rm -rf /var/lib/apt/lists/*

Review.

apt
apk
yum

Production images should avoid retaining unnecessary package managers whenever practical.


Terminal window
docker build \
-t secure-demo:v2 \
-f Dockerfile.secure .

Verify.

Terminal window
docker images

Record.

Image Size
insecure-demo
secure-demo

Discuss.

  • Reduced size
  • Faster downloads
  • Lower attack surface

Terminal window
docker history \
secure-demo:v2

Compare.

  • Layer count
  • Image size
  • Build steps

Terminal window
trivy image \
secure-demo:v2

Export.

Terminal window
trivy image \
--format json \
--output reports/secure-scan.json \
secure-demo:v2

Compare findings.


Document.

Finding Insecure Secure
Critical
High
Medium
Image Size
Runtime User
Layer Count

Terminal window
trivy image \
--format cyclonedx \
--output reports/sbom.json \
secure-demo:v2

Review components.


Terminal window
docker inspect \
secure-demo:v2

Confirm.

User:
10001

Inspect.

Terminal window
docker inspect \
secure-demo:v2

Review.

  • Labels
  • Entrypoint
  • User
  • Exposed ports

Compare.

Component Insecure Secure
Root User
Multi-stage
Minimal Image
Cache Removed
Package Manager Reduced
Smaller Size

Use immutable digest.

Example.

image:
python@sha256:<digest>

Avoid.

python:latest

Task 27 — Enterprise Security Assessment

Section titled “Task 27 — Enterprise Security Assessment”

Review.

  • Base image
  • User
  • Packages
  • Layers
  • Size
  • Vulnerabilities
  • SBOM
  • Image digest

Assign.

  • Approved
  • Conditional
  • Rejected

Task 28 — Produce Image Hardening Report

Section titled “Task 28 — Produce Image Hardening Report”

Create.

build-report.md

Include.

  • Image comparison
  • Vulnerabilities
  • Improvements
  • Image size reduction
  • Runtime user
  • Deployment recommendation

Collect.

  • Dockerfiles
  • Image history
  • Scan reports
  • Image sizes
  • Runtime UID
  • SBOM
  • Build report

Terminal window
docker image rm \
insecure-demo:v1
Terminal window
docker image rm \
secure-demo:v2

Review.

Terminal window
docker images

Control Status
Trusted base image
Minimal runtime
Multi-stage build
Non-root user
No secrets
Small attack surface
SBOM generated
Image scanned
Image digest recorded
Deployment approved

  • Root user
  • Embedded secrets
  • Malware
  • Unsupported base image
  • Large attack surface
  • High vulnerabilities
  • Mutable tags
  • Missing SBOM
  • Missing labels
  • Large image
  • Missing digest
  • Documentation
  • Metadata
  • Naming

After completing this lab you will be able to:

  • Build production-ready container images
  • Implement multi-stage builds
  • Configure non-root execution
  • Reduce attack surface
  • Optimise Dockerfiles
  • Generate SBOMs
  • Compare image layers
  • Scan hardened images
  • Prepare images for Kubernetes deployment

Why are multi-stage builds recommended?

Answer: They remove unnecessary build tools and reduce the size and attack surface of the final runtime image.

Why should production containers avoid running as root?

Answer: Running as a non-root user limits the impact of a compromise by reducing the privileges available to an attacker.

Why should .dockerignore include .env and key files?

Answer: To prevent sensitive files such as environment variables and private keys from being copied into the build context or image.

Why should images be referenced by digest instead of latest?

Answer: A digest is immutable and guarantees that Kubernetes deploys the exact approved image, whereas tags like latest can change over time.

What is the primary security benefit of using a minimal base image?

Answer: Fewer installed packages reduce the attack surface, decrease the number of vulnerabilities, and simplify patch management.


In this lab, you transformed an insecure development container into a hardened, production-ready image by applying enterprise DevSecOps best practices. You built a multi-stage Docker image, removed unnecessary build artifacts, configured a non-root runtime user, reduced the image size, protected build secrets, generated an SBOM, scanned the final image for vulnerabilities, and validated it for Kubernetes deployment.

You also compared insecure and hardened images to understand how Dockerfile design directly affects security, operational efficiency, and compliance.

These practices form a critical part of Kubernetes supply chain security and provide the foundation for secure image distribution through trusted container registries.


Next Lab: Lab 03 — Secure Private Container Registries

In the next lab, you will secure enterprise container registries by configuring repository access controls, enforcing immutable image tags, enabling vulnerability scanning, implementing image-signing workflows, applying registry lifecycle policies, and integrating secure registries with Kubernetes deployments.