Command Palette

Search for a command to run...

Hectal
Lab 2.3·Containers & InfrastructureMEDIUM

900 MB of someone else's vulnerabilities

ShopLite's first Dockerfile used node:latest: a full Debian with compilers, curl, git, and hundreds of CVEs — none of which the app ever uses.

Weakness
CWE-1104 · Use of unmaintained third-party components
Target
ShopLite image base layers
trivy image for OS packagesslim vs alpine vs distrolessmulti-stage buildspinning by digest + automated rebuilds

Run these techniques only against the lab you own. Using them on systems you don't have permission to test is illegal.

The threat

  1. 01Every package is attack surface

    A full OS base ships shells, package managers, compilers, and network tools. The app uses none of them, but after any code-execution bug, an attacker can use all of them. Each package also carries its own vulnerabilities, which you inherit and must track. A smaller image is a smaller target and much less scanner noise, so real findings stand out.

What's at risk

  • A long list of inherited CVEs, and ready-made tools for anyone who gets code execution inside the container.

Detect

  1. 01Compare bases with the same scanner

    terminal
    $ for img in node:22 node:22-slim node:22-alpine gcr.io/distroless/nodejs22-debian12; do
    printf "%-42s " "$img"; docker run --rm aquasec/trivy:0.63.0 image -q --scanners vuln --format json "$img" \
    | jq '[.Results[]?.Vulnerabilities[]?] | "\(length) vulns, \(map(select(.Severity=="CRITICAL" or .Severity=="HIGH")) | length) high+critical"'
    done
    ── output ──
    node:22 "1204 vulns, 147 high+critical"
    node:22-slim "96 vulns, 11 high+critical"
    node:22-alpine "9 vulns, 1 high+critical"
    gcr.io/distroless/nodejs22-debian12 "14 vulns, 0 high+critical"
    Exact numbers change daily as advisories are published and images rebuilt; the order of magnitude is the lesson.

Defend

  1. 01Multi-stage: build with tools, ship without them

    The build stage has npm and everything needed to install dependencies. The final stage copies only the app and its production node_modules onto a distroless base, which has no shell and no package manager, just the Node runtime and the libraries it needs. It runs as non-root by default (:nonroot tag).

    Vulnerable

    Dockerfilewhole filedocker
    FROM node:latest
    WORKDIR /app
    COPY . .
    RUN npm install
    CMD ["node", "server.js"]

    Hardened

    Dockerfilewhole filedocker
    # syntax=docker/dockerfile:1
    FROM node:22-alpine AS build
    WORKDIR /app
    COPY package.json package-lock.json ./
    RUN npm ci --omit=dev --ignore-scripts
    COPY server.js ./
    
    FROM gcr.io/distroless/nodejs22-debian12:nonroot
    WORKDIR /app
    COPY --from=build /app /app
    CMD ["server.js"]
  2. 02Pin by digest, rebuild on a schedule

    Pin the base image by digest for reproducibility (FROM image@sha256:...), and let Renovate or Dependabot open PRs when the upstream digest changes. Rebuild and redeploy at least weekly even without code changes, because base images receive security patches constantly.

Verify

  1. 01Re-scan the final image

    terminal
    $ docker build -t shoplite:distroless . && docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:0.63.0 image -q --severity HIGH,CRITICAL shoplite:distroless
    docker images shoplite --format '{{.Tag}} {{.Size}}'
    ── output ──
    (no HIGH or CRITICAL findings)
    distroless 168MB
    bad 1.12GB

The concepts

Choosing a base

slim Debian variants remove docs and build tools while keeping glibc compatibility. alpine is tiny (musl libc; occasionally incompatible with native modules). DISTROLESS and hardened minimal images (Google distroless, Chainguard/Wolfi, Docker Hardened Images) contain only the runtime and are rebuilt frequently. The trade-off: no shell for debugging. Use ephemeral debug containers (kubectl debug) instead of baking tools in.

Your turn

01

With no shell in the distroless image, how do you debug a running container?

Interview questions

01

How do you minimise vulnerabilities in container images?

0/4 · 0%