Best Practices for Container Security
Container security is not optional—every layer, from the image to runtime, needs to be hardened.
1. What You'll Learn
- CIS Docker Bench Security Baseline
- Image Signing and Verification (Docker Content Trust)
- Runtime security (seccomp/AppArmor)
- Secret Security Management Policy
- Principle of Least Privilege
2. A Story About a Security Audit
(1) Pain Point: 28 security audit alerts
Charlie's team received a security audit report: 28 alerts included "Containers running as root" (high risk), "Image contains 15 high-risk CVEs" (high risk), and "Secrets stored in environment variables" (medium risk). The audit score was only 45; they must raise it to 80 or higher to achieve compliance.
(2) A Systematic Approach to Security Hardening
Charlie led his team in addressing the issues one by one: non-root users, Trivy scans to fix CVEs, switching to Docker Secrets, and using seccomp to restrict system calls.
# Security hardening: non-root + minimal capabilities
docker run -d \
--user 1000:1000 \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges \
--read-only \
myapp:1.0
(3) Results: Audit Score 45→92
Following systematic hardening, the audit score improved from 45 to 92, and all high-risk issues were resolved.
3. Container Security Layered Model
(1) Layered Security
graph TB
L6["6. Secret Management<br/>Docker Secret / Vault"] --> L5["5. Cybersecurity<br/>Network isolation / TLS"]
L5 --> L4["4. Runtime Security<br/>seccomp / AppArmor / capabilities"]
L4 --> L3["3. Mirror Security<br/>Scan / Signature / Minimize"]
L3 --> L2["2. Docker Daemon Security<br/>TLS / User Permissions"]
L2 --> L1["1. Host Security<br/>OS Reinforcement / Kernel Update"]
(1) Security Level Comparison
| Level | Reinforcement Measures | Effect |
|---|---|---|
| 0 | Default configuration | ❌ Numerous alerts |
| 1 | Non-root + latest tag | ⚠️ Basic security |
| 2 | + Mirror Scan + Fixed Version | ✅ Moderate Security |
| 3 | + Minimum Capabilities + seccomp | ✅ Good Security |
| 4 | + DCT + Secret Management | ✅ High Security |
| 5 | + Network Isolation + Audit Logs | ✅ Maximum Security |
4. Mirror Vulnerability Scanning
▶ Example: Trivy Image Scan (Difficulty: ⭐⭐)
# Install Trivy (Linux)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Scan an image for vulnerabilities
trivy image nginx:1.25-alpine
# Scan with severity filter
trivy image --severity HIGH,CRITICAL nginx:1.25-alpine
# Scan and output JSON
trivy image --format json --output report.json myapp:1.0
nginx:1.25-alpine (alpine 3.19.0)
=========================
Total: 5 (HIGH: 2, CRITICAL: 0)
┌──────────┬────────────┬──────────┬──────────┬─────────────────┐
│ Library │ Vulnerability│ Severity │ Version │ Fixed Version │
├──────────┼────────────┼──────────┼──────────┼─────────────────┤
│ libcurl │ CVE-2023-xxxx│ HIGH │ 8.4.0 │ 8.5.0 │
│ openssl │ CVE-2023-yyyy│ HIGH │ 3.1.3 │ 3.1.4 │
└──────────┴────────────┴──────────┴──────────┴─────────────────┘
▶ Example: Docker Scout scan (Difficulty: ⭐⭐)
# Docker Scout (Docker Desktop built-in)
docker scout quickview nginx:latest
# Detailed CVE report
docker scout cves nginx:latest
# Compare two images
docker scout compare --from nginx:1.24 --to nginx:1.25
5. The Principle of Least Privilege
(1) Linux Capabilities
| Capability | Function | Common Requirements |
|---|---|---|
NET_BIND_SERVICE |
Bound to port <1024 | Web services (80/443) |
NET_RAW |
Raw Network Packets | ping / Network Debugging |
CHOWN |
Change File Owner | File Management |
SETUID / SETGID |
Switch User | Some System Tools |
ALL |
All Abilities | ❌ Do Not Use |
▶ Example: --cap-drop Least Privilege (Difficulty ⭐⭐⭐)
# Drop ALL capabilities, add only what's needed
docker run -d \
--name secure-app \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges \
-p 80:8080 \
myapp:1.0
| Parameter | Function |
|---|---|
--cap-drop ALL |
Remove all Linux capabilities |
--cap-add NET_BIND_SERVICE |
Add the ability to bind only to low ports |
--security-opt no-new-privileges |
Privilege escalation prohibited (setuid/setgid) |
--read-only |
Read-only file system (requires tmpfs) |
--privileged grants containers nearly all host privileges—never use this in a production environment. Using --cap-drop ALL + --cap-add on an as-needed basis embodies the principle of least privilege.**
6. Runtime Security
▶ Example: seccomp Restricts System Calls (Difficulty: ⭐⭐⭐)
# Run with a custom seccomp profile
docker run -d \
--security-opt seccomp=chrome.json \
--name browser \
chromium:latest
(1) Docker Default seccomp
Docker comes with a default seccomp configuration file that disables approximately 44 dangerous system calls (such as mount, keyctl, and add_key). Most applications run normally with the default configuration.
▶ Example: Docker Content Trust Signature (Difficulty: ⭐⭐)
# Enable DCT for the current session
export DOCKER_CONTENT_TRUST=1
# Push a signed image
docker push myorg/myapp:v1.0
# Pull: DCT verifies the signature
docker pull myorg/myapp:v1.0
# Pull unsigned image: DENIED (with DCT enabled)
docker pull myorg/myapp:unsigned
# Error: No trust data for unsigned
7. Secret Security Management
(1) Comparison of Three Secret Management Methods
| Method | Security Level | Visibility | Applicable Scenarios |
|---|---|---|---|
| Environment Variables (-e) | ❌ Low | docker inspect Visible |
Development Environment |
| Volume Files (-v) | ⚠️ In Progress | Visible to Host Files | Transition Plan |
| Docker Secret | ✅ High | Encrypted storage, runtime mounting | Swarm production environment |
| External Vault | ✅ Highest | Centralized Management + Auditing | Enterprise-grade |
8. CIS Docker Benchmark
▶ Example: Docker Bench Security Baseline Check (Difficulty: ⭐⭐)
# Run CIS Docker Benchmark check
docker run --rm --net host --pid host \
--userns host --cap-drop audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /lib/systemd/system:/lib/systemd/system:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
docker/docker-bench-security
[INFO] 1 - Host Configuration
[INFO] 1.1 - Ensure Docker is up to date
[PASS] 1.2 - Ensure only trusted users are allowed to control Docker daemon
[WARN] 1.4 - Ensure a separate partition for containers has been created
[INFO] 4 - Container Images
[PASS] 4.1 - Ensure a user for the container has been created
[WARN] 4.7 - Ensure HEALTHCHECK instructions have been added
[WARN] 4.9 - Ensure COPY is used instead of ADD
9. Complete Example: Full-Stack Hardening of Production Containers
# ============================================
# Complete walkthrough: Production container hardening
# From audit score 45 → 92
# ============================================
# Step 1: Scan for vulnerabilities
trivy image --severity HIGH,CRITICAL myapp:1.0
# Fix: update base image, pin versions
# Step 2: Run with maximum security constraints
docker run -d \
--name hardened-app \
--user 1000:1000 \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges \
--security-opt seccomp=default.json \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid \
--tmpfs /run:rw,noexec,nosuid \
-v app-logs:/app/logs \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--health-cmd="curl -f http://localhost:8080/health || exit 1" \
--health-interval=30s \
--health-retries=3 \
-p 8080:8080 \
myapp:hardened
# Step 3: Verify security configuration
docker inspect hardened-app --format='
User: {{.Config.User}}
ReadOnly: {{.HostConfig.ReadonlyRootfs}}
Capabilities: {{.HostConfig.CapAdd}}
NoNewPrivileges: {{.HostConfig.SecurityOpt}}
'
# Step 4: Run CIS benchmark
docker run --rm ... docker/docker-bench-security
# Step 5: Re-scan after hardening
trivy image myapp:hardened
❓ FAQ
docker inspect Environment variables are visible to anyone; ② Child processes inherit environment variables; ③ Environment variables may be accidentally printed in logs. Docker Secrets (Swarm) are stored in encrypted form and mounted as temporary files at runtime, which is more secure. In non-Swarm environments, use HashiCorp Vault.--privileged option, restricted network configuration). Rootless mode is recommended for production environments.📖 Summary
- Six-layer container security model: Host → Daemon → Image → Runtime → Network → Secret
--cap-drop ALL+ On-Demand--cap-addto Implement the Principle of Least Privilege- Trivy/Docker Scout scans images for vulnerabilities and automatically blocks high-risk CVEs in CI
- Docker Content Trust signature verification to prevent image tampering
- Secrets: Do not set environment variables; instead, use Docker Secrets or Vault for encrypted management
- The CIS Docker Benchmark is the standard tool for security baseline checks
📝 Exercises
- Basic Question (Difficulty: ⭐): Run a Docker Bench Security scan on your local machine and record any WARN-level alerts.
- Advanced Challenge (Difficulty ⭐⭐): Use Trivy to scan an existing image and fix all CRITICAL-level CVEs.
- Challenge (Difficulty: ⭐⭐⭐): Create a container with seccomp restrictions and verify that the restrictions are in effect (e.g., disabling the
mountsystem call).



