Lint and auto-fix Dockerfiles against Docker hardening best practices
Project description
🐳 dockerfile-hardener
Lint and auto-fix Dockerfiles against Docker hardening best practices.
dockerfile-hardener analyses Dockerfiles for security misconfigurations, applies auto-fixes where possible, and produces reports in four formats, including SARIF for native GitHub Code Scanning integration. It covers the hardening principles documented by Docker's own Hardened Images specification.
Unlike Hadolint, Trivy, and Dockle which detect issues but do not remediate dockerfile-hardener can automatically fix a subset of findings in-place.
Features
-
10 hardening rules covering root user, latest tags, secrets, shell tools, multi-stage builds, apt hygiene, ADD vs COPY, port exposure, file ownership, and healthchecks.
-
Auto-fix mode applies safe, targeted fixes and writes a
.bakbackup first. -
4 output formats Rich terminal table, JSON, SARIF (GitHub Code Scanning), HTML dashboard.
-
Configurable severity threshold fail CI only on findings above
--fail-on. -
GitHub Action drop-in integration for any repository.
-
Docker image run without installing Python.
Installation
pip install dockerfile-hardener
Or run via Docker:
docker run --rm -v "$(pwd)":/work ghcr.io/macbuildssys/dockerfile-hardener lint Dockerfile
Quick Start
# Lint with terminal output
dockerfile-hardener lint Dockerfile
# Generate an HTML report
dockerfile-hardener lint Dockerfile --format html -o report.html
# Generate a SARIF report (GitHub Code Scanning compatible)
dockerfile-hardener lint Dockerfile --format sarif -o results.sarif
# Auto-fix fixable findings (dry-run preview first)
dockerfile-hardener fix Dockerfile --dry-run
dockerfile-hardener fix Dockerfile
# List all rules
dockerfile-hardener rules
CLI Reference
lint
dockerfile-hardener lint [OPTIONS] [DOCKERFILE]
Options:
-f, --format [table|json|sarif|html] Output format (default: table)
-o, --output FILE Write output to FILE instead of stdout
-F, --fail-on SEVERITY Exit 1 on findings >= SEVERITY (default: LOW)
-r, --rule RULE_ID Run only specified rule(s) (repeatable)
-i, --ignore RULE_ID Skip specified rule(s) (repeatable)
-V, --version Show version and exit
Exit codes:
-
0— no findings at or above--fail-onseverity. -
1— one or more findings at or above--fail-onseverity. -
2— file not found or parse error.
fix
dockerfile-hardener fix [OPTIONS] [DOCKERFILE]
Options:
-n, --dry-run Show changes without writing to disk
--no-backup Skip writing a .bak backup before modifying
-r, --rule RULE_ID Fix only specified rule(s) (repeatable)
rules
dockerfile-hardener rules [OPTIONS]
Options:
-f, --format [table|json] Output format (default: table)
Rules
| ID | Name | Severity | Auto-fix | Description |
|---|---|---|---|---|
| DH001 | RootUser | 🔴 CRITICAL | ✔ | Final stage must set a non-root USER |
| DH002 | LatestTag | 🟠 HIGH | ✗ | FROM must pin an explicit tag or digest |
| DH003 | ShellTools | 🟠 HIGH | ✗ | Shell/debug/network tools in final stage |
| DH004 | MultiStage | 🟡 MEDIUM | ✗ | Build toolchains must use multi-stage builds |
| DH005 | NoHealthcheck | 🔵 LOW | ✔ | Every image should define a HEALTHCHECK |
| DH006 | SecretsInEnv | 🔴 CRITICAL | ✗ | Secrets/tokens must not be hardcoded in ENV/ARG |
| DH007 | AddInstruction | 🟡 MEDIUM | ✔ | Use COPY instead of ADD for local files |
| DH008 | AptHygiene | 🟡 MEDIUM | ✔ | apt/apk: clean cache, --no-install-recommends, pin versions |
| DH009 | PrivilegedPort | 🔵 LOW | ✗ | EXPOSE should not use privileged ports (< 1024) |
| DH010 | FileOwnership | ⚪ INFO | ✔ | COPY/ADD should use --chown when running as non-root |
Rule tags
Rules are tagged with compliance references where applicable:
-
CIS-DI-XXXXCIS Docker Benchmark -
OWASP-AXXOWASP Top 10 -
Docker-HardeningDocker Hardened Images specification -
Supply-ChainSoftware supply chain security -
SecretsSecrets management
GitHub Action
Add to any workflow:
- name: Harden Dockerfile
uses: macbuildssys/dockerfile-hardener@v1
with:
dockerfile: Dockerfile # default
fail-on: HIGH # default
format: sarif # default
upload-sarif: "true" # uploads to GitHub Security tab
Full example:
name: Security
on: [push, pull_request]
jobs:
dockerfile-hardening:
runs-on: ubuntu-latest
permissions:
security-events: write # required for SARIF upload
steps:
- uses: actions/checkout@v4
- name: Lint Dockerfile
uses: macbuildssys/dockerfile-hardener@v1
with:
dockerfile: Dockerfile
fail-on: MEDIUM
ignore: "DH009" # ignore privileged port rule
Outputs:
| Output | Description |
|---|---|
score |
Hardening score (0–100) |
grade |
Grade letter (A–F) |
findings |
Total number of findings |
Scoring
Each finding deducts points from a base score of 100:
| Severity | Deduction per finding |
|---|---|
| CRITICAL | 25 |
| HIGH | 15 |
| MEDIUM | 8 |
| LOW | 3 |
| INFO | 0 |
Scores map to grades: A (≥90), B (≥75), C (≥60), D (≥40), F (<40).
Comparison with other tools
| Feature | dockerfile-hardener | Hadolint | Dockle | Trivy |
|---|---|---|---|---|
| Dockerfile linting | ✔ | ✔ | ✔ | ✔ |
| Auto-fix | ✔ | ✗ | ✗ | ✗ |
| SARIF output | ✔ | ✔ | ✗ | ✔ |
| HTML report | ✔ | ✗ | ✗ | ✔ |
| Scoring / grading | ✔ | ✗ | ✗ | ✗ |
| GitHub Action | ✔ | ✔ | ✗ | ✔ |
| Python / pip | ✔ | ✗ | ✗ | ✗ |
Development
git clone https://github.com/macbuildssys/dockerfile-hardener
cd dockerfile-hardener
pip install -e ".[dev]"
pytest
Adding a new rule
-
Create
dockerfile_hardener/rules/my_rule.pysubclassingBaseRule. -
Set
RULE_ID,NAME,SEVERITY,TAGS,DESCRIPTION. -
Implement
check(self, dockerfile) -> RuleResult. -
Import and append to
ALL_RULESindockerfile_hardener/rules/__init__.py. -
Add tests in
tests/test_rules.py.
from dockerfile_hardener.rules.base import BaseRule, RuleResult, Severity
class MyRule(BaseRule):
RULE_ID = "DH011"
NAME = "MyRule"
SEVERITY = Severity.MEDIUM
TAGS = ["Docker-Hardening"]
DESCRIPTION = "Short description of what this rule checks."
def check(self, dockerfile) -> RuleResult:
findings = []
for instr in dockerfile.by_command("RUN"):
if "something_bad" in instr.value:
findings.append(self._finding(
message="Explanation of the issue.",
line_number=instr.line_number,
fix_description="How to fix it.",
context=instr.value[:80],
))
return self._make_result(findings)
License
Distributed under the MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dockerfile_hardener-1.0.2.tar.gz.
File metadata
- Download URL: dockerfile_hardener-1.0.2.tar.gz
- Upload date:
- Size: 37.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e18370f8cf081ed0dd3ab67859d2055f9f473fbd4d642869119af5092fc75e55
|
|
| MD5 |
3291d7033849ba29b4af652eeffc02e5
|
|
| BLAKE2b-256 |
6fff8ed581411d17b76a59b24607826c2b25cb85c58827cc4e58fb2c300ae9c6
|
Provenance
The following attestation bundles were made for dockerfile_hardener-1.0.2.tar.gz:
Publisher:
publish.yml on macbuildssys/dockerfile-hardener
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dockerfile_hardener-1.0.2.tar.gz -
Subject digest:
e18370f8cf081ed0dd3ab67859d2055f9f473fbd4d642869119af5092fc75e55 - Sigstore transparency entry: 1154780248
- Sigstore integration time:
-
Permalink:
macbuildssys/dockerfile-hardener@76ac4e74c053a687fd94c77594fe66672dd61bcb -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/macbuildssys
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@76ac4e74c053a687fd94c77594fe66672dd61bcb -
Trigger Event:
push
-
Statement type:
File details
Details for the file dockerfile_hardener-1.0.2-py3-none-any.whl.
File metadata
- Download URL: dockerfile_hardener-1.0.2-py3-none-any.whl
- Upload date:
- Size: 38.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a830f52b831ace29126dad4860d8e4f138faac6580bd9c205ec57e4b604e5c9
|
|
| MD5 |
9d4879511c3496db1a169434f7b53b62
|
|
| BLAKE2b-256 |
af867d1d65b656fdcc2ee49ce470effdfbee96895e14cfd2dcf80f50ed38a674
|
Provenance
The following attestation bundles were made for dockerfile_hardener-1.0.2-py3-none-any.whl:
Publisher:
publish.yml on macbuildssys/dockerfile-hardener
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dockerfile_hardener-1.0.2-py3-none-any.whl -
Subject digest:
2a830f52b831ace29126dad4860d8e4f138faac6580bd9c205ec57e4b604e5c9 - Sigstore transparency entry: 1154780255
- Sigstore integration time:
-
Permalink:
macbuildssys/dockerfile-hardener@76ac4e74c053a687fd94c77594fe66672dd61bcb -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/macbuildssys
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@76ac4e74c053a687fd94c77594fe66672dd61bcb -
Trigger Event:
push
-
Statement type: