9.2 KiB
Hadolint Security Rules Reference
Complete reference of Hadolint security rules with CIS Docker Benchmark mappings and remediation guidance.
Table of Contents
Critical Security Rules
DL3000: Use absolute WORKDIR
Severity: Error CIS Mapping: 4.10 - Ensure secrets are not stored in Dockerfiles
Issue: Relative WORKDIR can lead to path confusion and security vulnerabilities.
Bad:
WORKDIR app
Good:
WORKDIR /app
DL3001: Version pinning for package managers
Severity: Warning CIS Mapping: 4.3 - Do not install unnecessary packages
Issue: Unpinned versions lead to non-reproducible builds and potential security vulnerabilities from package updates.
Bad:
RUN yum install httpd
Good:
RUN yum install -y httpd-2.4.51
DL3002: Never switch back to root
Severity: Error CIS Mapping: 4.1 - Create a user for the container
Issue: Switching back to root defeats container isolation and violates least privilege principle.
Bad:
USER node
RUN npm install
USER root # ❌ Don't switch back to root
Good:
USER node
RUN npm install
# Stay as non-root user
DL3003: Use WORKDIR instead of cd
Severity: Warning CIS Mapping: Best practices
Issue: Using cd in RUN commands doesn't persist across instructions and can cause confusion.
Bad:
RUN cd /app && npm install
Good:
WORKDIR /app
RUN npm install
DL3006: Always tag image versions
Severity: Warning CIS Mapping: 4.3 - Ensure base images are verified
Issue: Using :latest or no tag creates non-reproducible builds and security risks.
Bad:
FROM node
FROM ubuntu:latest
Good:
FROM node:18.19.0-alpine3.19
FROM ubuntu:22.04
DL3007: Pin Docker image versions to specific digest
Severity: Info CIS Mapping: 4.3 - Ensure base images are verified
Issue: Tags can be overwritten; digests are immutable.
Good:
FROM node:18.19.0-alpine3.19
Better:
FROM node:18.19.0-alpine3.19@sha256:abc123...
DL3008: Pin apt-get package versions
Severity: Warning CIS Mapping: 4.3 - Do not install unnecessary packages
Issue: Unpinned apt packages lead to non-reproducible builds.
Bad:
RUN apt-get update && apt-get install -y curl
Good:
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl=7.68.0-1ubuntu2.14 && \
rm -rf /var/lib/apt/lists/*
DL3009: Delete apt cache after installation
Severity: Info CIS Mapping: 4.6 - Reduce image size
Issue: Unnecessary cache increases image size and attack surface.
Bad:
RUN apt-get update && apt-get install -y curl
Good:
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
DL3013: Pin pip package versions
Severity: Warning CIS Mapping: 4.3 - Do not install unnecessary packages
Issue: Unpinned pip packages compromise build reproducibility.
Bad:
RUN pip install flask
Good:
RUN pip install --no-cache-dir flask==2.3.2
DL3020: Use COPY instead of ADD
Severity: Error CIS Mapping: 4.9 - Use COPY instead of ADD
Issue: ADD has implicit behavior (auto-extraction, URL support) that can be exploited.
Bad:
ADD app.tar.gz /app/
ADD https://example.com/file.txt /tmp/
Good:
COPY app.tar.gz /app/
# For URLs, use RUN wget/curl instead
RUN curl -O https://example.com/file.txt
Exception: ADD is acceptable only when you explicitly need tar auto-extraction.
DL3025: Use JSON notation for CMD and ENTRYPOINT
Severity: Warning CIS Mapping: 4.6 - Add HEALTHCHECK instruction
Issue: Shell form enables shell injection attacks and doesn't properly handle signals.
Bad:
CMD node server.js
ENTRYPOINT /app/start.sh
Good:
CMD ["node", "server.js"]
ENTRYPOINT ["/app/start.sh"]
DL3028: Use credentials via build secrets
Severity: Warning CIS Mapping: 4.10 - Do not store secrets in Dockerfiles
Issue: Credentials in ENV or ARG end up in image layers.
Bad:
ARG API_KEY=secret123
RUN curl -H "Authorization: $API_KEY" https://api.example.com
Good (BuildKit secrets):
# syntax=docker/dockerfile:1.4
RUN --mount=type=secret,id=api_key \
curl -H "Authorization: $(cat /run/secrets/api_key)" https://api.example.com
DL3059: Multiple RUN instructions
Severity: Info CIS Mapping: 4.6 - Optimize layers
Issue: Multiple RUN instructions create unnecessary layers, increasing image size.
Less Optimal:
RUN apt-get update
RUN apt-get install -y curl
RUN curl -O https://example.com/file
Better:
RUN apt-get update && \
apt-get install -y curl && \
curl -O https://example.com/file && \
rm -rf /var/lib/apt/lists/*
Note: Balance between layer caching and image size. For development, separate RUN instructions may aid caching.
CIS Docker Benchmark Mappings
CIS 4.1: Create a user for the container
Hadolint Rules: DL3002
Requirement: Don't run containers as root.
Implementation:
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# Don't switch back to root
CIS 4.3: Do not install unnecessary packages
Hadolint Rules: DL3001, DL3008, DL3013, DL3015
Requirement: Minimize attack surface by installing only required packages with pinned versions.
Implementation:
# Use --no-install-recommends
RUN apt-get update && \
apt-get install -y --no-install-recommends \
package1=version1 \
package2=version2 && \
rm -rf /var/lib/apt/lists/*
CIS 4.6: Add HEALTHCHECK instruction
Hadolint Rules: DL3025 (related to proper CMD/ENTRYPOINT)
Requirement: Include HEALTHCHECK to enable container health monitoring.
Implementation:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CIS 4.7: Do not use update instructions alone
Hadolint Rules: DL3009, DL3014, DL3015
Requirement: Update and install should be in same RUN instruction to prevent cache issues.
Implementation:
# Bad
RUN apt-get update
RUN apt-get install -y package
# Good
RUN apt-get update && \
apt-get install -y package && \
rm -rf /var/lib/apt/lists/*
CIS 4.9: Use COPY instead of ADD
Hadolint Rules: DL3020
Requirement: Use COPY for file operations; ADD only for tar extraction.
Implementation: See DL3020 above.
CIS 4.10: Do not store secrets in Dockerfiles
Hadolint Rules: DL3028, DL3000 (indirectly)
Requirement: Use build secrets or external secret management.
Implementation: See DL3028 above.
Rule Categories
Base Image Security
- DL3006: Always tag image versions
- DL3007: Use specific image digests
- DL3026: Use trusted registries only
Package Management
- DL3001: Version pinning (yum/dnf/zypper)
- DL3008: Version pinning (apt-get)
- DL3013: Version pinning (pip)
- DL3016: Version pinning (npm)
- DL3018: Version pinning (apk)
- DL3028: Use build secrets for credentials
Instruction Best Practices
- DL3000: Use absolute WORKDIR
- DL3003: Use WORKDIR instead of cd
- DL3020: Use COPY instead of ADD
- DL3025: Use JSON notation for CMD/ENTRYPOINT
User and Permissions
- DL3002: Never switch back to root
- DL4001: Use SHELL to switch shells securely
Image Optimization
- DL3009: Delete apt cache
- DL3014: Use -y for apt-get
- DL3015: Avoid additional packages
- DL3059: Minimize RUN instructions
ShellCheck Integration
- DL4000-DL4006: Shell script best practices in RUN
Quick Reference Table
| Rule | Severity | CIS | Description |
|---|---|---|---|
| DL3000 | Error | 4.10 | Use absolute WORKDIR |
| DL3001 | Warning | 4.3 | Pin yum versions |
| DL3002 | Error | 4.1 | Don't switch to root |
| DL3003 | Warning | - | Use WORKDIR not cd |
| DL3006 | Warning | 4.3 | Tag image versions |
| DL3007 | Info | 4.3 | Use image digests |
| DL3008 | Warning | 4.3 | Pin apt versions |
| DL3009 | Info | 4.7 | Delete apt cache |
| DL3013 | Warning | 4.3 | Pin pip versions |
| DL3020 | Error | 4.9 | Use COPY not ADD |
| DL3025 | Warning | 4.6 | JSON notation CMD |
| DL3028 | Warning | 4.10 | Use build secrets |
| DL3059 | Info | - | Multiple RUN instructions |