container-hadolint
Dockerfile security linting and best practice validation using Hadolint with 100+ built-in rules aligned to CIS Docker Benchmark. Use when: (1) Analyzing Dockerfiles for security misconfigurations and anti-patterns, (2) Enforcing container image security best practices in CI/CD pipelines, (3) Detecting hardcoded secrets and credentials in container builds, (4) Validating compliance with CIS Docker Benchmark requirements, (5) Integrating shift-left container security into developer workflows, (6) Providing remediation guidance for insecure Dockerfile instructions.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install agentsecops-secopsagentkit-container-hadolint
Repository
Skill path: skills/devsecops/container-hadolint
Dockerfile security linting and best practice validation using Hadolint with 100+ built-in rules aligned to CIS Docker Benchmark. Use when: (1) Analyzing Dockerfiles for security misconfigurations and anti-patterns, (2) Enforcing container image security best practices in CI/CD pipelines, (3) Detecting hardcoded secrets and credentials in container builds, (4) Validating compliance with CIS Docker Benchmark requirements, (5) Integrating shift-left container security into developer workflows, (6) Providing remediation guidance for insecure Dockerfile instructions.
Open repositoryBest for
Primary workflow: Run DevOps.
Technical facets: DevOps, Security.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: AgentSecOps.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install container-hadolint into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/AgentSecOps/SecOpsAgentKit before adding container-hadolint to shared team environments
- Use container-hadolint for devsecops workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: container-hadolint
description: >
Dockerfile security linting and best practice validation using Hadolint with 100+ built-in
rules aligned to CIS Docker Benchmark. Use when: (1) Analyzing Dockerfiles for security
misconfigurations and anti-patterns, (2) Enforcing container image security best practices
in CI/CD pipelines, (3) Detecting hardcoded secrets and credentials in container builds,
(4) Validating compliance with CIS Docker Benchmark requirements, (5) Integrating shift-left
container security into developer workflows, (6) Providing remediation guidance for insecure
Dockerfile instructions.
version: 0.1.0
maintainer: SirAppSec
category: devsecops
tags: [docker, hadolint, dockerfile, container-security, cis-benchmark, linting, ci-cd]
frameworks: [CIS, OWASP]
dependencies:
tools: [hadolint, docker]
references:
- https://github.com/hadolint/hadolint
- https://www.cisecurity.org/benchmark/docker
- https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
---
# Dockerfile Security Linting with Hadolint
## Overview
Hadolint is a Dockerfile linter that validates container build files against security best practices and the CIS Docker Benchmark. It analyzes Dockerfile instructions to identify misconfigurations, anti-patterns, and security vulnerabilities before images are built and deployed.
Hadolint integrates ShellCheck to validate RUN instructions, ensuring shell commands follow security best practices. With 100+ built-in rules mapped to CIS Docker Benchmark controls, Hadolint provides comprehensive security validation for container images.
## Quick Start
### Install Hadolint
```bash
# macOS via Homebrew
brew install hadolint
# Linux via binary
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint
# Via Docker
docker pull hadolint/hadolint
```
### Scan Dockerfile
```bash
# Scan Dockerfile in current directory
hadolint Dockerfile
# Scan with specific Dockerfile path
hadolint path/to/Dockerfile
# Using Docker
docker run --rm -i hadolint/hadolint < Dockerfile
```
### Generate Report
```bash
# JSON output for automation
hadolint -f json Dockerfile > hadolint-report.json
# GitLab Code Quality format
hadolint -f gitlab_codeclimate Dockerfile > hadolint-codeclimate.json
# Checkstyle format for CI integration
hadolint -f checkstyle Dockerfile > hadolint-checkstyle.xml
```
## Core Workflows
### 1. Local Development Scanning
Validate Dockerfiles during development:
```bash
# Basic scan with colored output
hadolint Dockerfile
# Scan with specific severity threshold
hadolint --failure-threshold error Dockerfile
# Show only warnings and errors
hadolint --no-color --format tty Dockerfile | grep -E "^(warning|error)"
# Verbose output with rule IDs
hadolint -t style -t warning -t error Dockerfile
```
**Output Format:**
```
Dockerfile:3 DL3008 warning: Pin versions in apt get install
Dockerfile:7 DL3025 error: Use JSON notation for CMD and ENTRYPOINT
Dockerfile:12 DL3059 info: Multiple RUN instructions detected
```
**When to use**: Developer workstation, pre-commit validation, iterative Dockerfile development.
### 2. CI/CD Pipeline Integration
Automate Dockerfile validation in build pipelines:
#### GitHub Actions
```yaml
name: Hadolint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Hadolint Dockerfile
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
failure-threshold: warning
format: sarif
output-file: hadolint.sarif
- name: Upload SARIF to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: hadolint.sarif
```
#### GitLab CI
```yaml
hadolint:
image: hadolint/hadolint:latest-debian
stage: lint
script:
- hadolint -f gitlab_codeclimate Dockerfile > hadolint-report.json
artifacts:
reports:
codequality: hadolint-report.json
when: always
```
**When to use**: Automated security gates, pull request checks, deployment validation.
### 3. Configuration Customization
Create `.hadolint.yaml` to customize rules:
```yaml
# .hadolint.yaml
failure-threshold: warning
ignored:
- DL3008 # Allow unpinned apt-get packages (assess risk first)
- DL3059 # Allow multiple RUN instructions
trustedRegistries:
- docker.io/library # Official Docker Hub images
- gcr.io/distroless # Google distroless images
- registry.access.redhat.com # Red Hat registry
override:
error:
- DL3001 # Enforce: never use yum/dnf/zypper without version pins
warning:
- DL3015 # Warn: use --no-install-recommends with apt-get
info:
- DL3059 # Info: multiple RUN instructions reduce layer caching
label-schema:
maintainer: text
org.opencontainers.image.vendor: text
org.opencontainers.image.version: semver
```
Use bundled templates in `assets/`:
- `assets/hadolint-strict.yaml` - Strict security enforcement (CRITICAL/HIGH only)
- `assets/hadolint-balanced.yaml` - Balanced validation (recommended)
- `assets/hadolint-permissive.yaml` - Permissive for legacy Dockerfiles
**When to use**: Reducing false positives, organizational standards, legacy Dockerfile migration.
### 4. Security-Focused Validation
Enforce critical security rules:
```bash
# Only fail on security issues (error severity)
hadolint --failure-threshold error Dockerfile
# Check specific security rules
hadolint --trusted-registry docker.io/library Dockerfile
# Scan all Dockerfiles in project
find . -name "Dockerfile*" -exec hadolint {} \;
# Generate security report with only errors
hadolint -f json Dockerfile | jq '.[] | select(.level == "error")'
```
**Critical Security Rules:**
- **DL3000**: Use absolute WORKDIR (prevents directory traversal)
- **DL3001**: Always use version pinning for package managers
- **DL3002**: Never switch to root USER in Dockerfile
- **DL3020**: Use COPY instead of ADD (prevents arbitrary URL fetching)
- **DL3025**: Use JSON notation for CMD/ENTRYPOINT (prevents shell injection)
See `references/security_rules.md` for complete security rule catalog with CIS mappings.
### 5. Multi-Stage Build Validation
Scan complex multi-stage Dockerfiles:
```bash
# Validate all stages
hadolint Dockerfile
# Stage-specific validation (use custom script)
./scripts/hadolint_multistage.py Dockerfile
```
**Common Multi-Stage Issues:**
- Using same user across build and runtime stages
- Copying unnecessary build tools to production image
- Missing security hardening in final stage
- Secrets present in build stage propagating to runtime
**When to use**: Complex builds, security-hardened images, production containerization.
### 6. Pre-Commit Hook Integration
Prevent insecure Dockerfiles from being committed:
```bash
# Install pre-commit hook using bundled script
./scripts/install_precommit.sh
# Or manually create hook
cat << 'EOF' > .git/hooks/pre-commit
#!/bin/bash
for dockerfile in $(git diff --cached --name-only | grep -E 'Dockerfile'); do
hadolint --failure-threshold warning "$dockerfile" || exit 1
done
EOF
chmod +x .git/hooks/pre-commit
```
**When to use**: Developer workstations, team onboarding, mandatory security controls.
## Security Considerations
### Sensitive Data Handling
- **Secret Detection**: Hadolint flags hardcoded secrets in ENV, ARG, LABEL instructions
- **Build Secrets**: Use Docker BuildKit secrets (`RUN --mount=type=secret`) instead of ARG for credentials
- **Multi-Stage Security**: Ensure secrets in build stages don't leak to final image
- **Image Scanning**: Hadolint validates Dockerfile - combine with image scanning (Trivy, Grype) for runtime security
### Access Control
- **CI/CD Permissions**: Hadolint scans require read access to Dockerfile and build context
- **Report Storage**: Treat scan reports as internal documentation - may reveal security practices
- **Trusted Registries**: Configure `trustedRegistries` to enforce approved base image sources
### Audit Logging
Log the following for compliance and security auditing:
- Scan execution timestamps and Dockerfile paths
- Rule violations by severity (error, warning, info)
- Suppressed rules and justifications
- Base image registry validation results
- Remediation actions and timeline
### Compliance Requirements
- **CIS Docker Benchmark 1.6**: Hadolint rules map to CIS controls (see `references/cis_mapping.md`)
- 4.1: Create a user for the container (DL3002)
- 4.6: Add HEALTHCHECK instruction (DL3025)
- 4.7: Do not use update alone in Dockerfile (DL3009)
- 4.9: Use COPY instead of ADD (DL3020)
- **OWASP Docker Security**: Validates against OWASP container security best practices
- **NIST SP 800-190**: Application container security guidance
## Bundled Resources
### Scripts (`scripts/`)
- `hadolint_scan.py` - Comprehensive scanning with multiple Dockerfiles and output formats
- `hadolint_multistage.py` - Multi-stage Dockerfile analysis with stage-specific validation
- `install_precommit.sh` - Automated pre-commit hook installation
- `ci_integration.sh` - CI/CD integration examples for multiple platforms
### References (`references/`)
- `security_rules.md` - Complete Hadolint security rules with CIS Benchmark mappings
- `cis_mapping.md` - Detailed CIS Docker Benchmark control mapping
- `remediation_guide.md` - Rule-by-rule remediation guidance with secure examples
- `shellcheck_integration.md` - ShellCheck rules for RUN instruction validation
### Assets (`assets/`)
- `hadolint-strict.yaml` - Strict security configuration
- `hadolint-balanced.yaml` - Production-ready configuration (recommended)
- `hadolint-permissive.yaml` - Legacy Dockerfile migration configuration
- `github-actions.yml` - Complete GitHub Actions workflow
- `gitlab-ci.yml` - Complete GitLab CI pipeline
- `precommit-config.yaml` - Pre-commit framework configuration
## Common Patterns
### Pattern 1: Initial Dockerfile Security Audit
First-time security assessment:
```bash
# 1. Find all Dockerfiles
find . -type f -name "Dockerfile*" > dockerfile-list.txt
# 2. Scan all Dockerfiles with JSON output
mkdir -p security-reports
while read dockerfile; do
output_file="security-reports/$(echo $dockerfile | tr '/' '_').json"
hadolint -f json "$dockerfile" > "$output_file" 2>&1
done < dockerfile-list.txt
# 3. Generate summary report
./scripts/hadolint_scan.py --input-dir . --output summary-report.html
# 4. Review critical/high findings
cat security-reports/*.json | jq '.[] | select(.level == "error")' > critical-findings.json
```
### Pattern 2: Progressive Remediation
Gradual security hardening:
```bash
# Phase 1: Baseline (don't fail builds yet)
hadolint --failure-threshold none -f json Dockerfile > baseline.json
# Phase 2: Fix critical issues (fail on errors only)
hadolint --failure-threshold error Dockerfile
# Phase 3: Address warnings
hadolint --failure-threshold warning Dockerfile
# Phase 4: Full compliance (including style/info)
hadolint Dockerfile
```
### Pattern 3: Security-Hardened Production Image
Build security-first container image:
```dockerfile
# Example secure Dockerfile following Hadolint best practices
# Use specific base image version from trusted registry
FROM docker.io/library/node:18.19.0-alpine3.19
# Install packages with version pinning and cleanup
RUN apk add --no-cache \
dumb-init=1.2.5-r2 \
&& rm -rf /var/cache/apk/*
# Create non-root user
RUN addgroup -g 1001 -S appuser && \
adduser -S -u 1001 -G appuser appuser
# Set working directory
WORKDIR /app
# Copy application files (use COPY not ADD)
COPY --chown=appuser:appuser package*.json ./
COPY --chown=appuser:appuser . .
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force
# Switch to non-root user
USER appuser
# Expose port (document only, not security control)
EXPOSE 3000
# Add healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js || exit 1
# Use JSON notation for entrypoint/cmd
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "server.js"]
```
Validate with Hadolint:
```bash
hadolint Dockerfile # Should pass with no errors
```
### Pattern 4: CI/CD with Automated Remediation Suggestions
Provide actionable feedback in pull requests:
```bash
# In CI pipeline
hadolint -f json Dockerfile > hadolint.json
# Generate remediation suggestions
./scripts/hadolint_scan.py \
--input hadolint.json \
--format markdown \
--output pr-comment.md
# Post to PR comment (using gh CLI)
gh pr comment --body-file pr-comment.md
```
## Integration Points
### CI/CD Integration
- **GitHub Actions**: Native hadolint-action with SARIF support for Security tab
- **GitLab CI**: GitLab Code Quality format integration
- **Jenkins**: Checkstyle format for Jenkins Warnings plugin
- **CircleCI**: Docker-based executor with artifact retention
- **Azure Pipelines**: Task integration with results publishing
### Security Tools Ecosystem
- **Image Scanning**: Combine with Trivy, Grype, Clair for runtime vulnerability scanning
- **Secret Scanning**: Integrate with Gitleaks, TruffleHog for comprehensive secret detection
- **IaC Security**: Chain with Checkov for Kubernetes/Terraform validation
- **SBOM Generation**: Export findings alongside Syft/Trivy SBOM reports
- **Security Dashboards**: Export JSON to Grafana, Kibana, Datadog for centralized monitoring
### SDLC Integration
- **Development**: Pre-commit hooks provide immediate feedback
- **Code Review**: PR checks prevent insecure Dockerfiles from merging
- **Testing**: Scan test environment Dockerfiles
- **Staging**: Validation gate before production promotion
- **Production**: Periodic audits of deployed container configurations
## Troubleshooting
### Issue: Too Many False Positives
**Symptoms**: Legitimate patterns flagged (legacy Dockerfiles, specific use cases)
**Solution**:
```yaml
# Create .hadolint.yaml
ignored:
- DL3059 # Multiple RUN instructions (valid for complex builds)
# Or use inline ignores
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y curl
```
Consult `references/remediation_guide.md` for rule-specific guidance.
### Issue: Base Image Registry Not Trusted
**Symptoms**: Error about untrusted registry even for legitimate images
**Solution**:
```yaml
# Add to .hadolint.yaml
trustedRegistries:
- mycompany.azurecr.io
- gcr.io/my-project
- docker.io/library
```
### Issue: ShellCheck Warnings in RUN Instructions
**Symptoms**: SC2086, SC2046 warnings from ShellCheck integration
**Solution**:
```dockerfile
# Bad: Unquoted variables
RUN echo $MY_VAR > file.txt
# Good: Quoted variables
RUN echo "$MY_VAR" > file.txt
# Or disable specific ShellCheck rule
# hadolint ignore=DL4006
RUN echo $MY_VAR > file.txt
```
See `references/shellcheck_integration.md` for complete ShellCheck guidance.
### Issue: Multi-Stage Build Not Recognized
**Symptoms**: Errors about missing USER instruction despite proper multi-stage setup
**Solution**:
```dockerfile
# Ensure each stage has appropriate USER
FROM node:18 AS builder
# Build operations...
FROM node:18-alpine AS runtime
USER node # Add USER in final stage
CMD ["node", "app.js"]
```
### Issue: CI Pipeline Failing on Warnings
**Symptoms**: Build fails on low-severity issues
**Solution**:
```bash
# Adjust failure threshold in CI
hadolint --failure-threshold error Dockerfile
# Or configure per-environment
if [ "$CI_ENVIRONMENT" == "production" ]; then
hadolint --failure-threshold warning Dockerfile
else
hadolint --failure-threshold error Dockerfile
fi
```
## Advanced Configuration
### Custom Rule Severity Override
```yaml
# .hadolint.yaml
override:
error:
- DL3001 # Package versioning is critical
- DL3020 # COPY vs ADD is security-critical
warning:
- DL3059 # Multiple RUN is warning, not info
info:
- DL3008 # Downgrade apt-get pinning to info for dev images
```
### Inline Suppression
```dockerfile
# Suppress single rule for one instruction
# hadolint ignore=DL3018
RUN apk add --no-cache curl
# Suppress multiple rules
# hadolint ignore=DL3003,DL3009
WORKDIR /tmp
RUN apt-get update && apt-get install -y wget
# Global suppression (use sparingly)
# hadolint global ignore=DL3059
```
### Trusted Registry Enforcement
```yaml
# .hadolint.yaml
trustedRegistries:
- docker.io/library # Official images only
- gcr.io/distroless # Google distroless
- cgr.dev/chainguard # Chainguard images
# This will error on:
# FROM nginx:latest ❌ (docker.io/nginx)
# FROM docker.io/library/nginx:latest ✅ (trusted)
```
### Label Schema Validation
```yaml
# .hadolint.yaml
label-schema:
maintainer: text
org.opencontainers.image.created: rfc3339
org.opencontainers.image.version: semver
org.opencontainers.image.vendor: text
```
Ensures Dockerfile LABELs conform to OCI image specification.
## References
- [Hadolint GitHub Repository](https://github.com/hadolint/hadolint)
- [CIS Docker Benchmark](https://www.cisecurity.org/benchmark/docker)
- [Docker Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
- [ShellCheck Documentation](https://www.shellcheck.net/)
- [OCI Image Specification](https://github.com/opencontainers/image-spec)
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### assets/hadolint-strict.yaml
```yaml
# Hadolint Strict Configuration
# Enforces maximum security with minimal exceptions
# Use for: Production Dockerfiles, security-critical applications
failure-threshold: error
# Minimal ignores - only critical exceptions
ignored: []
# Only trust official and verified registries
trustedRegistries:
- docker.io/library # Official Docker Hub images
- gcr.io/distroless # Google distroless base images
- cgr.dev/chainguard # Chainguard minimal images
# Enforce strict severity levels
override:
error:
- DL3000 # Use absolute WORKDIR
- DL3001 # Version pinning for yum
- DL3002 # Never switch to root
- DL3003 # Use WORKDIR instead of cd
- DL3006 # Always tag images
- DL3008 # Version pinning for apt
- DL3013 # Version pinning for pip
- DL3016 # Version pinning for npm
- DL3018 # Version pinning for apk
- DL3020 # Use COPY instead of ADD
- DL3028 # Use build secrets for credentials
warning:
- DL3007 # Use specific digests (recommended)
- DL3009 # Delete apt cache
- DL3015 # Avoid additional packages
- DL3025 # Use JSON notation
# Enforce OCI image labels
label-schema:
maintainer: text
org.opencontainers.image.created: rfc3339
org.opencontainers.image.authors: text
org.opencontainers.image.url: url
org.opencontainers.image.documentation: url
org.opencontainers.image.source: url
org.opencontainers.image.version: semver
org.opencontainers.image.revision: text
org.opencontainers.image.vendor: text
org.opencontainers.image.title: text
org.opencontainers.image.description: text
```
### assets/hadolint-balanced.yaml
```yaml
# Hadolint Balanced Configuration
# Recommended for most production use cases
# Balances security enforcement with practical development needs
failure-threshold: warning
# Allow common development patterns that don't compromise security
ignored:
- DL3059 # Multiple RUN instructions (improves layer caching in development)
# Trusted registries - add your organization's registries
trustedRegistries:
- docker.io/library # Official Docker Hub images
- gcr.io/distroless # Google distroless images
- cgr.dev/chainguard # Chainguard images
# Add your private registries below:
# - mycompany.azurecr.io
# - gcr.io/my-project
# Balanced severity levels
override:
error:
- DL3002 # Never switch to root (critical security)
- DL3020 # Use COPY instead of ADD (prevent URL injection)
warning:
- DL3000 # Use absolute WORKDIR
- DL3001 # Version pinning for package managers
- DL3006 # Always tag images
- DL3008 # Version pinning for apt
- DL3013 # Version pinning for pip
- DL3025 # Use JSON notation for CMD/ENTRYPOINT
info:
- DL3007 # Use image digests (nice to have)
- DL3009 # Delete apt cache (optimization)
# Recommended OCI labels
label-schema:
maintainer: text
org.opencontainers.image.version: semver
org.opencontainers.image.vendor: text
```
### assets/hadolint-permissive.yaml
```yaml
# Hadolint Permissive Configuration
# For legacy Dockerfiles during migration or development environments
# Use temporarily while remediating existing issues
failure-threshold: error # Only fail on critical security issues
# Ignore common legacy patterns (review and remove as you fix them)
ignored:
- DL3006 # Image versioning (fix gradually)
- DL3008 # apt-get version pinning (fix gradually)
- DL3009 # apt cache cleanup (optimization, not security)
- DL3013 # pip version pinning (fix gradually)
- DL3015 # apt --no-install-recommends (optimization)
- DL3059 # Multiple RUN instructions (caching)
# Still enforce trusted registries
trustedRegistries:
- docker.io
- gcr.io
- ghcr.io
# Add your registries
# Minimal enforcement - only critical security issues
override:
error:
- DL3002 # Never switch to root (always enforce)
- DL3020 # Use COPY instead of ADD (security critical)
warning:
- DL3001 # Package manager version pinning
- DL3025 # JSON notation for CMD/ENTRYPOINT
info:
# Everything else is informational
- DL3000
- DL3003
- DL3007
```
### references/security_rules.md
```markdown
# Hadolint Security Rules Reference
Complete reference of Hadolint security rules with CIS Docker Benchmark mappings and remediation guidance.
## Table of Contents
- [Critical Security Rules](#critical-security-rules)
- [CIS Docker Benchmark Mappings](#cis-docker-benchmark-mappings)
- [Rule Categories](#rule-categories)
## 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**:
```dockerfile
WORKDIR app
```
**Good**:
```dockerfile
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**:
```dockerfile
RUN yum install httpd
```
**Good**:
```dockerfile
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**:
```dockerfile
USER node
RUN npm install
USER root # ❌ Don't switch back to root
```
**Good**:
```dockerfile
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**:
```dockerfile
RUN cd /app && npm install
```
**Good**:
```dockerfile
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**:
```dockerfile
FROM node
FROM ubuntu:latest
```
**Good**:
```dockerfile
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**:
```dockerfile
FROM node:18.19.0-alpine3.19
```
**Better**:
```dockerfile
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**:
```dockerfile
RUN apt-get update && apt-get install -y curl
```
**Good**:
```dockerfile
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**:
```dockerfile
RUN apt-get update && apt-get install -y curl
```
**Good**:
```dockerfile
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**:
```dockerfile
RUN pip install flask
```
**Good**:
```dockerfile
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**:
```dockerfile
ADD app.tar.gz /app/
ADD https://example.com/file.txt /tmp/
```
**Good**:
```dockerfile
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**:
```dockerfile
CMD node server.js
ENTRYPOINT /app/start.sh
```
**Good**:
```dockerfile
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**:
```dockerfile
ARG API_KEY=secret123
RUN curl -H "Authorization: $API_KEY" https://api.example.com
```
**Good** (BuildKit secrets):
```dockerfile
# 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**:
```dockerfile
RUN apt-get update
RUN apt-get install -y curl
RUN curl -O https://example.com/file
```
**Better**:
```dockerfile
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**:
```dockerfile
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**:
```dockerfile
# 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**:
```dockerfile
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**:
```dockerfile
# 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 |
---
## Additional Resources
- [Hadolint Rules Wiki](https://github.com/hadolint/hadolint/wiki)
- [CIS Docker Benchmark v1.6](https://www.cisecurity.org/benchmark/docker)
- [Docker Security Best Practices](https://docs.docker.com/develop/security-best-practices/)
- [NIST SP 800-190](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-190.pdf)
```