Dockerfile Security Best Practices and Hadolint Usage Guide

Implementing security best practices in your Dockerfiles

Featured image



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

1. Use Official Base Images
  • 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-work

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.

2. DL4000: Deprecated MAINTAINER The MAINTAINER directive is deprecated. Use LABEL instead.

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.

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.

5. DL3025: Using COPY with more than 2 arguments without WORKDIR Using WORKDIR is clearer and safer when copying multiple files.


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:


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



References