7 min to read
Dockerfile Security Best Practices and Hadolint Usage Guide
Implementing security best practices in your Dockerfiles

Overview
Today, we’ll explore Dockerfile security best practices and learn how to use Hadolint for identifying potential security issues.
Dockerfile Security Best Practices
Let’s start with a basic Dockerfile
Key Security Practices
- Ensure stability and security
- Avoid using unofficial or custom base images
2. Minimize Image Layers
- Reduce image size and build time
- Avoid unnecessary layers
3. Remove Unnecessary Packages
- Reduce attack surface
- Avoid vulnerabilities
4. Use Non-Root Users
- Limit container privileges
- Prevent privilege escalation
5. Scan for Vulnerabilities
- Regular security scanning
- Address known CVEs
6. Use Multi-Stage Builds
- Separate build and runtime environments
- Reduce final image size
7. Pin Software Versions
- Ensure reproducibility
- Avoid breaking changes
8. Implement Resource Limits
- Prevent resource abuse
- Avoid DoS attacks
9. Use Image Signing
- Ensure image authenticity
- Avoid tampering
10. Implement Health Checks
- Monitor container health
- Automatic recovery
Secure Version:
Hadolint Introduction
Hadolint is a Dockerfile linter that helps build better Docker images by following best practices and avoiding common mistakes.
How Hadolint Works
- Hadolint reads Dockerfile.
- We parse Dockerfile as an abstract syntax tree (AST) to identify each instruction and argument associated with it.
- Each command is then checked against a predefined set of rules covering security, efficiency, and code quality. The rules are part of the Hadolint source code. You can check the list of rules here.
- All rule violations are marked by Hadolint and generate feedback on all detected problems.
Installation:
# Linux and macOS
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint
# macOS with Homebrew
brew install hadolint
# Windows (PowerShell)
Invoke-WebRequest -Uri https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Windows-x86_64.exe -OutFile 'hadolint.exe'
# Docker
docker pull hadolint/hadolint
docker run --rm -i hadolint/hadolint < Dockerfile
Basic Usage:
Common Issues and Fixes:
1. DL3007: Using ‘latest’ tag Using the “latest” tag can cause problems with image version management. Use an explicit version tag instead.
- Bad:
FROM ubuntu:latest
- Good:
FROM ubuntu:20.04
2. DL4000: Deprecated MAINTAINER The MAINTAINER directive is deprecated. Use LABEL instead.
- Bad:
MAINTAINER somaz@gmail.com
- Good:
LABEL maintainer="somaz@gmail.com"
3. DL3009: Delete apt-get lists Image size increases if you don’t clean up cache files after apt-get update. Always clean up /var/lib/apt/lists.
- Bad:
RUN apt-get update && apt-get install nginx
- Good:
RUN apt-get update && apt-get install nginx && rm -rf /var/lib/apt/lists/*
4. DL3015: Using apt-get without –no-install-recommends Automatic installation of recommended packages unnecessarily increases image size. Always use the –no-install-recommends option.
- Bad:
RUN apt-get install -y nginx
- Good:
RUN apt-get install -y --no-install-recommends nginx
5. DL3025: Using COPY with more than 2 arguments without WORKDIR Using WORKDIR is clearer and safer when copying multiple files.
- Bad:
COPY file1 file2 /somedir/
- Good:
WORKDIR /somedir/
followed byCOPY file1 file2 ./
Advanced Hadolint Usage
1. Customizing Rules with Configuration Files
You can create a .hadolint.yaml
file to customize rules:
# .hadolint.yaml
ignored:
- DL3008 # Pin versions in apt get install
- DL3013 # Pin versions in pip install
trustedRegistries:
- docker.io
- gcr.io
override:
error:
- DL3003 # Use WORKDIR instead of RUN cd
- DL4000 # MAINTAINER is deprecated
warning:
- DL3015 # Use --no-install-recommends
info:
- DL3059 # Multiple consecutive RUN instructions
style:
- DL3001 # For pipe-chain use wget -O - | tar ..
2. Ignoring Rules Inline
Add inline comments to ignore specific rules:
FROM ubuntu:18.04
# hadolint ignore=DL3009
RUN apt-get update && apt-get install -y python
# Multiple ignores
# hadolint ignore=DL3008,DL3015
RUN apt-get install -y nginx
3. Generating JSON/XML Reports
# Generate JSON report
hadolint --format json Dockerfile > hadolint-report.json
# Generate JUnit XML report (useful for CI/CD)
hadolint --format sarif Dockerfile > hadolint-report.sarif
Integrating Hadolint with CI/CD
GitHub Actions
# .github/workflows/docker-lint.yml
name: Dockerfile Lint
on:
push:
paths:
- '**/Dockerfile*'
pull_request:
paths:
- '**/Dockerfile*'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Lint Dockerfile
uses: hadolint/hadolint-action@v2.0.0
with:
dockerfile: Dockerfile
format: sarif
output-file: hadolint-results.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1
if: always()
with:
sarif_file: hadolint-results.sarif
GitLab CI
# .gitlab-ci.yml
stages:
- lint
lint-dockerfile:
stage: lint
image: hadolint/hadolint:latest-debian
script:
- hadolint Dockerfile || exit 1
Jenkins Pipeline
// Jenkinsfile
pipeline {
agent any
stages {
stage('Lint Dockerfile') {
steps {
sh 'docker run --rm -i hadolint/hadolint < Dockerfile'
}
}
}
}
Practical Example
Let’s analyze a Node.js application Dockerfile:
Key Improvements:
- Pinned package versions
- Consolidated RUN instructions
- Used
--no-install-recommends
- Cleaned up package lists
- Multi-stage build for smaller final image
Real-World Examples
1. Python Application with Security Best Practices
2. Improved Node.js Example with Security Focus
Integrating with Container Scanning
To enhance your Docker security, combine Hadolint with container scanning tools:
GitHub Actions example with both Hadolint and Trivy:
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Lint Dockerfile
uses: hadolint/hadolint-action@v2.0.0
with:
dockerfile: Dockerfile
- name: Build Docker image
run: docker build -t my-app:$ .
- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:$'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
Troubleshooting Common Hadolint Issues
Common Problems and Solutions:
1. Too Many False Positives: - Use configuration file to ignore specific rules
- Use inline comments for exceptions
- Focus on critical and high severity issues first
2. Hadolint Doesn't Recognize Custom Base Images: - Add trusted registries in configuration file
- Use full image references with digests
3. CI Pipeline Failing Due to Hadolint: - Start with warnings instead of errors
- Gradually fix issues and increase strictness
- Use separate linting stage before building
Comments