--- 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/hadolint-action@v3.1.0 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)