Welcome to from-docker-to-kubernetes

Security

Learn about Docker security best practices, configurations, and tools

Docker Security

Security is crucial when running containers in production. Container security requires a comprehensive approach that addresses vulnerabilities at all layers - from the host system and container runtime to application code and network communications. This guide covers essential security practices and configurations for Docker environments to help you build and maintain secure containerized applications.

Security Fundamentals

Isolation

  • Process isolation: Containers share the host kernel but run in isolated process spaces
  • Network namespace separation: Each container has its own network stack
  • Resource limitations: Restricting CPU, memory, and other resources to prevent DoS
  • Filesystem isolation: Container-specific filesystem with controlled access to host
  • User namespace mapping: Mapping container users to non-privileged host users
  • Control groups (cgroups): Kernel feature that limits and isolates resource usage
  • Namespaces: Provide isolated workspace for each container (PID, NET, MNT, UTS, IPC)

Access Control

  • User permissions: Principle of least privilege for container processes
  • Group management: Proper use of Linux groups for access control
  • Role-based access control (RBAC): Define who can perform actions on Docker resources
  • API access restrictions: TLS authentication and authorization for Docker daemon
  • Registry authentication: Secure access to image registries with authentication
  • Secret management: Secure handling of credentials and sensitive information
  • Container user restrictions: Running containers as non-root users

Resource Control

  • CPU limits: Prevent containers from consuming excessive CPU
  • Memory constraints: Limit memory usage to prevent OOM conditions
  • Storage quotas: Control disk usage to prevent filesystem filling
  • Network rate limiting: Prevent bandwidth abuse
  • Process restrictions: Limit process creation and capabilities
  • Syscall filtering: Restrict available system calls with seccomp
  • Capabilities: Fine-grained control over privileged operations

Docker Security Best Practices

Image Security

Image security is the foundation of container security - a vulnerable base image can compromise your entire application.

# Scan image for vulnerabilities using Snyk integration
docker scan my-image:latest

# Scan with Trivy (popular open-source scanner)
trivy image my-image:latest

# Use specific image versions instead of 'latest' tag
docker pull nginx:1.21.6-alpine

# Use minimal base images to reduce attack surface
docker pull alpine:3.15.0

# Sign and verify images with Docker Content Trust
export DOCKER_CONTENT_TRUST=1
docker trust sign my-image:latest
docker trust inspect my-image:latest

# Verify image integrity with digest
docker pull nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767

Best practices for secure images:

  1. Use official or verified images when possible
  2. Keep base images updated with security patches
  3. Build from minimal images like Alpine or distroless
  4. Pin specific versions with digests for immutability
  5. Implement vulnerability scanning in CI/CD pipelines
  6. Remove unnecessary tools from production images
  7. Set up image signing for supply chain security
  8. Use multi-stage builds to minimize final image size

Container Security

Security Configurations

Secure Container Runtime

The following Docker Compose configuration demonstrates comprehensive security hardening:

version: '3.8'
services:
  secure-app:
    image: my-app:latest
    # Prevent privilege escalation
    security_opt:
      - no-new-privileges:true
      # Apply custom seccomp profile to restrict syscalls
      - seccomp:security-profile.json
      # Enable AppArmor profile (Linux)
      - apparmor=docker-default
    # Make root filesystem read-only
    read_only: true
    # Provide writable temporary storage
    tmpfs:
      - /tmp:size=64M,mode=1777
      - /var/run:size=32M
      - /var/cache:size=32M
    # Run as non-root user
    user: non-root-user
    # Drop all capabilities by default
    cap_drop:
      - ALL
    # Add only necessary capabilities
    cap_add:
      - NET_BIND_SERVICE  # Allow binding to ports below 1024
    # Set resource limits
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 256M
        reservations:
          memory: 128M
    # Health check for monitoring
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    # Configure logging
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    # Set environment variables
    environment:
      - NODE_ENV=production
    # Network access control
    networks:
      - internal
    # Proper restart policy
    restart: unless-stopped

networks:
  internal:
    # Internal network with no direct external access
    internal: true

Each of these security configurations serves a specific purpose:

  • no-new-privileges prevents privilege escalation attacks
  • Seccomp profiles restrict available system calls
  • Read-only filesystem prevents malicious modifications
  • Running as non-root minimizes potential damage from breaches
  • Dropping capabilities implements least-privilege principle
  • Resource limits prevent DoS attacks
  • Health checks enable automatic detection of compromised containers

Network Security

Network Isolation

  • Use custom networks to isolate container communication
    # Create an isolated network
    docker network create --driver bridge isolated_network
    
    # Attach container to isolated network
    docker run --network isolated_network my-app
    
  • Restrict external access with internal networks
    # In docker-compose.yml
    networks:
      backend:
        internal: true  # No outbound connectivity
    
  • Enable TLS for encrypted communications
    # Generate TLS certificates
    openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
    
  • Implement firewalls at host and container level
    # Configure host firewall (ufw example)
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw deny 8080/tcp
    
  • Monitor traffic with network monitoring tools
    # Capture container traffic
    docker run --net=container:target_container nicolaka/netshoot tcpdump -i eth0
    

Access Control

  • Port binding restrictions - only expose necessary ports
    # Bind only to specific interface
    docker run -p 127.0.0.1:8080:80 nginx
    
  • Service-specific networks - separate frontend/backend
    services:
      frontend:
        networks: [frontend]
      backend:
        networks: [backend]
      database:
        networks: [backend]
    
  • Network segmentation with multiple networks
    docker network create frontend
    docker network create backend
    docker network create database
    
  • Traffic encryption with TLS and VPNs
    # Docker Swarm overlay network with encryption
    networks:
      secure_overlay:
        driver: overlay
        driver_opts:
          encrypted: "true"
    
  • Load balancer security - TLS termination and WAF
    # Example with Traefik (secure headers)
    labels:
      - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
      - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
    

Docker Daemon Security

Securing the Docker daemon is critical since it runs with root privileges and manages all containers.

# Generate CA and certificates for Docker daemon TLS
mkdir -p ~/.docker/tls
cd ~/.docker/tls

# Create a certificate authority
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem

# Create server certificate
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=docker-host" -new -key server-key.pem -out server.csr
openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem

# Create client certificate
openssl genrsa -out key.pem 4096
openssl req -subj "/CN=client" -new -key key.pem -out client.csr
openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem

# Configure TLS for Docker daemon
sudo dockerd \
  --tlsverify \
  --tlscacert=ca.pem \
  --tlscert=server-cert.pem \
  --tlskey=server-key.pem \
  -H=0.0.0.0:2376 \
  -H unix:///var/run/docker.sock

# Update Docker daemon configuration in daemon.json
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "tls": true,
  "tlscacert": "/path/to/ca.pem",
  "tlscert": "/path/to/server-cert.pem",
  "tlskey": "/path/to/server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"],
  "log-level": "info",
  "icc": false,
  "no-new-privileges": true,
  "userland-proxy": false,
  "live-restore": true,
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 64000,
      "Soft": 64000
    }
  }
}
EOF

# Restart Docker daemon
sudo systemctl restart docker

# Restrict Docker socket access
sudo chmod 660 /var/run/docker.sock
sudo chown root:docker /var/run/docker.sock

# Use Docker client with TLS
docker --tlsverify \
  --tlscacert=ca.pem \
  --tlscert=cert.pem \
  --tlskey=key.pem \
  -H=docker-host:2376 version

Key Docker daemon security configurations:

  • "icc": false - Disable inter-container communication by default
  • "no-new-privileges": true - Prevent privilege escalation
  • "userland-proxy": false - Use iptables directly instead of userland proxy
  • "live-restore": true - Containers remain running if daemon is unavailable
  • "default-ulimits" - Set sensible default ulimits for all containers

Content Trust and Image Signing

Security Tools and Scanning

Common Security Tools

Security tools help identify vulnerabilities, misconfigurations, and potential threats in your container ecosystem.

# Trivy - comprehensive vulnerability scanner
trivy image my-image:latest

# Detailed scan with Trivy
trivy image --severity HIGH,CRITICAL --format json --output results.json my-image:latest

# Clair scanner - static vulnerability analyzer
clair-scanner --ip $(hostname -i) my-image:latest

# Docker Bench Security - checks for dozens of common best-practices
docker run -it --net host --pid host --userns host --cap-add audit_control \
    -v /var/lib:/var/lib \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /usr/lib/systemd:/usr/lib/systemd \
    -v /etc:/etc --label docker_bench_security \
    docker/docker-bench-security

# Anchore Engine - deep image inspection and policy evaluation
docker run -d --name anchore-engine anchore/anchore-engine
docker exec anchore-engine anchore-cli image add my-image:latest
docker exec anchore-engine anchore-cli image wait my-image:latest
docker exec anchore-engine anchore-cli image vuln my-image:latest os

# Falco - runtime security monitoring
docker run -d --name falco --privileged \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /dev:/host/dev \
    -v /proc:/host/proc:ro \
    -v /boot:/host/boot:ro \
    -v /lib/modules:/host/lib/modules:ro \
    -v /usr:/host/usr:ro \
    falcosecurity/falco:latest

# Syft - SBOM (Software Bill of Materials) generator
syft my-image:latest

# Grype - vulnerability scanner based on SBOM
grype my-image:latest

Integrating Security Tools in CI/CD

# Example GitHub Actions workflow with security scanning
name: Docker Security Scan

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build image
        run: docker build -t test-image:${{ github.sha }} .
      
      - name: Scan with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'test-image:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
      
      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

Secrets Management

Docker Secrets

  • Encrypted at rest - Secrets are stored encrypted in the Swarm Raft log
  • Secure transmission - Transmitted over TLS-encrypted networks
  • Version controlled - Track changes through secret versioning
  • Access restrictions - Only accessible to authorized services
  • Runtime injection - Available as files in the container at runtime
  • Centralized management - Consistent across the Docker Swarm cluster
  • Non-persistent - Not stored in container image or filesystem

Implementation

# Create a secret from a file
docker secret create site_key ./site_key.pem

# Create a secret from standard input
echo "my-secure-password" | docker secret create db_password -

# List available secrets
docker secret ls

# Inspect a secret (metadata only)
docker secret inspect db_password

# Use secret in a service
docker service create \
  --name secure-app \
  --secret db_password \
  --secret source=site_key,target=/etc/ssl/private/site.key,mode=0400 \
  my-app:latest

# Inside the container, secrets are available at:
# /run/secrets/<secret_name>
# /run/secrets/site.key

# Remove a secret
docker secret rm db_password

Docker Compose with Secrets

version: '3.8'
services:
  db:
    image: postgres:13
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
  
  web:
    image: nginx:alpine
    secrets:
      - source: site_certificate
        target: /etc/nginx/ssl/site.crt
        mode: 0444
      - source: site_key
        target: /etc/nginx/ssl/site.key
        mode: 0400

secrets:
  db_password:
    file: ./secrets/db_password.txt
  site_certificate:
    file: ./secrets/site.crt
  site_key:
    file: ./secrets/site.key

External Secrets Management

# Using external secrets from vault
version: '3.8'
services:
  app:
    image: myapp:latest
    environment:
      - DB_PASSWORD=${DB_PASSWORD}  # Injected via environment

# With docker-compose exec -e DB_PASSWORD=$(vault read -field=password secret/db)

Compliance and Auditing

Security Monitoring

Monitoring Tools and Practices

Comprehensive security monitoring enables detection of suspicious activities and potential security incidents.

# Configure Docker daemon logging
dockerd \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  --log-level info

# Configure daemon.json for logging
cat <<EOF > /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5",
    "labels": "production_status",
    "env": "os,customer"
  },
  "debug": true,
  "experimental": false
}
EOF

# Monitor real-time container events
docker events --filter 'type=container' --format '{{.ID}} {{.Type}} {{.Action}}'

# Check container resource usage with detailed stats
docker stats --all --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

# Monitor container lifecycle events
docker events --filter 'type=container' --filter 'event=start' --filter 'event=die'

# Configure container logging
docker run --log-driver=syslog --log-opt syslog-address=udp://syslog-server:514 nginx

# Use cAdvisor for container monitoring
docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --restart=always \
  google/cadvisor:latest

# Use Prometheus for metrics collection
docker run -d \
  --name=prometheus \
  --publish=9090:9090 \
  --volume=/path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:latest

# Use Falco for runtime security monitoring
docker run -d \
  --name falco \
  --privileged \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /dev:/host/dev \
  -v /proc:/host/proc:ro \
  -v /boot:/host/boot:ro \
  -v /lib/modules:/host/lib/modules:ro \
  -v /usr:/host/usr:ro \
  falcosecurity/falco:latest

Example Prometheus Configuration for Container Monitoring

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
  
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

Example Falco Rules for Security Monitoring

# falco_rules.yaml
- rule: Container Shell Activity
  desc: A shell was spawned inside a container
  condition: container.id != "" and proc.name = bash
  output: "Shell spawned in container (user=%user.name container=%container.name container_id=%container.id)"
  priority: WARNING

- rule: Unauthorized Files Access
  desc: Detect attempts to access sensitive files
  condition: >
    container and
    (fd.name startswith "/etc/shadow" or
     fd.name startswith "/etc/passwd")
  output: "Sensitive file accessed (user=%user.name file=%fd.name container=%container.name)"
  priority: ALERT

Incident Response

Detection

  • Monitor logs - Collect and analyze logs from all components
    # Centralize Docker logs
    docker run \
      --name fluentd \
      -p 24224:24224 \
      -v /path/to/fluent.conf:/fluentd/etc/fluent.conf \
      fluent/fluentd:latest
    
  • Track resource usage - Detect abnormal resource patterns
    # Set up alerting for unusual resource usage
    docker run -d \
      -p 9090:9090 \
      -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
      -v /path/to/alerts.yml:/etc/prometheus/alerts.yml \
      prom/prometheus
    
  • Alert on anomalies - Configure notification systems
    #
    

Advanced Security Topics

AppArmor Profiles

version: '3.8'
services:
  app:
    security_opt:
      - apparmor:custom-profile

SELinux Policies

# Run container with SELinux context
docker run --security-opt label=level:s0:c100,c200 my-app

Seccomp Profiles

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["accept", "bind", "listen"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Security Checklist