A fast, rule-based security scanner for Dockerfiles
Detect misconfigurations, exposed credentials, and security anti-patterns before they reach production.
Quick Start • Installation • GitHub Action • Usage • Rules • Custom Rules • Contributing
- Why dockerfile-sec?
- Features
- Quick Start
- Installation
- Using as GitHub Action
- Usage
- Configuration
- Built-in Rules
- Creating Custom Rules
- CLI Reference
- Contributing
- Security
- License
- Acknowledgments
Dockerfiles can contain security issues that are easy to miss during code reviews:
- Hardcoded credentials - Passwords, API keys, and tokens accidentally committed
- Running as root - Missing
USERdirective leads to container privilege escalation - Insecure base images - Using
latesttag or images without SHA256 verification - Exposed secrets in build args - Sensitive data passed via
ARGinstead of secrets - Recursive copies -
COPY . .accidentally including.envfiles and credentials
dockerfile-sec catches these issues automatically, integrating seamlessly into your development workflow and CI/CD pipelines.
| Feature | Description |
|---|---|
| 35 Built-in Rules | Comprehensive coverage of security best practices and credential detection |
| Blazing Fast | Written in Go for maximum performance on large codebases |
| Flexible Output | ASCII tables for humans, JSON for machines and automation |
| CI/CD Ready | Exit codes and quiet mode for seamless pipeline integration |
| Extensible | Load custom rules from local files or remote URLs |
| Zero Dependencies | Single static binary, no runtime required |
| Docker Support | Available as a minimal container image |
| Cross-Platform | Linux, macOS, and Windows support (amd64/arm64) |
# Download the binary (Linux/amd64)
curl -L https://github.com/cr0hn/dockerfile-security/releases/latest/download/dockerfile-sec-linux-amd64 -o dockerfile-sec
chmod +x dockerfile-sec
# Scan a Dockerfile
./dockerfile-sec Dockerfile
# Scan with exit code for CI/CD (exits 1 if issues found)
./dockerfile-sec -E DockerfileExample output:
+----------+-------------------------------------------+----------+
| Rule Id | Description | Severity |
+----------+-------------------------------------------+----------+
| core-002 | Posible text plain password in dockerfile | High |
| core-003 | Recursive copy found | Medium |
| core-005 | Use image tag instead of SHA256 hash | Medium |
| cred-001 | Generic credential | Medium |
+----------+-------------------------------------------+----------+
Download the latest release for your platform from the releases page.
| Platform | Architecture | Download |
|---|---|---|
| Linux | amd64 | dockerfile-sec-linux-amd64 |
| Linux | arm64 | dockerfile-sec-linux-arm64 |
| macOS | amd64 | dockerfile-sec-darwin-amd64 |
| macOS | arm64 (M1/M2) | dockerfile-sec-darwin-arm64 |
| Windows | amd64 | dockerfile-sec-windows-amd64.exe |
# Using GitHub Container Registry
docker pull ghcr.io/cr0hn/dockerfile-security:latest
# Scan a Dockerfile via stdin
cat Dockerfile | docker run --rm -i ghcr.io/cr0hn/dockerfile-security
# Scan a local file (mount as volume)
docker run --rm -v $(pwd):/app ghcr.io/cr0hn/dockerfile-security /app/DockerfileRequires Go 1.22 or later.
# Install directly
go install github.com/cr0hn/dockerfile-security/cmd/dockerfile-sec@latest
# Or build manually
git clone https://github.com/cr0hn/dockerfile-security.git
cd dockerfile-security
make buildYou can use dockerfile-sec directly in your GitHub Actions workflows without manual installation.
name: Dockerfile Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan Dockerfile
uses: cr0hn/dockerfile-security@v0.2.0
with:
dockerfile: 'Dockerfile'name: Advanced Dockerfile Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan with custom settings
id: scan
uses: cr0hn/dockerfile-security@v0.2.0
with:
dockerfile: 'docker/Dockerfile.prod'
categories: 'core,credentials,security'
ignore-rules: 'core-001'
output-file: 'security-results.json'
fail-on-issues: true
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: security-scan-results
path: security-results.json
- name: Comment on PR
if: github.event_name == 'pull_request' && failure()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('security-results.json', 'utf8'));
const comment = `## 🔒 Dockerfile Security Scan Results\n\n` +
`Found ${results.length} security issues:\n\n` +
results.map(i => `- **${i.id}**: ${i.description} (${i.severity})`).join('\n');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});| Input | Description | Required | Default |
|---|---|---|---|
dockerfile |
Path to Dockerfile to analyze | No | Dockerfile |
categories |
Rule categories (comma-separated): all, core, credentials, security, packages, configuration |
No | all |
ignore-rules |
Comma-separated rule IDs to ignore | No | '' |
ignore-file |
Path to ignore file | No | '' |
custom-rules |
Path to custom rules YAML file or URL | No | '' |
output-format |
Output format: table, json |
No | table |
output-file |
Path to save JSON output | No | '' |
fail-on-issues |
Exit with code 1 if issues found | No | true |
quiet |
Quiet mode (suppress output) | No | false |
version |
Version of dockerfile-sec to use | No | latest |
| Output | Description |
|---|---|
issues-found |
Number of security issues found |
exit-code |
Exit code of the scan (0=success, 1=issues) |
jobs:
scan-all:
runs-on: ubuntu-latest
strategy:
matrix:
dockerfile:
- Dockerfile
- docker/Dockerfile.dev
- docker/Dockerfile.prod
steps:
- uses: actions/checkout@v4
- name: Scan ${{ matrix.dockerfile }}
uses: cr0hn/dockerfile-security@v0.2.0
with:
dockerfile: ${{ matrix.dockerfile }}- name: Credential Scan Only
uses: cr0hn/dockerfile-security@v0.2.0
with:
dockerfile: 'Dockerfile'
categories: 'credentials'- name: Scan with Custom Rules
uses: cr0hn/dockerfile-security@v0.2.0
with:
dockerfile: 'Dockerfile'
custom-rules: '.github/dockerfile-rules.yaml'# Scan a Dockerfile (outputs ASCII table in terminal, JSON when piped)
dockerfile-sec Dockerfile
# Read from stdin
cat Dockerfile | dockerfile-sec
# Quiet mode with exit code for scripts
if dockerfile-sec -E -q Dockerfile; then
echo "No security issues found"
else
echo "Security issues detected!"
exit 1
fiThe Docker image is available at ghcr.io/cr0hn/dockerfile-security.
Basic usage:
# Scan a Dockerfile via stdin
cat Dockerfile | docker run --rm -i ghcr.io/cr0hn/dockerfile-security
# Scan a local file (mount current directory)
docker run --rm -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security /workspace/Dockerfile
# Scan with exit code for CI/CD
docker run --rm -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security -E /workspace/DockerfileAdvanced usage:
# Use a specific version
docker run --rm -i ghcr.io/cr0hn/dockerfile-security:v1.0.0
# With custom rules (mount rules file)
docker run --rm -i \
-v $(pwd)/Dockerfile:/Dockerfile \
-v $(pwd)/custom-rules.yaml:/rules.yaml \
ghcr.io/cr0hn/dockerfile-security -r /rules.yaml /Dockerfile
# Output JSON to a file
docker run --rm -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security \
-o /workspace/results.json /workspace/Dockerfile
# Ignore specific rules
docker run --rm -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security \
-i core-001 -i core-004 /workspace/Dockerfile
# Only credential rules
docker run --rm -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security \
-R credentials /workspace/DockerfileDocker Compose integration:
# docker-compose.yml
services:
dockerfile-sec:
image: ghcr.io/cr0hn/dockerfile-security:latest
volumes:
- ./Dockerfile:/Dockerfile:ro
command: ["-E", "/Dockerfile"]Shell alias for convenience:
# Add to ~/.bashrc or ~/.zshrc
alias dockerfile-sec='docker run --rm -i -v $(pwd):/workspace ghcr.io/cr0hn/dockerfile-security'
# Usage
dockerfile-sec /workspace/Dockerfile
dockerfile-sec -E /workspace/Dockerfiledockerfile-sec works seamlessly in UNIX pipelines:
# Chain with jq for JSON processing
cat Dockerfile | dockerfile-sec | jq '.[] | select(.severity == "High")'
# Process multiple Dockerfiles
find . -name "Dockerfile*" -exec dockerfile-sec -E {} \;name: Security Scan
on: [push, pull_request]
jobs:
dockerfile-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan Dockerfile
run: |
curl -sL https://github.com/cr0hn/dockerfile-security/releases/latest/download/dockerfile-sec-linux-amd64 -o dockerfile-sec
chmod +x dockerfile-sec
./dockerfile-sec -E Dockerfiledockerfile-security:
image: ghcr.io/cr0hn/dockerfile-security:latest
script:
- dockerfile-sec -E Dockerfile
rules:
- changes:
- Dockerfile
- "*.dockerfile"pipeline {
agent any
stages {
stage('Dockerfile Security') {
steps {
sh '''
curl -sL https://github.com/cr0hn/dockerfile-security/releases/latest/download/dockerfile-sec-linux-amd64 -o dockerfile-sec
chmod +x dockerfile-sec
./dockerfile-sec -E Dockerfile
'''
}
}
}
}Control which built-in rules are loaded:
# All rules (default)
dockerfile-sec Dockerfile
# Core rules only (best practices)
dockerfile-sec -R core Dockerfile
# Credential detection rules only
dockerfile-sec -R credentials Dockerfile
# Security rules only
dockerfile-sec -R security Dockerfile
# Package management rules only
dockerfile-sec -R packages Dockerfile
# Configuration rules only
dockerfile-sec -R configuration Dockerfile
# Combine multiple categories (comma-separated)
dockerfile-sec -R core,security Dockerfile
dockerfile-sec -R credentials,security,packages Dockerfile
# Disable built-in rules (use with -r for custom rules only)
dockerfile-sec -R none -r my-rules.yaml Dockerfile# ASCII table (default in terminal)
dockerfile-sec Dockerfile
# JSON output (automatic when piped, or explicit with -o)
dockerfile-sec Dockerfile | cat
dockerfile-sec -o results.json Dockerfile
# Quiet mode (no output, useful with -E for CI/CD)
dockerfile-sec -q -E DockerfileJSON Output Format:
[
{
"id": "core-002",
"description": "Posible text plain password in dockerfile",
"reference": "https://snyk.io/blog/10-docker-image-security-best-practices/",
"severity": "High"
}
]By rule ID (CLI):
# Ignore single rule
dockerfile-sec -i core-001 Dockerfile
# Ignore multiple rules
dockerfile-sec -i core-001 -i core-007 DockerfileBy ignore file:
Create a file with rule IDs to ignore (one per line):
# .dockerfile-sec-ignore
# Ignore USER requirement for this project
core-001
# We use ADD intentionally for tar extraction
core-004
dockerfile-sec -F .dockerfile-sec-ignore DockerfileLoad custom rules from files or URLs:
# From local file
dockerfile-sec -r my-rules.yaml Dockerfile
# From URL
dockerfile-sec -r https://example.com/rules.yaml Dockerfile
# Combine with built-in rules
dockerfile-sec -r my-rules.yaml Dockerfile
# Use only external rules
dockerfile-sec -R none -r my-rules.yaml Dockerfiledockerfile-sec includes 35 built-in rules across 5 categories:
Best practices and security guidelines for Dockerfiles.
| ID | Description | Severity |
|---|---|---|
core-001 |
Missing USER sentence (running as root) | High |
core-002 |
Possible plaintext password in Dockerfile | High |
core-003 |
Recursive copy found (COPY . .) |
Medium |
core-004 |
Use of ADD instead of COPY | Low |
core-005 |
Use image tag instead of SHA256 hash | Medium |
core-006 |
Use of latest tag in FROM |
Medium |
core-007 |
Use of deprecated MAINTAINER | Low |
core-008 |
Use of --insecurity=insecure in RUN |
High |
core-009 |
Secrets passed via ARG instead of ENV | High |
core-010 |
HEALTHCHECK contains sensitive information | High |
Detection of exposed secrets and credentials.
| ID | Description | Severity |
|---|---|---|
cred-001 |
Generic credential patterns | Medium |
cred-002 |
AWS Access Key ID | High |
cred-003 |
AWS MWS Key | High |
cred-004 |
EC Private Key | High |
cred-005 |
Google API Key | High |
cred-006 |
Slack Webhook URL | High |
cred-007 |
GitHub Personal Access Token | Critical |
cred-008 |
RSA Private Key | Critical |
cred-009 |
OpenAI API Key | High |
cred-010 |
Stripe API Key | Critical |
cred-011 |
Docker Registry Authentication Token | High |
Advanced security checks for container security.
| ID | Description | Severity |
|---|---|---|
sec-001 |
Docker socket mounted in container (container escape) | Critical |
sec-002 |
WORKDIR pointing to system directories or root | Medium |
sec-003 |
chmod 777 or dangerous permissions | High |
sec-004 |
Use of sudo in RUN commands | Medium |
sec-005 |
BuildKit secret mount without cleanup | High |
sec-006 |
Setting SUID/SGID bits on binaries | High |
sec-007 |
ENV directive with embedded credentials | High |
Package manager best practices and security.
| ID | Description | Severity |
|---|---|---|
pkg-001 |
apt-get without cleanup | Medium |
pkg-002 |
pip install without --no-cache-dir | Low |
pkg-003 |
npm install without cache cleanup | Low |
pkg-004 |
Piping curl/wget to bash | High |
Container runtime configuration issues.
| ID | Description | Severity |
|---|---|---|
cfg-001 |
Using --privileged flag | Critical |
cfg-002 |
Exposing dangerous ports (22, 23, 3389, etc.) | Medium |
cfg-003 |
Non-standard STOPSIGNAL defined | Low |
Rules are defined in YAML format. Each rule requires:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Unique identifier (e.g., custom-001) |
description |
string | Yes | Human-readable description |
regex |
string | Yes | Regular expression pattern to match |
reference |
string | Yes | URL with more information |
severity |
string | Yes | Low, Medium, or High |
custom-rules.yaml:
# Detect hardcoded IP addresses
- id: custom-001
description: Hardcoded IP address found
regex: '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
reference: https://example.com/security-guidelines
severity: Medium
# Detect curl without certificate verification
- id: custom-002
description: Use of curl without certificate verification
regex: '(curl.*-k|curl.*--insecure)'
reference: https://example.com/security-guidelines
severity: High
# Detect wget without certificate verification
- id: custom-003
description: Use of wget without certificate verification
regex: '(wget.*--no-check-certificate)'
reference: https://example.com/security-guidelines
severity: High
# Detect EXPOSE directive
- id: custom-004
description: EXPOSE directive detected - verify port is necessary
regex: '^(EXPOSE[\s]+[\d]+)'
reference: https://docs.docker.com/reference/dockerfile/#expose
severity: LowUsing custom rules:
# Add to built-in rules
dockerfile-sec -r custom-rules.yaml Dockerfile
# Replace built-in rules entirely
dockerfile-sec -R none -r custom-rules.yaml Dockerfile
# Multiple rule files
dockerfile-sec -r rules1.yaml -r rules2.yaml Dockerfile| Tip | Example |
|---|---|
| Match at line start | ^FROM |
| Case-insensitive | (?i)password |
| Match any character | . |
| Match literal dot | \. |
| Match word boundary | \b |
| Non-greedy match | .*? |
Test your regex at regex101.com (select Go flavor).
Usage: dockerfile-sec [OPTIONS] [DOCKERFILE]
Analyze a Dockerfile for security issues.
Arguments:
DOCKERFILE Path to Dockerfile (reads from stdin if not provided)
Options:
-E Exit with code 1 if issues are found (for CI/CD)
-F file Ignore file containing rule IDs to skip (repeatable)
-R selection Built-in rules: all, core, credentials, security, packages, configuration, none (comma-separated, default: all)
-i id Ignore specific rule ID (repeatable)
-o file Write JSON output to file
-q Quiet mode (suppress stdout output)
-r file External rules file or URL (repeatable)
-h, --help Show help message
-v, --version Show version information
Exit Codes:
0 Success (no issues found, or -E not specified)
1 Issues found (only when -E is specified)
2 Error (invalid arguments, file not found, etc.)
We welcome contributions! Please see our Contributing Guide for details on:
- Setting up the development environment
- Code style guidelines
- How to submit pull requests
- Adding new security rules
Before contributing, please read our Code of Conduct.
# Clone the repository
git clone https://github.com/cr0hn/dockerfile-security.git
cd dockerfile-security
# Run tests
make test
# Run linter
make lint
# Build
make buildFor information about reporting security vulnerabilities, please see our Security Policy.
Please do not report security vulnerabilities through public GitHub issues.
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
BSD 3-Clause License
Copyright (c) 2020-2025, Daniel Garcia (cr0hn)
All rights reserved.
- Snyk - Docker security best practices
- gitleaks - Credential detection patterns
- Docker Documentation - Dockerfile reference
- Snyk: 10 Docker Image Security Best Practices
- Dockerfile Security Tuneup
- Container Deployments: A Lesson in Deterministic Ops
- Spacelift: Docker Security Best Practices
- Docker Documentation: Dockerfile Reference
You might also be interested in dockerscan - A Docker image security analyzer that complements dockerfile-sec by scanning built Docker images for vulnerabilities, malware, and security issues.
Made with ❤️ by Daniel Garcia (cr0hn)
