Skip to main content

Common Misconfiguration

Running containers with privileged: true grants them nearly all capabilities of the host, effectively breaking container isolation. This allows containers to access host devices, mount filesystems, and potentially compromise the entire system.

Vulnerable Example

# Vulnerable docker-compose.yml
version: '3.8'
services:
  monitoring:
    image: monitoring-agent:latest
    privileged: true  # DANGEROUS - full host access
    network_mode: host  # Also problematic
    pid: host  # Access to host processes
    volumes:
      - /:/host  # Mounting entire host filesystem
      - /var/run/docker.sock:/var/run/docker.sock
    
  network-tools:
    image: network-scanner:latest
    privileged: true  # Unnecessary for most network operations
    cap_add:
      - ALL  # Adding all capabilities
    
  backup-service:
    image: backup:latest
    privileged: true
    volumes:
      - /:/backup-source  # Full system access
    user: root

Secure Example

# Secure docker-compose.yml with minimal capabilities
version: '3.8'
services:
  monitoring:
    image: monitoring-agent:latest
    # Use specific capabilities instead of privileged mode
    cap_add:
      - SYS_PTRACE  # Only for process monitoring
      - SYS_ADMIN   # Only if absolutely necessary
    cap_drop:
      - ALL  # Drop all other capabilities
    security_opt:
      - no-new-privileges:true
      - apparmor:docker-default
      - seccomp:default
    volumes:
      # Mount only necessary paths with read-only where possible
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - monitoring_data:/var/lib/monitoring
    read_only: true  # Make root filesystem read-only
    tmpfs:
      - /tmp
      - /var/run
    user: "1000:1000"  # Run as non-root

  network-tools:
    image: network-scanner:latest
    # Only add required network capabilities
    cap_add:
      - NET_RAW  # For packet capture
      - NET_ADMIN  # For network configuration
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    networks:
      - monitoring_net
    user: "1001:1001"

  backup-service:
    image: backup:latest
    cap_drop:
      - ALL
    cap_add:
      - DAC_READ_SEARCH  # For reading files with different ownership
    security_opt:
      - no-new-privileges:true
    volumes:
      # Mount only specific directories needed for backup
      - /data/app:/backup-source/app:ro
      - /data/database:/backup-source/database:ro
      - backup_storage:/backups
    user: "1002:1002"
    read_only: true
    tmpfs:
      - /tmp

volumes:
  monitoring_data:
    driver: local
  backup_storage:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /secure/backups

networks:
  monitoring_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/24

Capability Reference

# Common capabilities and their secure alternatives
version: '3.8'
services:
  # Example: Container needing to bind to privileged ports
  web-server:
    image: nginx:alpine
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  # Bind to ports < 1024
      - CHOWN  # Change file ownership
      - SETUID  # Switch user context
      - SETGID  # Switch group context
    ports:
      - "80:80"
      - "443:443"

  # Example: Container needing to modify network settings
  vpn-client:
    image: vpn-client:latest
    cap_drop:
      - ALL
    cap_add:
      - NET_ADMIN  # Network administration
      - NET_RAW    # Raw socket access
    devices:
      - /dev/net/tun  # Only specific device access
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv6.conf.all.disable_ipv6=0

  # Example: Container needing to monitor system
  system-monitor:
    image: prometheus-node-exporter:latest
    cap_drop:
      - ALL
    cap_add:
      - SYS_PTRACE  # Process tracing
    pid: host  # Only if absolutely necessary
    security_opt:
      - no-new-privileges:true
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)'

Security Policy Example

# AppArmor profile for additional security
# Save as: /etc/apparmor.d/docker-custom
profile docker-custom flags=(attach_disconnected,mediate_deleted) {
  # Include base abstraction
  include <abstractions/base>
  
  # Deny all file writes by default
  deny /** w,
  
  # Allow specific read access
  /etc/ld.so.cache r,
  /lib/** r,
  /usr/lib/** r,
  /proc/sys/kernel/random/uuid r,
  
  # Allow writing to specific directories
  /var/log/app/** w,
  /tmp/** rw,
  
  # Network access
  network inet tcp,
  network inet udp,
  
  # Deny capability usage
  deny capability,
}