332 docker image hardening
Skill FluxonLab/Skillry/plugins/cloud-and-infrastructure/skills/332-docker-image-hardening
Installable, permission-bounded, multi-platform agent skills & subagents for Claude Code, Codex, Copilot & Gemini/Antigravity — 125 skills + 73 subagents across 18 departments, with a validation harness, native plugin marketplace, and full upstream attribution. by FluxonLab.
npx -y skills add FluxonLab/Skillry --skill 332-docker-image-hardeningAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when you need to review or author a Dockerfile for multi-stage builds, minimal or distroless base images, non-root runtime, layer caching, .dockerignore hygiene, pinned digests, image vulnerability scanning, and SBOM generation.
SKILL.md
7.7 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
Docker Image Hardening
Purpose
Conduct a structured review-and-author pass over a Dockerfile and its build context — covering multi-stage build structure, minimal/distroless base selection, non-root runtime user, layer-cache efficiency, .dockerignore hygiene, pinned base digests, secret leakage in layers, image vulnerability scanning, and SBOM generation. The goal is a small, reproducible, low-CVE image that runs as an unprivileged user and never bakes secrets into a layer. Findings must come from the actual Dockerfile and scan output, not assumptions.
When to use
- A PR adds or modifies a
Dockerfile,Containerfile,.dockerignore, or compose build context. - An image is large, slow to build, or rebuilds from scratch on every code change (bad cache order).
- The container runs as root or uses a
latest/unpinned base tag. - A vulnerability scan has not been run and CVE exposure of the image is unknown.
- You need an SBOM for supply-chain compliance.
- You are reviewing a base image choice (full OS vs slim vs distroless).
When not to use
- The change is orchestration only (Kubernetes manifests, compose service wiring) — use the Kubernetes or compose review instead.
- There is no container build in the repo at all.
- The image is a throwaway local-only dev sandbox with no distribution or production intent.
Procedure
1. Orient to the build context
find . -maxdepth 3 -iname "Dockerfile*" -o -iname "Containerfile" 2>/dev/null
cat .dockerignore 2>/dev/null || echo "NO .dockerignore — context may leak secrets/.git"
# Base images in use and whether tags are pinned
grep -nE "^FROM " Dockerfile
2. Check multi-stage structure and base image
# Count build stages — a build/runtime split should exist for compiled langs
grep -cE "^FROM " Dockerfile
# Flag floating tags (latest / no tag) and missing digest pins
grep -nE "^FROM .*(:latest|^FROM [^@]+$)" Dockerfile
grep -nE "^FROM .*@sha256:" Dockerfile || echo "no digest pins (tags can move)"
3. Verify non-root runtime
# Must create and switch to a non-root USER before CMD/ENTRYPOINT
grep -nE "^USER " Dockerfile || echo "NO USER — container runs as root (HIGH)"
grep -nE "adduser|useradd|addgroup|--chown" Dockerfile
# distroless nonroot variants imply UID 65532
grep -n "nonroot" Dockerfile
4. Audit secret leakage and layer hygiene
# Secrets must never be COPYd or ARG-baked into a layer
grep -nE "ARG .*(SECRET|TOKEN|PASSWORD|KEY)|ENV .*(SECRET|TOKEN|PASSWORD)" Dockerfile
grep -nE "COPY .*(\.env|id_rsa|\.pem|credentials)" Dockerfile
# .git, node_modules, secrets should be excluded from context
grep -E "\.git|node_modules|\.env|\*\.pem" .dockerignore 2>/dev/null || echo "context not trimmed"
5. Review layer-cache ordering
# Dependency manifests should be COPYd and installed BEFORE app source,
# so code edits don't bust the dependency cache layer.
grep -nE "^COPY |^RUN .*(install|ci|build)" Dockerfile
6. Scan the image and generate an SBOM
# Build for scanning (local tag, no push)
docker build -t app:scan .
# Vulnerability scan (choose one available)
trivy image --severity HIGH,CRITICAL app:scan 2>/dev/null \
|| docker scout cves app:scan 2>/dev/null \
|| grype app:scan 2>/dev/null
# SBOM
syft app:scan -o spdx-json > sbom.spdx.json 2>/dev/null \
|| docker sbom app:scan 2>/dev/null
# Image size
docker image inspect app:scan --format '{{.Size}}' | numfmt --to=iec 2>/dev/null
Concrete checks
- Multi-stage build separates build dependencies from the runtime image.
- Base image is minimal (slim, alpine, or distroless) appropriate to the language.
- Every
FROMis pinned to a specific tag, ideally with an@sha256:digest. - A non-root
USERis set beforeCMD/ENTRYPOINT. - No secrets are passed via
ARG/ENVorCOPYd into any layer. - A
.dockerignoreexcludes.git,node_modules,.env, keys, and build artifacts. - Dependency install layers come before application source for cache reuse.
-
RUNcommands clean their package caches in the same layer (no orphaned cache bloat). - A vulnerability scan reports no unaddressed HIGH/CRITICAL CVEs.
- An SBOM can be generated for the final image.
-
HEALTHCHECKis defined (or health is handled by the orchestrator). - No unnecessary build tools (compilers, curl, package managers) remain in the final stage.
Commands or Templates
# syntax=docker/dockerfile:1
# ---- build stage ----
FROM golang:1.22-bookworm AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download # cached unless go.mod/go.sum change
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/server
# ---- runtime stage (distroless, non-root) ----
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app /app
USER nonroot:nonroot # UID 65532, no shell, no package manager
EXPOSE 8080
ENTRYPOINT ["/app"]
# Node example: pinned digest, non-root, cache-friendly
FROM node:20-slim@sha256:<digest> AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY . .
USER node
CMD ["node", "server.js"]
# Safe build + scan + SBOM (local, no registry push)
docker build -t app:scan .
trivy image --severity HIGH,CRITICAL --exit-code 1 app:scan
syft app:scan -o spdx-json > sbom.spdx.json
Common issues & anti-patterns
FROM ubuntu:latest(or no tag) — non-reproducible and large attack surface.- Single-stage build shipping compilers, git, and dev headers into production.
- Container runs as root because no
USERis set. ARG GITHUB_TOKEN=...orCOPY .env .— secrets persist in image history forever.COPY . .beforeRUN npm ci— every code edit invalidates the dependency cache.- No
.dockerignore, so.gitand local secrets get copied into the build context. RUN apt-get installwithout removing/var/lib/apt/listsin the same layer — cache bloat.- Pushing an unscanned image to a registry.
Required output
Produce a structured report with:
- Base & structure — base image, tag/digest pin status, number of stages.
- Runtime user — root vs non-root; where
USERis set. - Secret hygiene — any secrets in ARG/ENV/COPY (redacted);
.dockerignorecoverage. - Cache efficiency — layer ordering issues that bust caching.
- Scan results — HIGH/CRITICAL CVE count from the scanner; SBOM availability.
- Size — final image size and the largest avoidable contributors.
- Findings table —
Dockerfile:line | issue | severity | concrete fix. - Next safe action — highest-priority hardening change.
Safety
- This is a review and authoring skill. Building and scanning a local image is safe; NEVER
docker pushto a registry, deploy, or run a container with mounted host secrets or privileged flags without explicit human approval. - Never bake real secret values into a Dockerfile,
ARG, orENV; use build secrets (--mount=type=secret) or runtime injection. - Redact any discovered credentials to
****and flag them for rotation; treat secrets in image history as compromised. - Do not run untrusted images with
--privileged, host networking, or host volume mounts. - Do not delete existing images/volumes on the host without approval.
- Report scanner findings; do not auto-upgrade base images in a way that silently changes runtime behavior — propose the change for human review.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.