Docker-Compose-Sicherheitsscanner
Tiefgreifende Sicherheitsanalyse von docker-compose für privilegierte Container und Geheimnisse
Docker Compose Security Scanning
Docker Compose security scanning is the practice of analyzing docker-compose.yml files
for misconfigurations that introduce privilege escalation paths, secret exposure, network isolation
failures, and resource exhaustion vulnerabilities into containerized environments. While Docker Compose
simplifies multi-container orchestration, its declarative syntax makes it dangerously easy to grant
containers root-level host access, expose credentials in plaintext environment variables, bypass
network segmentation, or deploy services without memory and CPU constraints — any of which can
transform a single compromised container into a full host takeover.
Unlike general-purpose Docker Compose inspectors that focus on service topology, dependency graphs,
and port mappings, a security scanner examines the attack surface of each service definition.
It identifies configurations where privileged: true grants unrestricted kernel access,
where cap_add: [SYS_ADMIN] enables container escapes, where host network mode eliminates
network namespace isolation, and where volume mounts like /var/run/docker.sock or
/ give containers control over the Docker daemon or the entire host filesystem. These
findings are classified by severity and accompanied by specific remediation guidance — turning a
passive configuration review into an actionable security audit.
Privileged Mode and Linux Capability Abuse
The most dangerous Docker Compose misconfiguration is running containers in privileged mode.
When privileged: true is set on a service, Docker disables all security mechanisms
— removing seccomp profiles, AppArmor confinement, and capability restrictions. The container
gains full access to all host devices (/dev), can load kernel modules, modify
iptables rules, and mount host filesystems without restriction. A single compromised process
inside a privileged container effectively becomes root on the host system.
-
privileged: true— Grants all Linux capabilities and removes all security profiles. The container has unrestricted access to the host kernel. This should never appear in production Compose files. In the rare cases where device access is genuinely needed (GPU workloads, USB passthrough), usedevicesdirectives with specific device paths instead. -
cap_add: [SYS_ADMIN]— The most abused individual capability. SYS_ADMIN permits mounting filesystems, usingptrace, modifying kernel parameters via/proc/sys, and performing namespace operations. Attackers use SYS_ADMIN to escape containers by mounting the host filesystem from within. Legitimate alternatives: use specific capabilities likeDAC_READ_SEARCHfor file access orNET_ADMINfor networking only when absolutely required. -
cap_add: [NET_ADMIN, NET_RAW]— NET_ADMIN allows modification of network interfaces, routing tables, and firewall rules inside the container's network namespace. NET_RAW enables raw socket creation for packet crafting. Together they allow ARP spoofing, traffic interception, and network-level attacks against other containers on the same Docker network. -
Missing
cap_drop: [ALL]— Docker containers inherit a default set of capabilities includingCHOWN,DAC_OVERRIDE,SETUID,SETGID, andNET_BIND_SERVICE. The security best practice is to drop all capabilities and add back only the specific ones required:cap_drop: [ALL]followed bycap_add: [NET_BIND_SERVICE]for services that only need to bind privileged ports.
Secret Exposure and Environment Variable Risks
Docker Compose files frequently become repositories of plaintext secrets. Environment variables
defined directly in docker-compose.yml are stored unencrypted in version control,
visible in docker inspect output, logged by orchestration systems, and accessible
to any process inside the container via /proc/1/environ. A security scanner
identifies hardcoded credentials and recommends secure alternatives.
-
Inline secrets in
environment:blocks: Patterns likeDB_PASSWORD=production_s3cr3t,AWS_SECRET_ACCESS_KEY=..., orAPI_KEY=sk_live_...directly in the Compose file. These persist in Git history even after removal and are visible to anyone with repository access. -
Secrets in
.envfiles without.gitignoreprotection: Whileenv_file: .envseparates secrets from the Compose file, the.envfile itself is often committed accidentally. A scanner flagsenv_filedirectives and recommends verifying exclusion from version control. -
Docker Secrets vs environment variables: For Docker Swarm mode and Compose v3+,
the
secretstop-level key provides encrypted secret storage. Secrets are mounted as files at/run/secrets/<name>with restricted permissions, never appearing indocker inspector container environment. The scanner recommends migrating fromenvironment:tosecrets:for any variable containing credentials. -
Build arguments containing secrets: Using
args:in thebuild:section to pass secrets like SSH keys or tokens embeds them in image layers. Even multi-stage builds retain build arguments in the build cache. The scanner flagsargs:values that match secret patterns.
Network Isolation and Exposure Vulnerabilities
Docker's network namespacing is a primary security boundary between containers and the host. Compose configurations that weaken or eliminate this isolation expose services to attacks from adjacent containers, the host network, or external traffic that should never reach internal services.
-
network_mode: host— Removes network namespace isolation entirely. The container shares the host's network stack, seeing all host interfaces, all listening ports, and all network traffic. Services binding to0.0.0.0inside the container are directly accessible on the host without port mapping. This eliminates Docker's network-level containment and should only be used when specific host networking features are required (typically performance-sensitive applications needing kernel bypass). -
Missing network segmentation: Compose files that define all services on the
default bridge network allow unrestricted inter-container communication. A compromised web
frontend can directly probe the database, cache, and queue services. The scanner recommends
defining explicit networks — a
frontendnetwork for public-facing services and abackendnetwork for data stores — with services attached only to the networks they genuinely need. -
Unnecessary port publishing: Publishing internal service ports with
ports: ["5432:5432"]for databases orports: ["6379:6379"]for Redis exposes these services to the host network (and potentially to external traffic if the host has a public IP). Internal services should communicate through Docker networks without port publishing. Only edge services (reverse proxies, load balancers) need published ports. -
Binding to all interfaces: Port mappings without explicit bind addresses
(
ports: ["8080:80"]) default to binding on0.0.0.0, accepting connections from any network interface. For development environments,ports: ["127.0.0.1:8080:80"]restricts access to localhost. The scanner flags published ports without bind address restrictions.
Resource Limits and Denial of Service Prevention
Containers without resource constraints can consume unlimited CPU, memory, and disk I/O from the host system. A single runaway container — whether due to a memory leak, infinite loop, or malicious workload — can starve other containers and host processes, causing cascading failures across the entire Docker environment.
-
Missing memory limits: Without
mem_limit(Compose v2) ordeploy.resources.limits.memory(Compose v3), a container can allocate memory until the host OOM killer intervenes — killing arbitrary processes including other containers or critical system services. Every production service should have explicit memory limits based on profiled resource usage plus a safety margin. -
Missing CPU limits: Without
cpusordeploy.resources.limits.cpus, a container can monopolize all CPU cores. This is particularly dangerous in multi-tenant environments where a crypto-mining attack in one container degrades all co-located services. Set CPU limits to prevent any single container from consuming more than its fair share. -
No restart policy or uncontrolled restarts: A
restart: alwayspolicy without resource limits means a crashing container continuously restarts, potentially creating fork-bomb-like resource consumption. Combine restart policies with resource limits and considerrestart: on-failurewith a maximum retry count to prevent infinite crash loops. -
PID limits: Without
pids_limit, a container can create unlimited processes via fork bombs, exhausting the kernel's PID space and affecting the entire host. Settingpids_limit: 100(or an appropriate value for the workload) prevents process-based DoS attacks.
Dangerous Volume Mounts and Filesystem Access
Volume mounts bridge the isolation boundary between container and host filesystems. While necessary for persistent storage and configuration injection, certain mount paths grant containers the ability to escape their confinement, compromise other containers, or modify critical host system files.
-
/var/run/docker.sockmounts: Mounting the Docker socket gives the container full control over the Docker daemon — it can create privileged containers, access any volume, read secrets from other containers, and effectively gain root access to the host. This is the single most dangerous volume mount in Docker security. If Docker API access is required (CI/CD runners, monitoring tools), use TCP-based access with TLS mutual authentication and restrict API endpoints via authorization plugins. -
Root filesystem mounts (
/:/host): Mounting the entire host filesystem into a container allows reading and writing any file —/etc/shadowfor credential theft,/etc/crontabfor persistence, or/root/.sshfor lateral movement. Even read-only root mounts (/:/host:ro) expose sensitive configuration and credentials. -
/etcand/procmounts: Mounting/etcenables modification of host configuration includingpasswd,sudoers, andresolv.conf. Mounting/procfrom the host exposes process information and kernel tunables. Both paths should be avoided unless a specific file within them is needed (and can be mounted individually). -
Missing
:roflag: Volumes mounted without the read-only flag grant write access by default. Configuration files, TLS certificates, and reference data that containers only need to read should always use:roto enforce the principle of least privilege:./config:/app/config:ro.
Code Examples
Insecure vs Secure Docker Compose Configuration
# ⚠️ INSECURE: Common security misconfigurations
# This configuration has multiple critical vulnerabilities
version: "3.8"
services:
web:
image: myapp:latest
privileged: true # CRITICAL: Full host access
cap_add:
- SYS_ADMIN # CRITICAL: Container escape risk
- NET_ADMIN # HIGH: Network manipulation
network_mode: host # HIGH: No network isolation
ports:
- "3000:3000" # Binds to all interfaces
environment:
- DB_PASSWORD=super_secret_123 # CRITICAL: Plaintext secret
- AWS_SECRET_ACCESS_KEY=wJalrXU # CRITICAL: Cloud credentials
- JWT_SECRET=my-signing-key # HIGH: Auth token forgery
volumes:
- /var/run/docker.sock:/var/run/docker.sock # CRITICAL: Docker escape
- /:/host # CRITICAL: Full host filesystem
- ./config:/app/config # Missing :ro flag
database:
image: postgres:15
ports:
- "5432:5432" # HIGH: DB exposed to network
environment:
- POSTGRES_PASSWORD=admin123 # CRITICAL: Weak plaintext password
# No resource limits # MEDIUM: DoS vulnerability
---
# ✅ SECURE: Hardened configuration with proper security controls
version: "3.8"
services:
web:
image: myapp:latest
cap_drop:
- ALL # Drop all capabilities first
cap_add:
- NET_BIND_SERVICE # Only add what's needed
read_only: true # Read-only root filesystem
security_opt:
- no-new-privileges:true # Prevent privilege escalation
networks:
- frontend # Explicit network segmentation
ports:
- "127.0.0.1:3000:3000" # Bind to localhost only
env_file:
- .env # Secrets in .gitignored file
secrets:
- db_password # Docker secrets for sensitive data
- jwt_signing_key
volumes:
- ./config:/app/config:ro # Read-only mount
tmpfs:
- /tmp # Writable temp in memory only
deploy:
resources:
limits:
memory: 512M # Memory ceiling
cpus: "0.5" # CPU limit
reservations:
memory: 256M # Guaranteed minimum
pids_limit: 100 # Prevent fork bombs
database:
image: postgres:15
networks:
- backend # Internal-only network
# No published ports — accessed via Docker network only
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
volumes:
- pgdata:/var/lib/postgresql/data # Named volume for persistence
deploy:
resources:
limits:
memory: 1G
cpus: "1.0"
pids_limit: 200
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external access
volumes:
pgdata:
secrets:
db_password:
file: ./secrets/db_password.txt # External secret file
jwt_signing_key:
file: ./secrets/jwt_key.txtOutput:
Security Scan Results:
─────────────────────
Score: 15/100 (INSECURE configuration)
CRITICAL (4 findings):
• Line 6: privileged: true — Full host kernel access, container escape trivial
• Line 15: DB_PASSWORD in plaintext — Secret exposed in version control
• Line 20: /var/run/docker.sock mounted — Container can control Docker daemon
• Line 21: Root filesystem (/) mounted — Full host read/write access
HIGH (4 findings):
• Line 8: cap_add: SYS_ADMIN — Enables namespace manipulation and escape
• Line 10: network_mode: host — All network isolation removed
• Line 28: Database port 5432 published — Direct network exposure
• Line 17: AWS_SECRET_ACCESS_KEY in plaintext — Cloud credential leak
MEDIUM (2 findings):
• Line 13: Port 3000 bound to 0.0.0.0 — Accessible from all interfaces
• Line 30: No resource limits on database — DoS vulnerability
Recommendations:
1. Remove privileged: true, use specific capabilities instead
2. Move all secrets to Docker secrets or external vault
3. Remove Docker socket mount, use TCP+TLS if API access needed
4. Add deploy.resources.limits to all services
5. Define explicit networks with internal flag for backendStandards & Specifications
- CIS Docker Benchmark — Industry-standard security configuration guidelines for Docker deployments covering host, daemon, container runtime, and image security
- Docker Security Documentation — Official Docker documentation on kernel namespaces, control groups, capabilities, seccomp profiles, and container security best practices
- OWASP Docker Security Cheat Sheet — OWASP guidelines for secure Docker configuration including network segmentation, secret management, and least-privilege containers
- Docker Compose Specification — Deploy — Official specification for resource limits, restart policies, and deployment constraints in Docker Compose files
Häufig Gestellte Fragen
How is the Docker Compose Security Scanner different from the Docker Compose Inspector?
The Docker Compose Inspector focuses on general best practices and misconfigurations (exposed ports, latest tags, unused networks). The Security Scanner goes deeper with security-specific analysis: it detects host namespace sharing (PID, IPC, network), writable root filesystems, containers running as root, hardcoded secrets in environment variables, dangerous Linux capabilities, missing health checks, insecure registries, and missing CPU limits. Use the Inspector for quick checks, and the Security Scanner for thorough security audits.
What makes a container 'privileged' and why is it critical?
A container with privileged: true or cap_add: ALL has unrestricted access to the host kernel, all devices, and all Linux capabilities. An attacker exploiting a vulnerability in a privileged container can escape to the host, access other containers, modify kernel parameters, and potentially compromise the entire infrastructure. The scanner flags this as critical because it completely eliminates container isolation.
Why does the scanner flag containers without a user specification?
By default, Docker containers run as root (UID 0). If an attacker gains code execution inside the container, they have root privileges — which combined with any misconfiguration (like a writable host volume) can lead to host compromise. Setting user: '1000:1000' or a named non-root user limits the blast radius of a container breach.
What are dangerous Linux capabilities?
Linux capabilities are fine-grained privileges that replace the traditional root/non-root binary. Dangerous capabilities include SYS_ADMIN (near-root access, mount operations), NET_ADMIN (network configuration, firewall changes), SYS_PTRACE (debug other processes, read memory), SYS_RAWIO (raw I/O access to hardware), and NET_RAW (create raw sockets for network sniffing). The scanner flags these individually because each introduces specific attack vectors.
Why is sharing host PID/IPC/network namespace dangerous?
Host namespace sharing breaks container isolation at the kernel level. pid: host lets the container see and signal all host processes. ipc: host allows access to host shared memory. network_mode: host bypasses Docker network isolation entirely — the container shares the host's network stack, ports, and can sniff all traffic. Each is flagged as critical or high severity because they enable direct host-level attacks.
How does the scanner detect secrets in environment variables?
The scanner checks environment variable names and values against known secret patterns: passwords, API keys, tokens, private keys, database URLs, and session secrets. It specifically flags hardcoded values (not variable references like ${SECRET}) because these end up in version control, docker inspect output, and process listings. The recommendation is to use Docker secrets, env_file, or external secret managers.
What is a writable root filesystem and why does it matter?
By default, a container's root filesystem is writable. If an attacker gains code execution, they can modify application binaries, install tools, or alter configuration files. Setting read_only: true makes the filesystem immutable — the attacker cannot persist changes. Use tmpfs mounts for paths that need writes (like /tmp or /var/run).
Is my docker-compose.yml sent to any server?
No. All security scanning happens entirely in your browser using JavaScript. Your Docker Compose configuration — which may contain service names, internal hostnames, registry URLs, and infrastructure topology — never leaves your device. No data is stored, logged, or transmitted.