Analisador de Dockerfile
Analise Dockerfiles para problemas de segurança e oportunidades de otimização
Dockerfile Security and Best Practices Analysis
A Dockerfile is the blueprint for building container images — every instruction defines a layer that
becomes part of the final artifact deployed to production. Security vulnerabilities, misconfigurations,
and anti-patterns in Dockerfiles propagate to every container instance spawned from the resulting image.
A single RUN apt-get install without version pinning, a USER root left in
place, or a COPY . . that inadvertently bundles secrets can expose your infrastructure
to supply chain attacks, privilege escalation, and data leaks. This analyzer inspects Dockerfiles for
security issues, linting violations, and deviations from established best practices before the image
is ever built.
Unlike optimization tools that focus on reducing image size and build speed, a Dockerfile analyzer operates as a static analysis linter — it detects problems that would otherwise surface only at runtime or during a security audit. It flags running as root, use of deprecated instructions, missing health checks, insecure base images, exposed secrets in build arguments, and instructions that violate the principle of least privilege. The goal is to catch vulnerabilities at authoring time, before they enter your CI/CD pipeline.
Security Vulnerabilities in Dockerfiles
Container security starts with the Dockerfile. The most impactful security issues detectable through static analysis include:
-
Running as root — Containers that run as the root user (UID 0) grant attackers
full control of the container filesystem if a process is compromised. Without a
USERinstruction switching to a non-root user, the container process inherits root privileges by default. This violates CIS Docker Benchmark 4.1. -
Hardcoded secrets — Using
ENVorARGto pass passwords, API keys, or tokens embeds them in image layers. Anyone withdocker historyaccess can extract these values. Secrets should use Docker BuildKit's--mount=type=secretinstead. -
Unpinned base images — Using
FROM node:latestorFROM ubuntuwithout a specific tag or digest means your build is non-reproducible. A compromised upstream tag can inject malicious code into every build. Always pin images by digest or explicit version tag. -
Excessive capabilities — Instructions like
RUN chmod 777or mounting sensitive host paths grant unnecessary permissions. The analyzer flags overly permissive file operations that widen the attack surface. -
Exposed ports without justification —
EXPOSEinstructions for unnecessary ports increase the network attack surface. The analyzer checks for commonly dangerous ports (22 for SSH, 3306 for MySQL) that should not typically be exposed in production containers.
These issues are ranked by severity: critical (secrets exposure, running as root), high (unpinned images, excessive permissions), medium (missing healthchecks, deprecated syntax), and low (cosmetic issues, ordering suggestions). The analyzer provides actionable remediation guidance for each finding.
Linting Rules and Instruction Validation
Dockerfile linting validates that instructions follow the syntax rules defined in the Dockerfile reference and adhere to community best practices. The analyzer checks for:
-
Invalid or deprecated instructions —
MAINTAINERhas been deprecated since Docker 1.13 in favor of theLABEL maintainer="..."pattern. The analyzer flags deprecated instructions and suggests their modern replacements. -
Shell form vs exec form —
CMD npm start(shell form) wraps the command in/bin/sh -c, preventing proper signal propagation. The exec formCMD ["npm", "start"]receives signals directly, enabling graceful shutdown. Similarly,ENTRYPOINTshould use exec form to handle PID 1 correctly. -
Multiple CMD or ENTRYPOINT instructions — Only the last
CMDorENTRYPOINTin a Dockerfile takes effect. Multiple occurrences indicate confusion about instruction behavior and are flagged as likely bugs. -
Missing .dockerignore patterns — A
COPY . .without an appropriate.dockerignorebrings in node_modules, .git directories, test files, and potentially secrets. The analyzer warns when broad copy patterns are detected. -
Apt-get without cleanup — Running
RUN apt-get update && apt-get install -y packagewithoutrm -rf /var/lib/apt/lists/*in the same layer leaves cached package lists in the image, increasing size and potentially leaking repository metadata. -
Missing WORKDIR — Without an explicit
WORKDIR, file operations default to/(root directory), which mixes application files with system files and complicates debugging.
Each lint rule references a specific Hadolint rule ID (DL prefix for Dockerfile rules, SC prefix for ShellCheck rules applied to RUN instructions) when applicable, providing traceability to the community-standard rule set.
Multi-Stage Build Analysis
Multi-stage builds use multiple FROM instructions to separate build-time dependencies
from the final runtime image. The analyzer validates multi-stage patterns for correctness and
security:
-
Unused build stages — A named stage (
FROM node:20 AS builder) that is never referenced by a subsequentCOPY --from=builderindicates dead code that adds confusion without contributing to the final image. - Secrets leaking across stages — If a build stage installs credentials (SSH keys for private repos, npm tokens for private registries), these must not be copied to the final stage. The analyzer tracks which files are created in each stage and flags cross-stage copies of sensitive paths.
- Base image consistency — Using different OS families across stages (e.g., Alpine for build and Debian for runtime) can cause binary incompatibilities when copying compiled artifacts. The analyzer warns about potential ABI mismatches.
- Final stage minimality — The last stage should contain only runtime dependencies. The analyzer checks whether build tools (gcc, make, git) or package managers (npm with devDependencies, pip without --no-dev) are present in the final image.
Proper multi-stage analysis requires understanding the full Dockerfile as a directed graph of stages rather than a linear sequence of instructions. The analyzer builds this graph to detect issues that single-instruction linting would miss.
Base Image Risk Assessment
The choice of base image determines the initial security posture of every container. The analyzer evaluates base image references for known risks:
-
Tag mutability — Tags like
latest,stable, or major-version-only tags (node:20) point to different digests over time. A build that worked yesterday may produce a different image today. The analyzer recommends pinning to specific patch versions or SHA256 digests for reproducibility. -
Image provenance — Official images from Docker Hub
(
library/node,library/python) follow a review process. Third-party images without verified publisher status carry higher supply chain risk. The analyzer flags images from unverified sources. -
Distro-specific vulnerabilities — Full OS base images
(
ubuntu:22.04,debian:bookworm) include hundreds of packages with potential CVEs. Minimal alternatives like-alpine,-slim, or distroless images drastically reduce the vulnerability surface. - End-of-life images — Base images referencing EOL distributions (Ubuntu 18.04, Debian Stretch, Node.js 16) no longer receive security patches. The analyzer checks image tags against known EOL dates and warns when support has ended.
Base image analysis is one of the highest-value checks because it affects the entire dependency tree. A vulnerable base image makes every downstream layer vulnerable regardless of how carefully the application code is written.
Code Examples
Dockerfile with common security issues (analyzer detects these)
# ❌ Unpinned base image — non-reproducible builds
FROM node:latest
# ❌ Deprecated instruction
MAINTAINER dev@example.com
# ❌ Hardcoded secret in ENV — visible in image history
ENV DATABASE_PASSWORD=s3cr3t_p4ssw0rd
ARG NPM_TOKEN=abc123
# ❌ No WORKDIR — defaults to /
COPY . .
# ❌ apt-get without cleanup in same layer
RUN apt-get update
RUN apt-get install -y curl wget
# ❌ Overly permissive file permissions
RUN chmod 777 /app
# ❌ Exposing SSH port in production container
EXPOSE 22
EXPOSE 3000
# ❌ Shell form CMD — no signal propagation
CMD npm start
# ❌ No USER instruction — runs as rootSecure Dockerfile following best practices
# ✅ Pinned base image with specific version
FROM node:20.11-alpine AS builder
LABEL maintainer="team@example.com"
WORKDIR /app
# ✅ Copy package files first for better layer caching
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY src/ ./src/
COPY tsconfig.json ./
RUN npm run build
# ✅ Minimal runtime stage — no build tools
FROM node:20.11-alpine AS runtime
WORKDIR /app
# ✅ Non-root user for runtime
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# ✅ Only production dependencies
COPY package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts && \
rm -rf /root/.npm
# ✅ Copy only build artifacts from builder
COPY --from=builder /app/dist ./dist
# ✅ Health check for orchestrator integration
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
# ✅ Switch to non-root user
USER appuser
EXPOSE 3000
# ✅ Exec form for proper signal handling
CMD ["node", "dist/server.js"]Standards & Specifications
- Dockerfile Reference — Official Docker documentation defining all Dockerfile instructions, their syntax, and behavior
- Docker Security Best Practices — Official guide for writing secure and efficient Dockerfiles including multi-stage builds and least privilege
- CIS Docker Benchmark — Industry-standard security configuration benchmark for Docker containers and images
- Hadolint Rules Reference — Community-standard Dockerfile linter rules for detecting bad practices and security issues