Chapter 6
3 min read

The Audit

Image Hardening

Gord’s audit is image hardening in story form: reduce weight, reduce noise, reduce risk.

Gord’s audit is image hardening in story form: reduce weight, reduce noise, reduce risk.

Every unnecessary package inside a container becomes:

  • another dependency to patch,
  • another hiding place for vulnerabilities,
  • another clue an attacker can use to understand your system.

Just as Gord reduces Rothütle’s burden, image hardening reduces your container's attack surface.

How to harden your images

  • Start from minimal base images (distroless, slim, Alpine, Docker Hardened Images).
  • Use multi-stage builds so only compiled artifacts reach the final image.
  • Remove “just in case” tools — no shells, curl, ping, nano unless absolutely needed.
  • Prefer rebuilt clean images over patched ones.
  • Scan images regularly.

Example 1: Docker Hardened Images

FROM dhi.io/python:3.13

# Copy only necessary application files
COPY app/ /app/
WORKDIR /app/
CMD ["python", "main.py"]

Docker Hardened Images is a set of near-zero-CVE base images maintained by Docker, designed for production workloads. In December 2025, they were announced to be open-source and freely available at dhi.io.

Example 2: Alpine Minimal Base Image

FROM alpine:3.19
# Install only the package needed to run the app, then remove build cache
RUN apk --no-cache add python3

# Copy the application
COPY app/ /app/
WORKDIR /app/
CMD ["python3", "main.py"]

Alpine Linux is a small, security-oriented Linux distribution often used for minimal container images. Named after the Alpine mountains, that are not too far away from the Black Forest.

Example 3: Multi-Stage Build

# Stage 1: The Build Stage (contains heavy build tools)
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY . .
RUN go build -o /usr/local/bin/myapp ./cmd/

# Stage 2: The Final Runtime Stage (minimal, hardened)
FROM alpine:3.19
# Copy only the compiled binary from the builder stage
COPY --from=builder /usr/local/bin/myapp /usr/local/bin/myapp
# No shell or Go dependencies are carried over, only the single binary.
CMD ["/usr/local/bin/myapp"]

A quiet image behaves like a quiet traveler:
harder to detect, harder to exploit, and far more resilient.

Exercise

  1. Review one of your current container images. List all installed packages and identify any that are unnecessary for the application to run.

  2. Replace your current base image with a minimal alternative (like Alpine or Docker Hardened Images) and rebuild the image. Check for size reduction and vulnerability count before and after.

  3. Check your base image for CVEs using tools like Trivy or Docker Scout.

    $ docker scout cve <your-image>
    
  4. Which base image has the fewest vulnerabilities? Can you switch to it without breaking your application?

You might think a hardened warrior is someone in heavy armor