Common Misconfiguration
Mounting sensitive host directories like/etc, /root, or system directories into containers can expose critical system files, credentials, and configurations to potential compromise.
Vulnerable Example
Copy
# Vulnerable docker-compose.yml
version: '3.8'
services:
app:
image: webapp:latest
volumes:
- /etc:/host/etc # System configuration files exposed
- /root:/host/root # Root user home directory
- ~/.ssh:/app/.ssh # SSH keys exposed
- ~/.aws:/app/.aws # AWS credentials exposed
- /var/run/docker.sock:/var/run/docker.sock # Docker socket
- /:/rootfs # Entire filesystem mounted
file-manager:
image: filebrowser:latest
volumes:
- /home:/srv # All user home directories exposed
- /etc/passwd:/etc/passwd # System users file
- /etc/shadow:/etc/shadow # Password hashes exposed
ports:
- "8080:80"
logger:
image: log-collector:latest
volumes:
- /var/log:/logs # All system logs
- /proc:/host/proc # Process information
- /sys:/host/sys # System information
user: root # Running as root makes it worse
Secure Example
Copy
# Secure docker-compose.yml
version: '3.8'
services:
app:
image: webapp:latest
volumes:
# Use named volumes instead of host mounts
- app_data:/app/data
- app_config:/app/config:ro
# If host mount is necessary, be specific and use read-only
- ./app/public:/app/public:ro
- type: tmpfs
target: /app/tmp
tmpfs:
size: 100m
mode: 1777
configs:
- source: app_settings
target: /app/settings.yml
mode: 0440
secrets:
- source: app_credentials
target: /app/.credentials
uid: '1000'
gid: '1000'
mode: 0400
user: "1000:1000"
read_only: true
file-manager:
image: filebrowser:latest
volumes:
# Mount only specific, non-sensitive directories
- ./shared/documents:/srv/documents:ro
- ./shared/public:/srv/public
- filebrowser_db:/database
environment:
- FB_DATABASE=/database/filebrowser.db
- FB_ROOT=/srv
- FB_NOAUTH=false
ports:
- "127.0.0.1:8080:80" # Bind only to localhost
user: "1001:1001"
security_opt:
- no-new-privileges:true
logger:
image: log-collector:latest
volumes:
# Mount only specific log directories with restrictions
- ./logs/app:/logs/app:ro
- type: bind
source: /var/log/myapp
target: /logs/system
read_only: true
bind:
propagation: slave
cap_drop:
- ALL
cap_add:
- DAC_READ_SEARCH # Only for reading files
user: "1002:1002"
security_opt:
- no-new-privileges:true
- seccomp:default
volumes:
app_data:
driver: local
driver_opts:
type: none
o: bind
device: ./volumes/app_data
app_config:
driver: local
filebrowser_db:
driver: local
configs:
app_settings:
file: ./configs/app_settings.yml
secrets:
app_credentials:
file: ./secrets/credentials.json
Volume Security Best Practices
Copy
# Example: Secure data processing pipeline
version: '3.8'
services:
data-processor:
image: processor:latest
volumes:
# Input directory - read-only
- type: bind
source: ./input
target: /data/input
read_only: true
bind:
propagation: rprivate
# Output directory - specific path with limited access
- type: bind
source: ./output
target: /data/output
bind:
propagation: rprivate
# Temporary processing - use tmpfs
- type: tmpfs
target: /tmp
tmpfs:
size: 500m
mode: 1770
# Named volume for persistent data
- processed_data:/data/processed
# Security context
user: "2000:2000"
group_add:
- "2001" # Additional group for shared access
security_opt:
- no-new-privileges:true
- label:type:container_file_t
cap_drop:
- ALL
read_only: true
# Example: Secure backup service
backup:
image: restic:latest
volumes:
# Mount only backup sources as read-only
- type: bind
source: /data/important
target: /source/important
read_only: true
# Backup destination with specific permissions
- type: volume
source: backup_storage
target: /backup
volume:
nocopy: true
# Use secrets for backup encryption
secrets:
- restic_password
- restic_repository
environment:
- RESTIC_PASSWORD_FILE=/run/secrets/restic_password
- RESTIC_REPOSITORY_FILE=/run/secrets/restic_repository
user: "2002:2002"
cap_drop:
- ALL
cap_add:
- DAC_READ_SEARCH
- CHOWN
volumes:
processed_data:
driver: local
labels:
com.example.description: "Processed data storage"
com.example.department: "Data Processing"
backup_storage:
driver: local
driver_opts:
type: nfs
o: addr=10.0.0.10,nolock,soft,rw
device: ":/exports/backups"
secrets:
restic_password:
external: true
restic_repository:
external: true
Directory Permission Script
Copy
#!/bin/bash
# setup-volumes.sh - Prepare volume directories with proper permissions
# Create volume directories
mkdir -p ./volumes/{app_data,app_config,processed_data}
mkdir -p ./shared/{documents,public}
mkdir -p ./logs/app
mkdir -p ./input ./output
# Set restrictive permissions
chmod 750 ./volumes/*
chmod 755 ./shared/public
chmod 750 ./shared/documents
chmod 750 ./logs/app
chmod 750 ./input
chmod 770 ./output
# Set ownership (adjust UIDs to match container users)
chown 1000:1000 ./volumes/app_data
chown 1000:1000 ./volumes/app_config
chown 1001:1001 ./shared/*
chown 1002:1002 ./logs/app
chown 2000:2000 ./input ./output
# Set SELinux context if applicable
if command -v chcon &> /dev/null; then
chcon -Rt svirt_sandbox_file_t ./volumes
chcon -Rt svirt_sandbox_file_t ./shared
chcon -Rt svirt_sandbox_file_t ./logs
fi

